Skip to main content
Examples default to Python (snake_case) — most AI integrations are Python. TypeScript is the same surface in camelCase (await_settledawaitSettled), with identical parameters and byte-identical signed requests.

Authentication

Two credentials on every request:
  • API key (tier 1) — read from ATOA_API_KEY; identifies your business and pins the environment. Sandbox and production keys are separate and not interchangeable.
  • ES256 signing key (tier 2) — the SDK signs every request; Atoa verifies against the public key you registered once. The private key is never sent. Requests can’t be tampered with or replayed.
You never sign anything yourself — you only decide where the private key lives: pass privateKeyPem loaded from your secrets manager, or a custom signer (below) so the key never enters your process. Re-registering the same agent with a different key is rejected. Keep both credentials out of source control, logs, and client-side code. Three ways to supply the two credentials — pick one (Python shown; TypeScript mirrors it in camelCase):

KMS / custom signer

Pass a signer instead of a PEM. The SDK hands it the canonical subject string per request and expects a detached compact JWS back: b64url(header)..b64url(rawSig), where rawSig is the 64-byte IEEE-P1363 r‖s form (a KMS returns DER — convert it). Register the public key.

Methods

Every operation is laid out the same way — Parameters (marked required or optional), a Request example (Python + TypeScript), then the Response type and an example. Python is snake_case; TypeScript mirrors it in camelCase; every call is async. Amounts are decimal major units — { amount: 12.50, currency?: "GBP" }, so £12.50 is 12.50, never 1250; currency defaults to GBP. Pagination. Every list(opts?) returns a Page<T> envelope; the paired listAll(opts?) walks every page and returns a flat T[].
object
Common paging inputs on every list: page (zero-based, default 0) and size (default 20).

agent

agent.register(opts)

Bootstrap your agent’s identity — the challenge → sign → register handshake in one call. Idempotent: the same key/env/business returns the same agent; a changed name/description is a metadata update, not a conflict. Parameters
string
required
Human-readable agent name shown in the Atoa dashboard.
string
Free-text description of what this agent does.
string
Your ES256 public key (PEM, SPKI). Pass it only with a KMS/custom signer; omit when the SDK holds the private key (it derives the public key itself).
Request
ResponseRegisteredAgent
string
The id this client signs as from now on.
string
The business this agent belongs to.
'sandbox' | 'production'
string
string
Free-text description, if you set one.
string
Echoed here onlyme/list do not return it.

agent.me()

Read this agent’s own identity as the service sees it. Takes no parameters. Request
ResponseAgentIdentity
string
string
'sandbox' | 'production'
string
string
Free-text description, if you set one.
string
e.g. ACTIVE.

agent.list()

List every agent registered under your business + environment. Authenticated by the SDK API key alone (no JWS). Takes no parameters. Request
ResponseAgentIdentity[] An array of AgentIdentity (shape as agent.me).

contract

contract.create(input)

Create a spending contract. Returns it PENDING_AUTHORIZATION with an authorizationUrl the account holder (SEND) or customer (COLLECT) opens to authorize; poll awaitActive until ACTIVE. Parameters
string
required
Your label for this spending authority (e.g. “Supplier payouts”).
'SEND' | 'COLLECT'
default:"SEND"
SEND (money out) or COLLECT (money in, off-session).
string
Required for COLLECT — the customer this contract charges (id from customer.create). Ignored for SEND.
string
Free-text description.
ContractLimitsInput
required
The per-payment cap, ≥1 period cap, and validity window.
Request
ResponseContract
string
string
The agent that owns this contract.
'sandbox' | 'production'
'SEND' | 'COLLECT'
Emitted on every row so you can branch without inference.
string (ISO datetime)
ContractStatus
Fresh from create: PENDING_AUTHORIZATION.
string
The page the human authorizes at (create/update only — absent on get/list).
ContractLimits
The caps echoed back with defaults resolved.
ContractUsageWindow[]
Live per-period headroom (populated on get — see below).
string
COLLECT only — the customer this contract charges.
{ paymentMethodId, lastFourDigits?, brand?, expiryDate? }
COLLECT only — the masked card the customer linked. Absent until they link one on the contract page.
number
COLLECT only — authorized terms version (1 on first approval; increments per approved update).
{ limits, requestedAt } | null
A staged limits change; the limits above stay enforced until it’s authorized. null when nothing is staged.

contract.awaitActive(id, opts?)

Poll get until the contract is ACTIVE, with internal backoff. Throws AUTHORIZATION_TIMEOUT on timeout or AUTHORIZATION_FAILED if the human declined / the link expired. Parameters
string
required
number
How long to wait before throwing AUTHORIZATION_TIMEOUT.
Request
ResponseContract The Contract (shape above) once status is ACTIVE.

contract.get(id)

Read a contract’s current state, including live usage. No webhooks — poll this. Parameters
string
required
Request
ResponseContract The Contract shape (see contract.create), plus live per-period usage:
ContractUsageWindow[]
One window per period cap — remaining = cap − usedThisPeriod − reserved.

contract.list(opts?) · contract.listAll(opts?)

List this business’s contracts. list returns one page; listAll walks every page and returns a flat array. (Two methods, one underlying list — grouped for that reason.) Parameters
ListContractsOptions
All filters optional. listAll takes the same shape minus page.
Request
ResponsePage<Contract> (listAllContract[])
Page<Contract>
The pagination envelope; data is Contract[].
Contract[]
Every page flattened.

contract.update(id, input)

Change a contract’s limits — a re-consent. Returns it PENDING_AUTHORIZATION with a new authorizationUrl; the old consent stays live and payable until re-authorized. Poll awaitActive again. Parameters
string
required
{ limits: ContractLimitsInput }
required
The new caps (same shape as create).
Request
ResponseContract The Contract, back at PENDING_AUTHORIZATION with a fresh authorizationUrl.

contract.revoke(id)

Terminate the contract. Payments against it are then rejected. Parameters
string
required
Request
ResponseContractRevokeResult
object
{ contractId, type, status } — the lean terminal confirmation.

payment

payment.collect(input)

Money in, one verb. contractId absent → a pay-link/QR the customer pays now. contractId present → an off-session charge on the linked method of that COLLECT contract (no card details, ever). Either way, follow with awaitSettled(paymentRequestId). One payment per call; not idempotent. Parameters
{ amount, currency? }
required
The amount to collect.
string
required
Your order reference — echoed on the payment, webhooks, and dashboard. Not an idempotency key.
string
Present → off-session charge on this COLLECT contract’s linked method. Absent → a pay-link/QR the customer pays now.
string
The managed Atoa customer (id from customer.create). Required for an off-session charge — pass it with contractId, and it must match the contract’s own atoaCustomerId (a mismatch is rejected with CONTRACT_CUSTOMER_MISMATCH). Optional for a pay-link, where it links the payment to a managed customer (and enables savePaymentMethod). Not the same as customerId, which is your own reference.
string
YOUR id for the payer, echoed back everywhere. Omit for guest checkout (one is synthesized).
{ fullName?, email?, phoneCountryCode?, phoneNumber? }
Prefill the checkout with the customer’s details.
string
Where the customer lands after paying.
number (ms)
default:"180000"
Pay-link lifetime (default 3 minutes).
The rest are optional power knobs — the simple path never needs them:
('PAY_BY_BANK' | 'CARD')[]
Restrict how the customer may pay. Both show by default.
boolean
Save the card during checkout. Needs atoaCustomerId + paymentMethod: ['CARD']; incompatible with splitBill.
string
Charge under a specific store (see store.list); defaults to the primary store.
'EXTERNAL_DISPLAY' | 'EXTERNAL_DISPLAY_PNG' | 'RECEIPT' | 'RECEIPT_PNG'
Till/receipt QR template — sets templateUrl.
boolean
default:"true"
Allow several attempts while the link is live.
boolean
object
Echoed back to your redirectUrl after payment.
string
Free-text note captured on the payment.
Request
ResponsePaymentRequest
string
Poll awaitSettled with this — the same id Atoa’s checkout widgets + direct API use.
string
Your reference, echoed back.
string
Your payer reference (or the synthesized one for guest checkout).
Amount
The requested amount + currency.
string
Pay-link mode — the link/QR the customer pays at. Also expiresAt.
PaymentStatus / string
Off-session charge mode — immediate charge state + the contract it ran under.
NextAction
Off-session charge mode, only when the customer must approve — see approval gate. Absent for a pay-link.

payment.send(input)

Money out against an ACTIVE SEND contract. payments is always an array (1–20; a single payment = one element); the result is a Payment[] in the same order. A terminal FAILED with a failureReason is a returned per-item Payment, not a thrown error — only operational faults throw. Confirm each with awaitSettled. Parameters
string
required
The ACTIVE SEND contract every instruction is paid against.
SendPaymentInstruction[]
required
1–20 instructions; each orderId must be unique in the batch.
Request
ResponseSendResult (a Payment[] + optional nextAction) A Payment[] in input order (iteration / result[0] work as before), with an optional nextAction (present when an owner approval gates the batch — see approval gate). See payment.get for the full Payment shape.

payment.get(id)

Read one payment. One read model for both directions; accepts a paymentRequestId (CREDIT parent — current/best state) or a paymentIdempotencyId (one attempt). Parameters
string
required
A paymentRequestId or a paymentIdempotencyId.
Request
ResponsePayment
'CREDIT' | 'DEBIT'
CREDIT = money in (COLLECT) · DEBIT = money out (SEND). Discriminates which fields below are present.
PaymentStatus
AWAITING_AUTHORIZATION · PENDING · AUTHORIZED · COMPLETED · FAILED · CANCELLED · EXPIRED · PARTIALLY_REFUNDED · REFUNDED.
number / string
What settled — flat, not nested.
string
Your reference (not a de-dup key).
string | null
Settlement-attempt id; null until an attempt exists.
string
SEND (DEBIT) only — the contract this draw ran under.
{ name, sortCode?, accountNumber? }
SEND (DEBIT) only — who you paid, masked.
string
SEND (DEBIT) only — present while the draw is gated on an approval.
string
COLLECT (CREDIT) only — the payment-request (parent) id.
string
COLLECT (CREDIT) only — your own payer reference, and the Atoa customer id (once known).
string
COLLECT (CREDIT) only — who paid, once an attempt exists: payer name, bank, and masked account.
string
Either direction — present on a terminal FAILED/CANCELLED.

payment.awaitSettled(id, opts?)

Poll a payment until it stops moving (COMPLETED/FAILED/CANCELLED, EXPIRED on CREDIT, or AUTHORIZED). Throws SETTLEMENT_TIMEOUT on timeout. Accepts a paymentRequestId or a paymentIdempotencyId. Parameters
string
required
A paymentRequestId or a paymentIdempotencyId.
number
How long to wait before throwing SETTLEMENT_TIMEOUT.
Request
ResponsePayment The Payment (shape above) at a resting state.

payment.list(opts?) · payment.listAll(opts?)

List this agent’s payments across both directions. list returns one page; listAll walks every page. (Two methods, one underlying list.) Parameters
ListPaymentsOptions
All filters optional. listAll takes the same shape minus page.
Request
ResponsePage<Payment> (listAllPayment[])
Page<Payment>
The pagination envelope; data is Payment[] (shape above).
Payment[]
Every page flattened.

payment.refund(id, input)

Refund a COMPLETED collected payment — full or partial. Parameters
string
required
The collected payment to refund.
CreateRefundInput
required
{ amount, reason? } — the amount must not exceed the paid amount. Sandbox: reason: "FAILURE TEST" forces a FAILED refund.
Request
ResponseRefund
string
The refund id — pass to cancelRefund while still INITIATED.
string
The collected payment this refund belongs to.
string (ISO datetime)
RefundStatus
INITIATED · COMPLETED · FAILED · CANCELLED.
Amount
The refunded amount (refund_amount, a float, in Python).
Amount
The original paid amount.

payment.listRefunds(id) · payment.cancelRefund(refundId)

List the refunds of one collected payment, or cancel a still-INITIATED refund. Parameters
string
required
string
required
Only a not-yet-processed refund can be cancelled.
Request
Response
Refund[]
The refunds of the payment.
CancelRefundResult
{ refundId, message? }.

payment.cancel(id)

Cancel an unpaid collect (link) or a charge resting at AUTHORIZED. Parameters
string
required
Request
ResponsePayment The CANCELLED payment.

payment.awaitDecision(approvalId)

Poll an approval (from a send/collect nextAction) to its terminal decision. See the approval gate for the flow. Parameters
string
required
From a nextAction.
number
How long to wait before throwing SETTLEMENT_TIMEOUT.
Request
ResponseApproval
string
'SEND' | 'COLLECT'
Which side the approval gates.
string
The contract this approval is bound to.
ApprovalStatus
PENDING · APPROVED · DECLINED · CANCELLED · EXPIRED · SUPERSEDED.
string (ISO datetime)
When the approval lapses.
string (ISO datetime)
string
Who decided and when, once resolved.

payment.cancelApproval(contractId, approvalId)

Agent-initiated cancel of a pending approval — voids the underlying draws. Parameters
string
required
string
required
Request
Response
{ approvalId, status }
The cancelled approval id + its new status.

customer

customer.create(input)

Create a managed customer — needed for off-session COLLECT contracts (the customer links a card to the contract on its authorization page). Guest checkout does not require one. Parameters
string
required
2–30 characters.
string
Valid email. Either email or a phone number is required.
string
Digits only (e.g. 44). Pair with phoneNumber.
string
Digits only, without country code.
'INDIVIDUAL' | 'BUSINESS'
default:"INDIVIDUAL"
string
Business customers only.
string
Optional postal details.
Request
ResponseCustomer
string
The atoaCustomerId used across collect + saved cards.
string
string
'INDIVIDUAL' | 'BUSINESS'
string (ISO datetime)

customer.get(id) · customer.update(id, input) · customer.delete(id)

Read, edit, or remove one managed customer. (Three verbs on one resource id — grouped for that reason.) Parameters
string
required
Partial<CreateCustomerInput>
required
Any subset of the create fields.
Request
Response
Customer
The Customer shape (above).
DeleteResult
{ success, message }.

customer.list(opts?)

List the customers this agent created. Parameters
{ page?, size? }
Request
ResponsePage<Customer>
object
The pagination envelope; data is Customer[].

store · client-level

store.list(opts?)

List this business’s stores — discover a storeId to tag a collect by location. Parameters
{ page?, size? }
Request
ResponsePage<Store>
object
The pagination envelope; data is Store[]. Pass a store’s id as storeId on a collect.

checkAvailability()

Unauthenticated health probe. Takes no parameters and never throws — check the returned flag. Request
ResponseAvailabilityStatus
object
Whether the backend is reachable — inspect the flag rather than catching an error.

sandboxTestAccounts()

The sandbox SEND recipients and the outcome each forces. Takes no parameters; production has none. Request
ResponseSandboxTestAccounts
object
{ sandbox, note, accounts } — the recipient accounts and their forced outcomes.

Errors

Business outcomes are returned — branch on the Payment’s status and failureReason. Operational faults are thrown AgentPayError subclasses — catch and switch on code. Returned failureReason values: Thrown AgentPayError subclasses: Retry guidance:
  • Retry: RATE_LIMIT (back off), SERVER_ERROR, NETWORK_ERROR and timeouts — but re-read with get / awaitSettled first; the operation may still be in flight, and orderId is not a de-duplication key.
  • Fix first: VALIDATION_ERROR, AUTH_ERROR, PARAMS_CONFLICT, CAP_EXCEEDED (charge ≤ .remaining), CONTRACT_NOT_ACTIVE (re-approve).
  • Don’t retry: a returned FAILED is a decision, not a glitch.

Sandbox

Create the client with environment: "sandbox" and a sandbox API key. No real money moves; you choose every outcome. Production is the same code with a production key and environment: "production" — real banks, real approvers, real money.

Approval gate

Every send and every off-session charge pauses for approval. A send result (still a Payment[]) and an off-session collect result carry the action under nextAction (TS) / next_action (Python); a pay-link collect has none. The approver is the party bound to the contract — the business owner for SEND, the customer for an off-session COLLECT — deciding on Atoa’s hosted page with a one-time code or a WebAuthn passkey. Your app never collects the credential.
string
Always "APPROVAL" today; switch on it so new action kinds don’t break you.
string
Pass to awaitDecision / cancelApproval.
string
Bearer secret for the hosted page / the approvals browser SDK.
string
The hosted approval page to hand to the approver.
string (ISO datetime)
When the approval lapses.

Approvals browser SDK

@atoapayments/agentic-payment-approvals-js embeds Atoa’s hosted approval page as an iframe inside a container you provide and resolves to the decision. Browser/TypeScript only; zero dependencies. Python integrators share the approvalUrl or drive this from their web layer. Full guide: Approvals SDK.
confirmApproval returns an ApprovalHandle (result · on(…) · destroy()) — it is not awaited directly. Other options: theme (bounded, contrast-clamped tokens), labels.approve (APPROVE | PAY | CONFIRM | AUTHORIZE), onResult, apiUrl (local-stack override). Events: opened · loaded · approved · declined · expired · superseded · error · closed.
The credential — code or passkey — is entered only on Atoa’s page. Never build your own form that collects it.

Go-live checklist

  1. Production API key as ATOA_API_KEY, client with environment: "production".
  2. Signing key from a secrets manager, or a KMS signer — never a generated throwaway.
  3. Branch on all three shapes: returned COMPLETED, returned FAILED/CANCELLED with a failureReason, thrown AgentPayError.
  4. Deliver the approvalUrl to the approver (or drive the browser SDK) on every send and off-session charge; handle APPROVAL_DECLINED / APPROVAL_EXPIRED; observe with awaitDecision.
  5. Register webhooks for status changes; keep get / awaitSettled polling as a fallback.
  6. Run one small real payment end to end — including a declined and a cancelled path — before scaling up.

Changelog

  • 0.0.1 — initial release.
Identifiers (paymentRequestId, customer ids, statuses, field names) carry straight over to Atoa’s direct API if you outgrow the SDK.