Managing Payments

After creating a payment, you can retrieve its details or list payments with filters. This guide covers both endpoints.

Get a single payment

Retrieve full details for a payment by its ID.

curl "https://{api-host}/v1/payments/PAYMENT_ID" \
  -H "Authorization: Bearer $YOUR_API_KEY"

Response

All amount fields (paymentAmount, discountFee, flatFee, returnFee) are in minor units (cents for USD). For example, 5000 means $50.00.

externalPaymentId is a free-form label you supply on payment creation. It is not unique -- multiple payments may share the same value (for example, an original attempt and a retry after fixing party data). For retry-safety on payment creation, use the Idempotency-Key header instead. See Idempotency and Retries.

direction is read from the originator's point of view, and the example above is OUTBOUND. On an INBOUND payment every field is the same and only the roles swap: contact.bankAccount is the account that was debited and the originator's is the account that was credited. paymentAmount stays a positive integer in minor units in both directions, so nothing about the response tells you the direction except direction itself.

The originator and contact objects describe the parties on the payment. For INDIVIDUAL parties, firstName and lastName are populated and companyName is null. For BUSINESS parties, companyName is populated; firstName and lastName may be null if the party was created without a person name. The same shape applies to party objects in the list response.

achAccountNumber, achRoutingNumber and swiftCode are masked in every response: leading asterisks with the last four characters visible (e.g. *****6789; account numbers of four or fewer characters show only the last two). bankName is not masked. There is no parameter or flag that returns a full account number, so keep your own record of the details you submit.

While a payment is in AWAITING_CONFIRMATION, the counterparty supplies their bank details through a payment link, so contact.bankAccount is null until they submit. If the counterparty is not an existing contact, the whole contact object is null until then. From QUEUED onward, contact and contact.bankAccount are always populated. A CANCELED payment was always canceled before submission (cancellation is only allowed from AWAITING_CONFIRMATION), so it keeps the same nulls as AWAITING_CONFIRMATION. The originator object and its bankAccount are populated in every status.

{
  "data": {
    "paymentId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "status": "COMPLETED",
    "createdAt": "2026-02-26T12:00:00.000Z",
    "completedAt": "2026-02-27T09:15:00.000Z",
    "failedAt": null,
    "returnedAt": null,
    "canceledAt": null,
    "paymentAmount": 5000,
    "discountFee": 100,
    "flatFee": 50,
    "returnFee": 0,
    "currencyCode": "USD",
    "paymentRail": "ACH",
    "direction": "OUTBOUND",
    "paymentNote": "Invoice 2026-001234",
    "externalPaymentId": "inv-2026-001234",
    "failureCode": null,
    "failureMessage": null,
    "failureReason": null,
    "returnReasonCode": null,
    "originator": {
      "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "firstName": null,
      "lastName": null,
      "companyName": "Acme Corp",
      "entityType": "BUSINESS",
      "bankAccount": {
        "bankName": null,
        "swiftCode": null,
        "subType": "CHECKING",
        "achRoutingNumber": "*****0021",
        "achAccountNumber": "*****6789"
      }
    },
    "contact": {
      "id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
      "firstName": "Jane",
      "lastName": "Smith",
      "companyName": null,
      "entityType": "INDIVIDUAL",
      "bankAccount": {
        "bankName": null,
        "swiftCode": null,
        "subType": "CHECKING",
        "achRoutingNumber": "*****0021",
        "achAccountNumber": "*****6789"
      }
    }
  },
  "meta": { "timestamp": "2026-02-27T10:00:00.000Z" }
}

Error responses

Code Reason
400 Invalid payment ID format
401 Missing or invalid API key
404 Payment not found or does not belong to your organization

List payments

Retrieve a paginated list of payments, optionally filtered by status, direction, originator, or date range.

The list response is a summary. Each item carries the same payment fields as the single-payment response except failureReason, returnReasonCode and the parties' bankAccount objects, which are only on GET /v1/payments/{id}.

curl "https://{api-host}/v1/payments/?status=COMPLETED&direction=OUTBOUND&limit=10" \
  -H "Authorization: Bearer $YOUR_API_KEY"

Query parameters

Parameter Type Default Description
cursor string -- Opaque pagination token. Omit on the first request; pass the nextCursor from the previous response to fetch the next page.
limit integer 20 Results per page (1-100)
status string -- Filter by status: AWAITING_CONFIRMATION, QUEUED, PROCESSING, COMPLETED, FAILED, FAILED_IN_FLIGHT, BLOCKED, RETURNED, CANCELED
originatorId UUID -- Filter by originator
direction string -- Filter by direction: INBOUND, OUTBOUND
createdFrom ISO 8601 -- Payments created on or after this timestamp
createdTo ISO 8601 -- Payments created on or before this timestamp

Results are ordered by createdAt descending (newest first). Filters are stable across pages; keep them identical when fetching subsequent pages with a cursor.

Response

{
  "data": [
    {
      "paymentId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
      "status": "COMPLETED",
      "createdAt": "2026-02-26T12:00:00.000Z",
      "completedAt": "2026-02-27T09:15:00.000Z",
      "failedAt": null,
      "returnedAt": null,
      "canceledAt": null,
      "paymentAmount": 5000,
      "discountFee": 100,
      "flatFee": 50,
      "returnFee": 0,
      "currencyCode": "USD",
      "paymentRail": "ACH",
      "direction": "OUTBOUND",
      "paymentNote": "Invoice 2026-001234",
      "externalPaymentId": "inv-2026-001234",
      "failureCode": null,
      "failureMessage": null,
      "originator": {
        "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "firstName": null,
        "lastName": null,
        "companyName": "Acme Corp",
        "entityType": "BUSINESS"
      },
      "contact": {
        "id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
        "firstName": "Jane",
        "lastName": "Smith",
        "companyName": null,
        "entityType": "INDIVIDUAL"
      }
    }
  ],
  "meta": {
    "timestamp": "2026-02-27T10:00:00.000Z",
    "pagination": {
      "limit": 10,
      "nextCursor": "eyJjcmVhdGVkQXQiOiIyMDI2LTAyLTI2VDEyOjAwOjAwLjAwMFoiLCJpZCI6ImY0N2FjMTBiLTU4Y2MtNDM3Mi1hNTY3LTBlMDJiMmMzZDQ3OSJ9"
    }
  }
}

Note that the list response includes party summaries (no bank account details). Use the single-payment endpoint to get full details including bank accounts.

Paginating through results

Pagination is cursor-based. The first request omits cursor; the response returns up to limit items and a nextCursor token if more results exist. Pass that token as cursor on the next request to continue. When the last page is reached, nextCursor is null.

# First page
curl "https://{api-host}/v1/payments/?limit=10" \
  -H "Authorization: Bearer $YOUR_API_KEY"

# Next page (use nextCursor from the previous response)
curl "https://{api-host}/v1/payments/?limit=10&cursor=NEXT_CURSOR" \
  -H "Authorization: Bearer $YOUR_API_KEY"

The cursor is opaque -- treat it as a token and do not parse or modify it. A malformed cursor returns 400. Filters (status, direction, originatorId, createdFrom, createdTo) must stay the same across pages; changing them mid-pagination produces undefined results.

Error responses

Code Reason
400 Invalid query parameters
401 Missing or invalid API key

Tracking status updates

Subscribe to payment webhooks to be notified on every status transition -- see Webhooks. Each transition delivers the full payment snapshot plus the status it transitioned from, so you rarely need to call the API at all.

If you prefer not to run a webhook endpoint, poll the single-payment endpoint at an interval of 15 minutes or longer until the payment reaches a terminal status. ACH payments typically settle in 1-2 business days, so frequent polling is unnecessary.

# Check payment status
curl "https://{api-host}/v1/payments/PAYMENT_ID" \
  -H "Authorization: Bearer $YOUR_API_KEY"

See Payment Lifecycle for the full status flow and what each transition means.