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

# Agent Pay: Payments for AI Agents

> Give AI agents a guarded way to move money within human-approved limits. Collect takes money in, Send pays out, and Atoa enforces every cap.

**Agent Pay** gives your AI agents a guarded way to move money — within limits a human approved once.
**Collect** takes money in; **send** pays money out. Contracts are AP2-aligned mandates; sends and off-session
charges pause for Strong Customer Authentication. Atoa enforces every limit server-side.

<Frame>
  <img className="block dark:hidden" src="https://mintcdn.com/atoa-payments-limited/KEZXxpd7888D-dlX/images/agent-pay/money-flow-overview-light.svg?fit=max&auto=format&n=KEZXxpd7888D-dlX&q=85&s=fb7406f6274e25e83308c9ae90fc13f8" alt="Your agent calls Atoa, which enforces consent and caps, then collects money in from a customer or sends money out to a payee." width="960" height="500" data-path="images/agent-pay/money-flow-overview-light.svg" />

  <img className="hidden dark:block" src="https://mintcdn.com/atoa-payments-limited/KEZXxpd7888D-dlX/images/agent-pay/money-flow-overview-dark.svg?fit=max&auto=format&n=KEZXxpd7888D-dlX&q=85&s=6e6373d58b70f8b8b96f6166abd8451a" alt="Your agent calls Atoa, which enforces consent and caps, then collects money in from a customer or sends money out to a payee." width="960" height="500" data-path="images/agent-pay/money-flow-overview-dark.svg" />
</Frame>

## Install

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

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

## Setup

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 theme={null}
export ATOA_API_KEY="your_sandbox_api_key"
```

Create a client and register your agent. Registration is idempotent. The ES256 key signs every request; here it's
generated in memory — in production, load it from your secrets manager or use a [KMS signer](/agent-pay/reference#kms-custom-signer).
Both ways to supply credentials — env var vs. explicit — are covered in [Authentication](/agent-pay/reference#authentication).

<CodeGroup>
  ```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
  ```
</CodeGroup>

## Receiving or sending?

<Frame>
  <img className="block dark:hidden" src="https://mintcdn.com/atoa-payments-limited/KEZXxpd7888D-dlX/images/agent-pay/decision-tree-light.svg?fit=max&auto=format&n=KEZXxpd7888D-dlX&q=85&s=b8793c5895af03747933a1fb48c1ad4f" alt="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." width="1040" height="452" data-path="images/agent-pay/decision-tree-light.svg" />

  <img className="hidden dark:block" src="https://mintcdn.com/atoa-payments-limited/KEZXxpd7888D-dlX/images/agent-pay/decision-tree-dark.svg?fit=max&auto=format&n=KEZXxpd7888D-dlX&q=85&s=df1e3fd7e49e268089ecdbed66091cfc" alt="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." width="1040" height="452" data-path="images/agent-pay/decision-tree-dark.svg" />
</Frame>

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

## Collect your first payment

Create a pay-link and wait for the result. In sandbox, open the link, pick the **Atoa Test Bank**, and choose the
outcome yourself.

<CodeGroup>
  ```python Python theme={null}
  req = atoa.payment.collect(
      amount={"amount": 45.00},    # decimal major units; GBP by 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 },     // decimal major units; GBP by 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
  ```
</CodeGroup>

## 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 (SCA) before money moves. In sandbox the recipient account decides the outcome — discover the
accounts with `atoa.sandboxTestAccounts()`.

<CodeGroup>
  ```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
  ```
</CodeGroup>

Money moved in sandbox. The `next_action` approval step applies to every send and every off-session charge — see
[Collect](/agent-pay/collect#sca-on-a-charge) and [Send](/agent-pay/send#sca-on-a-payout).

## Next

<CardGroup cols={2}>
  <Card title="AI agents & tools" icon="robot" href="/agent-pay/ai-agents">
    Hand the SDK to your model: the tool surface, the loop, and how a human approves.
  </Card>

  <Card title="Collect — money in" icon="money-bill-wave" href="/agent-pay/collect">
    Pay-links, off-session charges, SCA, refunds.
  </Card>

  <Card title="Send — money out" icon="money-bill-transfer" href="/agent-pay/send">
    Payouts, SCA, batches, contract limits.
  </Card>

  <Card title="Reference" icon="list" href="/agent-pay/reference">
    Auth, methods, types, errors, sandbox, go-live.
  </Card>
</CardGroup>

<Card title="Download the notebook · run it locally" icon="download" href="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.
</Card>
