Skip to main content
Show Atoa’s checkout inside your own product. Your server creates a payment and sends its nextAction to the browser. Your browser passes that to AtoaUI.mount() and gets a callback when the customer is done.
A chat thread drawn as grey placeholder bubbles, with the Atoa checkout rendered inside one of them and outlined as your checkout div.

Your thread, your container — the checkout is the only part Atoa draws.

Install

Pin an exact version in production. A payment surface should not change under you between deploys.

Create a payment

The API key creates payments, so it stays on your server and never reaches the browser. The embed needs a backend. If you don’t have one, send the customer a pay-link instead. Your server creates the payment and returns its nextAction:
Send the nextAction, not the result. A full Agent Pay result carries beneficiary details, contact details and your own metadata — none of which the checkout reads.
Approval secrets travel in the URL fragment. Don’t copy them into query strings, logs, or your own code.

Mount the checkout

Fetch it and mount it. Set your container’s width — the iframe sets its own height.
mount() returns a handle for unmounting and refreshing the surface. Destroy it when your component unmounts, or the iframe outlives the view that owned it. There is no method to create a payment from the browser, so re-rendering or reloading always shows the same payment rather than creating a second one.

Payment surfaces

mount() reads nextAction.type and shows the right screen — you don’t branch on it.
Four checkout screens in a grid: the bank picker, an approval with Face ID, contract setup with saved cards, and the add-a-card form.

The same mount call, four of the screens it can produce.

Those four are the whole set an Agent Pay call returns. Picking a card, entering card details and enrolling a passkey are steps inside those screens, on Atoa’s origin — they don’t come back to you as actions to render. The last row is the forward-compatibility rule: a type this version doesn’t know renders the action’s fallback, so Atoa can add one without breaking your build. See the nextAction reference.

Handle events

Domain events tell you what happened to the money. Every one arrives in the same envelope:
Which resource the event names follows from what you mounted: a payment surface emits payment.*, a contract surface contract.*, an approval surface approval.*. The subject’s id is always on the envelope as resourceId — read it there rather than out of data. data is a passthrough of what the frame reported, so a settled payment carries its id, status and timestamp, while a failure carries only the reason. resourceId is populated on every event except an approval’s, where the mount holds the client secret and never the id.

Events with no named callback

These are emitted today and reach onEvent only — there is no on… option for them. If any matter to you, handle them there: contract.expired · contract.revoked · contract.suspended · contract.ended · contract.invalid · contract.failed · approval.superseded · approval.cancelled
type is an open string, so onEvent is also where types added after your installed version arrive. Treat data as additive — read the keys you know, ignore the rest. On an approval surface resourceId is null: the mount holds the client secret, never the approval’s id.
These fire on a real status change recorded by Atoa, not on an optimistic UI guess — but they reach you through the customer’s browser, which can close, lose signal, or never send them. Confirm on your own backend before you fulfil an order, with payment.get or awaitSettled. Webhooks don’t fire for contract payments in this phase, so polling is the path.

UI callbacks [#ui-callbacks]

Separate from the events above, and these can fire many times. They describe the frame, never the money, so never branch on them to decide an order is paid.

Options and theming

AtoaUI.mount(nextAction, options)Handle.
Four wireframe hosts: a chat thread, an in-app assistant panel, a mobile sheet, and a plain inline card, each with the checkout slot marked.

One mount call, whatever container you give it.

Theming

That is the whole theme surface. Anything unrecognised normalises to "auto". Colours, fonts, corner radius and density are deliberately not settable. A checkout a host can restyle is a checkout a host can make look like something it is not, and the trust the frame carries is what you are embedding. Brand treatment, when it ships, will be issued to your business from Atoa’s side — not accepted from the page.
Don’t modify the iframe’s allow or sandbox attributes. Removing them breaks passkeys, one-time-code autofill, and the redirect to the customer’s bank. The loader warns in the console if it detects either was altered.

The handle

Errors

mount() throws straight away — before any iframe exists — if container isn’t an HTMLElement, or if environment isn’t 'sandbox' or 'production'. Everything after that reaches onError as an ApiFailure: An onError doesn’t end the surface — the frame may recover. Call destroy() yourself if you want to stop. If the container stays blank, check the console. AtoaUI.mount: the embed frame at <origin> never connected within <n>ms means the iframe loaded but never answered — usually a frame-src content-security policy on your own page, or a network path that can’t reach Atoa. If nothing is logged at all, container probably resolved to null.

Test in sandbox

Create the payment with a sandbox API key and pass environment: 'sandbox'. Choose Atoa Test Bank in the checkout and pick the outcome you want. No money moves — see Sandbox. To go live, switch to a production API key and pass environment: 'production'. Nothing else changes.

Next

Approvals SDK

Show just the human sign-off step, without the whole checkout.

The nextAction contract

What mount() reads — for anyone rendering their own UI.