POST
Step 4. Send action: "submit" to POST /v1/customers/{customerId}/batch-payments/submit with the scaChallenge from initiate and a confirmation object. There are three ways to authenticate (see the submit examples below):
  • Passkey — first send action: "passkey-challenge" to the same endpoint to get a passkeySession and fido2options, run the passkey on the device, then submit with confirmation: { "method": "passkey", "passkeySession": …, "assertion": … }.
  • 2FA code — submit with confirmation: { "method": "totp", "totp": …, "accessToken": … }. accessToken is the access_token from sign-in — not the id_token you send as the Authorization bearer.
  • PIN (program capability) — submit with confirmation: { "method": "pin", "pin": … }.
On success you get a batchId. Then track the batch.

Paying a draft

batchId is required on this route. It comes from verify, so this endpoint can only pay a batch that went through verification — omitting it is a 400, not a new batch. The draft is checked for expiry and for belonging to you, and it can only be paid once — a second submit of the same draft is rejected rather than paying twice. One that has expired, was already submitted, or belongs to someone else is rejected before any money moves.
The older unsuffixed route (POST /v1/customers/{customerId}/batch-payments) keeps the previous contract: batchId is optional there, and omitting it creates a new batch. Existing integrations are unaffected.
This endpoint serves initiate, passkey-challenge and submit — the three steps that authorise a payment. action is required; "verify" is rejected with 403 and belongs to verify.Send the same items you sent to initiate. The scaChallenge is bound to the payee account and amount of every row, in order, so any change between the two calls invalidates it.
Availability. This route is rolled out per program and returns 403 until yours is enabled. Until then, send action: "submit" to POST /v1/customers/{customerId}/batch-payments — the request and response bodies are identical. See the overview.

Multi-customer and back office

Paying from the accounts of many customers in one batch? Use the path-less form, POST /v1/batch-payments/submit, which resolves each item’s payer account against you. It is back-office only and rejects any account you are not responsible for.

Passkey step-up is always your passkey

The challenge is issued against the acting caller — the signed-in user’s own email and identity — never the customer being paid. An operator paying on behalf of a customer authenticates with their own passkey, and the assertion is bound to that identity, so a session minted for one user cannot authorise another user’s batch. The ceremony itself is identical on every route: same action: "passkey-challenge", same passkeySession + fido2options response, same confirmation shape on submit. Only the tenant the credential is looked up in differs: That’s because a back-office operator’s programId is the program they’re acting on, taken from the query string — their Cognito user and FIDO credentials live in the back-office pool, not in that program. One consequence worth knowing: a back-office passkey ceremony always resolves to production, even when the batch itself is a sandbox batch. Send the Origin header on the challenge request. It is carried into the WebAuthn ceremony, and a mismatch fails verification.

Retry safely

Pass an idempotencyKey (a UUID) in the request body and reuse the same key on every retry, so a lost response can’t send the whole batch twice. See Idempotency for the full retry contract.
Batch takes the key as a body field only — unlike the single-resource creates, it doesn’t read an Idempotency-Key header.
Mint a fresh UUID for every batch, and never a constant, a template value, or a reused test fixture. A batch’s identity comes from the idempotencyKey alone — it is not scoped to the customer the way the single-resource creates are, and it does not make submit replayable.Two submissions that share a key land on the same batchId and re-run the submission rather than returning the first result: the funds are reserved again and the payments can be sent a second time. Parameters aren’t compared either, so a reused key with a different list of payments is not rejected.If a submit response is lost, do not resend. Track the batch with the batchId to find out whether it landed — or, if you never received one, list the customer’s batches.
idempotencyKey is optional today but will be required in a future release. Start sending it now.

Authorizations

Authorization
string
header
required

The caller's id_token from authentication — the ID token, not the access_token. The program and environment come from the token.

Path Parameters

customerId
string
required

The customer id.

Body

application/json
action
enum<string>
required

Required — initiate, passkey-challenge or submit. verify is rejected with 403; it belongs to the /batch-payments/verify route.

Available options:
initiate,
passkey-challenge,
submit
payments
object[]

The payment items. Required for initiate and submit, and must be identical between them or SCA verification fails.

scaChallenge
object

The strong-customer-authentication challenge from initiate. Pass it back on submit.

confirmation
Passkey · object

Step-up confirmation (for submit). Set method to passkey, totp, or pin and include that method's fields. pin is a program capability — see Program capabilities.

idempotencyKey
string<uuid>

Optional UUID. Reuse the same key to make submit safe to retry — batch and item IDs are then derived deterministically, so a retry cannot double-pay.

batchId
string<uuid>

Required. The draft returned by verify. This route only pays a batch that was verified first, so initiate and submit both need it. It is checked for expiry and ownership and cannot be paid twice.

Response

Result for the requested step — initiatetotalAmount/currency/scaChallenge; passkey-challengepasskeySession/fido2options; submitbatchId/status/counts.

The response is of type object.