> ## Documentation Index
> Fetch the complete documentation index at: https://docs.paywithatoa.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Agent Pay: Payments for AI Agents

> Let an AI agent take payments and make payouts within spending limits a human approved. Atoa enforces the limits server-side.

**Agent Pay** lets your AI agent take payments and make payouts, inside limits a human approved once. Atoa
enforces those limits on its own servers, so nothing the model does can go past them.

![An agent panel feeding the Atoa payment rail, which collects money in from a customer and sends money out to a payee.](https://mintcdn.com/atoaproddocs/i1RSfhZLVHfg2IA3/images/agent-pay/hero-light.png?fit=max\&auto=format\&n=i1RSfhZLVHfg2IA3\&q=85\&s=d3c67568e36177604dde42e23b80fed5)

Your agent asks; the customer approves in your own UI; Atoa enforces the caps on every payment after that.

## How it works

Two calls. Your server creates a payment and sends its `nextAction` to the browser; your browser shows it.

```python Server · Python theme={null}
req = atoa.payment.collect(
    amount={"amount": 45.00},
    order_id="booking-8812",
)
# send req.next_action to your front end
```

```typescript Server · TypeScript theme={null}
const result = await atoa.payment.collect({
  amount: { amount: 45.00 },
  orderId: "booking-8812",
});
// send result.nextAction to your front end
```

```javascript Browser theme={null}
AtoaUI.mount(nextAction, {                    // fetched from your own endpoint
  container: document.querySelector("#checkout"),
  environment: "sandbox",
  onPaymentCompleted: (e) => showReceipt(e),
});
```

The same result works without a browser: it carries a pay-link you can send, or a
[`nextAction`](https://docs.paywithatoa.com/agent-pay/next-action) your agent can read.

## Contracts and limits

A **contract** is a human's standing approval. They set it up once — a cap per payment, a cap per period, an
expiry date, and a direction — and after that your agent can move money inside it without asking again.

Atoa checks every charge against the contract on its own servers. A payment over the cap is refused there, not in
your code. Two things still need a person: setting the contract up, and Strong Customer Authentication (SCA) on
payouts and off-session charges, where the approver enters a one-time code or uses a passkey on Atoa's page.

![Your agent calls Atoa, which enforces consent and caps, then collects money in from a customer or sends money out to a payee.](https://mintcdn.com/atoaproddocs/ZhMdcV3xf3fCdH09/images/agent-pay/money-flow-overview-light.svg?fit=max\&auto=format\&n=ZhMdcV3xf3fCdH09\&q=85\&s=39b67a74f5ffbcbc4f11f7597daf6028)

## Collect or send

![Decision tree: which way is the money going? Receiving splits on whether the customer is here now — yes is a pay-link (payment.collect, no contract), no is an off-session charge (payment.collect under a COLLECT contract). Sending is payment.send under a SEND contract the owner authorises once.](https://mintcdn.com/atoaproddocs/ZhMdcV3xf3fCdH09/images/agent-pay/decision-tree-light.svg?fit=max\&auto=format\&n=ZhMdcV3xf3fCdH09\&q=85\&s=ec30bc66adc7eab6c866667b24b0788f)

* **Customer present** → `payment.collect` returns a pay-link/QR. No contract. → [Collect](https://docs.paywithatoa.com/agent-pay/collect.md)
* **Customer not present** → `payment.collect` with a `contractId`, under a **COLLECT contract** the customer approved once. → [Collect](https://docs.paywithatoa.com/agent-pay/collect.md#off-session)
* **Paying out** → `payment.send` under a **SEND contract** the account owner approved once. → [Send](https://docs.paywithatoa.com/agent-pay/send.md)

## Get started

Get a **sandbox** API key from the [Atoa dashboard](https://dashboard.paywithatoa.co.uk/) and set it in your
environment. Sandbox and production keys are separate.

```bash npm theme={null}
npm install @atoapayments/agent-pay        # Node 22+
```

```bash pip theme={null}
pip install atoa-agent-pay         # Python 3.10+
```

```bash theme={null}
export ATOA_API_KEY="your_sandbox_api_key"
```

Create a client and register your agent. Registering twice with the same key returns the same agent, so it's safe
to run on every start. The example generates a signing key in memory — in production, load it from your secrets
manager or keep it out of your process entirely with a [KMS signer](https://docs.paywithatoa.com/agent-pay/reference.md#kms-custom-signer). See
[Authentication](https://docs.paywithatoa.com/agent-pay/reference.md#authentication) for the other ways to pass credentials.

```python Python theme={null}
import atoa_agent_pay

private_key_pem, _ = atoa_agent_pay.generate_es256_keypair()
# api_key is read from ATOA_API_KEY; the public key is derived from private_key_pem.
atoa = atoa_agent_pay.init(environment="sandbox", private_key_pem=private_key_pem)

atoa.agent.register(name="Bookings assistant")   # registers the derived public key
```

```typescript TypeScript theme={null}
import { createAgentPayClient, generateEs256KeyPair } from "@atoapayments/agent-pay";

const { privateKeyPem } = generateEs256KeyPair();
// apiKey is read from ATOA_API_KEY; the public key is derived from privateKeyPem.
const atoa = createAgentPayClient({ environment: "sandbox", privateKeyPem });

await atoa.agent.register({ name: "Bookings assistant" });   // registers the derived public key
```

## Collect your first payment

Create a payment on your server, then show it. In sandbox, choose the **Atoa Test Bank** in the checkout and pick
the outcome yourself.

```python Python theme={null}
req = atoa.payment.collect(
    amount={"amount": 45.00},    # pounds, not pence (45.00 = £45); GBP default
    order_id="booking-8812",     # your own reference
)

print(req.payment_url)           # open this and choose an outcome

settled = atoa.payment.await_settled(req.payment_request_id)
print(settled.status)            # COMPLETED
```

```typescript TypeScript theme={null}
const req = await atoa.payment.collect({
  amount: { amount: 45.00 },     // pounds, not pence (45.00 = £45); GBP default
  orderId: "booking-8812",       // your own reference
});

console.log(req.paymentUrl);     // open this and choose an outcome

const settled = await atoa.payment.awaitSettled(req.paymentRequestId);
console.log(settled.status);     // COMPLETED
```

To show the checkout inside your own product instead of sending a link, send `req.next_action` from your
server to the browser and mount it:

```javascript Browser theme={null}
import { AtoaUI } from "@atoapayments/pay-embed";

AtoaUI.mount(nextAction, {                       // fetched from your own endpoint
  container: document.querySelector("#checkout"),
  environment: "sandbox",
  onPaymentCompleted: (e) => showReceipt(e),
});
```

Full walkthrough: [Embedded checkout](https://docs.paywithatoa.com/agent-pay/embed.md).

## Send your first payout

Create a SEND contract, approve it once at the `authorizationUrl`, then send. Every payout pauses for the
business owner's approval before money moves. In sandbox the recipient account decides the outcome — list the
accounts with `atoa.sandboxTestAccounts()`.

```python Python theme={null}
contract = atoa.contract.create(
    type="SEND",
    name="Supplier payouts",
    limits={
        "max_per_payment": 500.00,
        "period_limits": [{"amount": 2000.00, "period": "MONTH"}],
        "valid_to": "2026-12-31T23:59:59Z",
    },
)

print(contract.authorization_url)               # the account owner approves here
atoa.contract.await_active(contract.contract_id)

result = atoa.payment.send(
    contract_id=contract.contract_id,
    payments=[{
        "amount": {"amount": 250.00},
        "beneficiary": {"name": "ACME LTD", "sortCode": "040004", "accountNumber": "12345678"},  # sandbox: settles COMPLETED
        "orderId": "payout-3001",
    }],
)

print(result.next_action.approval_url)          # the owner approves the payout here — in sandbox, open it yourself
atoa.payment.await_decision(result.next_action.approval_id)   # APPROVED → the payment executes

print(result[0].status)                         # COMPLETED
```

```typescript TypeScript theme={null}
const contract = await atoa.contract.create({
  type: "SEND",
  name: "Supplier payouts",
  limits: {
    maxPerPayment: 500.00,
    periodLimits: [{ amount: 2000.00, period: "MONTH" }],
    validTo: "2026-12-31T23:59:59Z",
  },
});

console.log(contract.authorizationUrl);         // the account owner approves here
await atoa.contract.awaitActive(contract.contractId);

const result = await atoa.payment.send({
  contractId: contract.contractId,
  payments: [{
    amount: { amount: 250.00 },
    beneficiary: { name: "ACME LTD", sortCode: "040004", accountNumber: "12345678" }, // sandbox: settles COMPLETED
    orderId: "payout-3001",
  }],
});

console.log(result.nextAction.approvalUrl);     // the owner approves the payout here — in sandbox, open it yourself
await atoa.payment.awaitDecision(result.nextAction.approvalId);   // APPROVED → the payment executes

console.log(result[0].status);                  // COMPLETED
```

That's a payout in sandbox. Every payout and every off-session charge returns an approval step like this one —
see [Collect](https://docs.paywithatoa.com/agent-pay/collect.md#sca-on-a-charge) and [Send](https://docs.paywithatoa.com/agent-pay/send.md#sca-on-a-payout).

## Next steps

> **[Embedded checkout](https://docs.paywithatoa.com/agent-pay/embed.md)**
>
> Render the payment inside your own product.

> **[Collect — money in](https://docs.paywithatoa.com/agent-pay/collect.md)**
>
> Pay-links, off-session charges, SCA, refunds.

> **[Send — money out](https://docs.paywithatoa.com/agent-pay/send.md)**
>
> Payouts, SCA, batches, contract limits.

> **[AI agents & tools](https://docs.paywithatoa.com/agent-pay/ai-agents.md)**
>
> Hand the SDK to your model as tools.

> **[Reference](https://docs.paywithatoa.com/agent-pay/reference.md)**
>
> Auth, methods, types, errors, sandbox, go-live.

> **[Download the notebook · run it locally](https://atoa-pdf.s3.eu-west-2.amazonaws.com/developer-guide.ipynb)**
>
> The full flow as a runnable Jupyter notebook — `pip install atoa-agent-pay`, add a sandbox `ATOA_API_KEY`, run.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.