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

> ## Agent Instructions
> Sapiom Pay is for platforms whose agent shops for many end users. Each end user is a buyer with their own Sapiom key and saved cards.
> Use the buyer's key for every call made on that buyer's behalf. Never use one buyer's key for another user, and never give the agent the platform key.
> Agents call the hosted MCP tools at https://api.sapiom.ai/v1/mcp. Backends call the REST API at https://api.sapiom.ai/v1. Both take the key in an x-api-key header.
> Never ask a buyer for a card number, expiry date or CVV in chat. Buyers enter card details only on Sapiom's hosted pages.
> Follow the nextAction in each transaction result. Action links are secrets for the intended buyer only.
> Use one idempotencyKey per transaction. After a timeout, retry with the same key. Never start a new transaction to escape an uncertain outcome.
> Report an order as placed only when order.outcome is confirmed. If it is unknown, stop and hand off to a person.

# Authorize a transaction from a UCP checkout

> Send a merchant's UCP checkout and a buyer's saved card, then follow each nextAction until payment is ready.

A transaction ties one merchant checkout to one of the buyer's saved cards. Your agent sends the checkout, Sapiom opens it with the merchant and checks it against your rules, and each result says what to do next.

**Before you start:**

* Your agent is [connected with the buyer's key](https://docs.sapiom.ai/buyers/connect-agent.md).
* The buyer has a card that is ready to pay. See [Vault a card](https://docs.sapiom.ai/cards/vault-a-card.md).
* `sapiom_check_transaction_compatibility` returns `supported` for the merchant.
* Your [browser worker](https://docs.sapiom.ai/transactions/browser-pay.md) is ready to finish checkout.

## Where each step runs

| Step | Runs in | Uses |
| - | - | - |
| Request, read, update or cancel the transaction | Your agent | MCP tools, buyer key |
| Send approval links to the buyer | Your agent or app | Links from `nextAction` |
| Fill and submit checkout | Your browser worker | Browser Pay, buyer key |
| Refresh a checkout when asked | Your backend | REST, buyer key |

Your backend stores each buyer's key and passes it to the agent and the browser worker for that buyer's session.

## Request the transaction

```json theme={null}
{
  "name": "sapiom_request_transaction",
  "arguments": {
    "idempotencyKey": "order-7f3a",
    "merchantOrigin": "https://shop.example",
    "checkout": {
      "currency": "USD",
      "line_items": [
        { "id": "line-1", "item": { "id": "merchant-product-id" }, "quantity": 1 }
      ],
      "buyer": { "email": "jane@example.com" }
    },
    "paymentMethodId": "00000000-0000-4000-8000-000000000001"
  }
}
```

* `checkout` is a UCP checkout request. `item.id` is the merchant's product ID; the line `id` is your own label.
* `paymentMethodId` is the `id` of the buyer's chosen card.
* `idempotencyKey` is your own unique ID for this transaction. See below.

The result includes a `transactionId`. Save it: every later step uses it. All arguments are in the [MCP tools reference](https://docs.sapiom.ai/api-reference/mcp-tools.md#sapiom_request_transaction).

Calling Sapiom from your backend instead? See [Use REST instead](#use-rest-instead).

## Keep the same idempotency key

The key identifies this transaction. If a request times out or you lose the response, send the identical request with the same key to pick it back up. Don't switch to a new key to get past an uncertain result: that can buy twice.

## Follow nextAction

Every result has a `nextAction` object. Its `type` says what to do. Act on it, then read the transaction with `sapiom_get_transaction` to get the next one.

| `type` | What to do | Fields to use |
| - | - | - |
| `buyer_security_code` | Send the link to the buyer. They approve the transaction by re-entering their CVV. | `url`, `expiresAt` |
| `payment_ready` | Pass `continueUrl` and the `transactionId` to your browser worker. See [Pay in your browser](https://docs.sapiom.ai/transactions/browser-pay.md). | `continueUrl` |
| `payment_submitted` | The card was sent to the merchant's payment processor, which ends Sapiom's part. Stop polling, and never pay again for this transaction. | |
| `wait` | Wait, then read again. | `retryAfterSeconds` |
| `resend_request` | Send the original request again with the same key. | `retry.afterSeconds` |
| `update_checkout`, `revise` | The checkout needs a change. Fix it with `sapiom_update_transaction`. | `messages`, `reasons` |
| `merchant_escalation` | The merchant needs the buyer. Send them the link and messages. | `url`, `messages` |
| `complete_payment_method_setup` | Only when you passed `setupLinkId`: the buyer is still adding their card. Wait, then send the same request with the same key. | `retry.afterSeconds` |
| `allowance_verification` | Send the link to the buyer. | `url`, `expiresAt` |
| `approval` | An approval rule on your account applies. Send the link to the approver if there is one. | `url`, `expiresAt` |
| `reconcile_checkout` | Your backend refreshes the checkout. See [Confirm the order](https://docs.sapiom.ai/transactions/confirm-and-recover.md#refresh-the-checkout). | `restRoute` |
| `blocked`, `closed`, `none` | Stop and tell the buyer why. | `reason`, `messages` |

## Change or cancel a transaction

* **Change:** `sapiom_update_transaction` replaces the whole checkout. Send `transactionId`, the latest `version`, a new stable `idempotencyKey` for this change, and the full checkout. Changing items or the total can mean the buyer approves again.
* **Cancel:** `sapiom_cancel_transaction` with `transactionId` and an `idempotencyKey`. It cannot stop a checkout that is already completing, and it does not refund an order.

## Use REST instead

Your backend can run the same transaction with the buyer's key.

1. [Create a transaction](https://docs.sapiom.ai/api-reference/transactions/create-a-transaction.md). `estimate` is your expected total and is optional. Omit `paymentCardId` to use the buyer's default card.
   ```bash theme={null}
   curl -X POST https://api.sapiom.ai/v1/transactions \
     -H "x-api-key: $SAPIOM_BUYER_KEY" \
     -H "x-idempotency-key: order-7f3a" \
     -H "Content-Type: application/json" \
     -d '{
       "requestFacts": {
         "source": "ucp-checkout",
         "version": "v1",
         "sdk": {},
         "request": {
           "merchantOrigin": "https://shop.example",
           "estimate": { "amount": "248.00", "currency": "USD" }
         }
       },
       "funding": { "source": "external_card", "paymentCardId": "00000000-0000-4000-8000-000000000001" }
     }'
   ```
2. [Open the checkout](https://docs.sapiom.ai/api-reference/transactions/open-the-checkout.md) with `POST /v1/transactions/{id}/ucp/checkout`, sending the UCP `line_items` and `buyer`. If you sent `shipping` when you created the transaction, leave out `buyer`.
3. Read `nextAction.action` in each response. While it's `update`, [replace the checkout](https://docs.sapiom.ai/api-reference/transactions/replace-the-checkout.md) with `PUT` on the same path.

Both checkout calls take an `Idempotency-Key` header.

REST names some actions differently from the MCP tools:

| REST `nextAction.action` | MCP `nextAction.type` |
| - | - |
| `update` | `update_checkout` |
| `requires_action` | `buyer_security_code`, `approval`, `allowance_verification` or `merchant_escalation`, by `kind` |
| `hand_off` | `payment_ready` |
| `submitted` | `payment_submitted` |
| `refresh` | `reconcile_checkout` |
| `done` | `none` |

`wait`, `revise`, `blocked` and `closed` have the same names.


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