Pay

Pay

Store payment methods and pay bills. The Pay API mirrors the accept-payments flow: mint a pay token, store a payment method, initiate a payment, and retrieve the resulting payment attempt.

Related guides: Accept Payments · Pay Sessions

Payment execution is gated

Heads up: bill-payment execution is currently gated off. POST /v1/pay/payments returns 501 PAYMENT_EXECUTION_NOT_AVAILABLE until the pay pillar is enabled. Minting pay tokens and storing payment methods work today; do not integrate against the payment-initiation endpoint yet — track availability in the changelog.

POST/v1/pay/token/create

Server-to-server. Mint a short-lived pay_token (15-minute TTL) that authorizes the hosted Elements add-payment-method / pay flows. Requires client credentials; the client_id is taken from the credential — a mismatched body value is rejected with 403.

Request body

client_user_id*stringYour unique identifier for the end user
bill_idstringBill to pay (required for the pay flow; omit for add-payment-method only)
amountobject{ value: integer (minor units), currency: string } — the amount to authorize
payment_method_idstringPre-bind a saved payment method
metadata_jsonstringOpaque JSON string echoed back on the token

See the Pay Sessions reference for the full pay-token lifecycle (validate + scope).

Sample uses your_client_id · sign in to auto-fill your sandbox key
curl -X POST https://sandbox.api.billerapi.com/v1/pay/token/create \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $BILLERAPI_API_KEY" \
  -d '{
    "client_user_id": "user_12345",
    "bill_id": "bill_abc123",
    "amount": { "value": 1299, "currency": "USD" }
  }'
Response201 Created
{
  "success": true,
  "pay_token": "payt_5f3c…",
  "pay_token_id": "ptid_789xyz",
  "expires_at": "2026-06-29T12:15:00Z"
}
POST/v1/pay/payment-methods

Store a payment method. In production the PAN is tokenized by the hosted Elements page and never touches your servers; this endpoint accepts the card details from that BillerAPI-hosted surface and returns a reusable payment_method_id. Authenticate with a bearer token, a pay token, or client credentials.

Request body

pan*stringPrimary account number (digits, 13–19 chars)
expiry_month*integerExpiry month (1–12)
expiry_year*integerExpiry year (4-digit)
cvvstringCard verification value (3–4 digits)
bucketstringBucket label, e.g. "primary"

The returned payment_method_id is what you pass to POST /v1/pay/payments. Never send raw card data from your own frontend — use the hosted Elements page.

Sample uses your_client_id · sign in to auto-fill your sandbox key
curl -X POST https://sandbox.api.billerapi.com/v1/pay/payment-methods \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer at_sandbox_xyz789" \
  -d '{
    "pan": "4242424242424242",
    "expiry_month": 12,
    "expiry_year": 2030,
    "cvv": "123",
    "bucket": "primary"
  }'
Response201 Created
{
  "payment_method_id": "pm_abc123",
  "bucket": "primary"
}
POST/v1/pay/payments

Initiate a bill payment against a stored payment method. Gated: returns 501 PAYMENT_EXECUTION_NOT_AVAILABLE until payment execution is enabled. An idempotency_key is required (via the Idempotency-Key header or the body field).

Request body

bill_id*stringThe bill to pay
payment_method_id*stringA stored payment method id
idempotency_keystringRetry-safe key (or send the Idempotency-Key header)
amount_minor_unitsintegerAmount in minor units (cents). Defaults to the bill amount.
currencystring3-letter ISO currency code

Outcomes are confirmed by the pay.succeeded / pay.failed webhooks — a 202 means the attempt was scheduled, not that it settled. See Webhooks.

Sample uses your_client_id · sign in to auto-fill your sandbox key
curl -X POST https://sandbox.api.billerapi.com/v1/pay/payments \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer at_sandbox_xyz789" \
  -H "Idempotency-Key: idem_2026-06-25_bill_abc123" \
  -d '{
    "bill_id": "bill_abc123",
    "payment_method_id": "pm_abc123",
    "amount_minor_units": 1299,
    "currency": "USD"
  }'
Response202 Accepted
{
  "payment_attempt_id": "patt_01HX5...",
  "bill_id": "bill_abc123",
  "status": "SCHEDULED"
}
GET/v1/pay/payments/:attemptId

Retrieve a payment attempt by id. This endpoint is not gated — it proxies straight to pay-service. Because payment initiation is gated off today, there are no attempts to retrieve yet; once execution is enabled and POST /v1/pay/payments creates attempts, this returns their current status.

Path parameters

attemptId*stringThe payment attempt id
Sample uses your_client_id · sign in to auto-fill your sandbox key
curl https://sandbox.api.billerapi.com/v1/pay/payments/patt_01HX5 \
  -H "Authorization: Bearer at_sandbox_xyz789"
Response200 OK
{
  "payment_attempt_id": "patt_01HX5...",
  "bill_id": "bill_abc123",
  "status": "SUCCEEDED",
  "amount": "12.99",
  "currency": "USD",
  "confirmation_number": "CNF-998877"
}

The Payment Attempt object

A Payment Attempt records a single attempt to pay a bill. It progresses from SCHEDULED to a terminal state, mirrored by the pay.* webhooks.

Attributes

payment_attempt_id*stringUnique identifier for the payment attempt
bill_id*stringThe bill this attempt is paying
status*stringSCHEDULED | SUCCEEDED | FAILED | ESCALATED
amountstringDecimal amount string in `currency` units, when available
currencystringISO 4217 currency code, e.g. "USD"
confirmation_numberstringBiller-side confirmation / reference number, when the payment succeeds
The Payment Attempt object
{
  "payment_attempt_id": "patt_01HX5...",
  "bill_id": "bill_abc123",
  "status": "SUCCEEDED",
  "amount": "12.99",
  "currency": "USD",
  "confirmation_number": "CNF-998877"
}
Was this page helpful?