DEV Community

CAI
CAI

Posted on

Payment Intents and Hosted Actions: The Checkout Flow Between Intent and Settlement

Payment Intents and Hosted Actions: The Checkout Flow Between Intent and Settlement

A SaaS developer adds checkout to their product. A customer clicks "buy." The payment clears. The developer has never written a line of crypto code, and the customer never connected a wallet. This is the design target for the @cai.com checkout flow. The API behind it is a three-layer system: a payment intent that tracks state, a hosted action that replaces wallet signatures with a browser click, and a polling loop that turns that click into settlement.

The Intent

Every payment starts with an intent. The GET /payment-intent-status endpoint is the read side of a state machine. You pass an intent id, and the response tells you where the payment is in its lifecycle. The intent model is partial-live in the current API, the system honors gap_id fields in responses and evolves as checkout scenarios expand, but the state transitions are already stable enough to build production flows around.

GET /payment-intent-status?id=int_abc123
Enter fullscreen mode Exit fullscreen mode

Returns one of: pending, awaiting_user_confirmation, confirmed, completed, or failed. The transition from pending to awaiting_user_confirmation happens when the system receives a valid payment creation that needs the user to authorize it. The transition from confirmed to completed happens after the on-chain settlement reaches the required confirmation depth.

This state machine is what separates a checkout flow from a raw transfer. A raw transfer (POST /wallet-custodial-transfer) is fire-and-forget. You either get a tx hash or an error. An intent allows the system to hold state at each step, pause for user action, and resume automatically.

The Hosted Action

The hosted action link is the mechanism that bridges the gap between an API-driven agent and a human user who needs to confirm a payment. The endpoint is POST /create-hosted-action with a structured JSON body. The critical field is action_type. For payments, you pass deposit or a specific payment-related action type.

POST /create-hosted-action
Content-Type: application/json

{
  "action_type": "deposit",
  "amount_usd": 50,
  "chain": "ETH",
  "token": "usdc",
  "local_part": "alice"
}
Enter fullscreen mode Exit fullscreen mode

The response returns a URL:

{
  "ok": true,
  "url": "https://cai.com/act/a1b2c3d4e5f6",
  "expires_at": "2026-08-14T07:00:00Z"
}
Enter fullscreen mode Exit fullscreen mode

This URL serves a single-tap confirmation page. The user opens it, sees the amount, the destination, and a confirm button. No wallet extension. No gas fee in their browser. No seed phrase. They tap confirm, and the system moves the intent from awaiting_user_confirmation to confirmed.

The hosted action is the product insight that makes agent-initiated payments work in practice. An agent can create an intent, push a confirmation URL to the user's dashboard or email, and wait. The agent never handles the private key. The user never sees an opaque transaction request. Both sides of the trade get the abstraction they need.

The Polling Loop

Once the intent reaches confirmed, the agent polls for settlement. The polling endpoint is the same GET /payment-intent-status, now returning completed with a tx_hash.

GET /payment-intent-status?id=int_abc123

{
  "ok": true,
  "id": "int_abc123",
  "status": "completed",
  "tx_hash": "0x7a8b9c0d1e2f...",
  "chain": "ETH",
  "confirmations": 12,
  "settled_at": "2026-08-14T06:32:00Z"
}
Enter fullscreen mode Exit fullscreen mode

The polling cadence depends on the chain. EVM chains settle in seconds to a couple of minutes. BSC and Polygon are faster. Tron is comparable to EVM. The intent response includes confirmations and a minConfirmations threshold so the agent knows when settlement is considered final.

For agents that need asynchronous notification instead of polling, the POST /transfer-notify-register endpoint accepts an optional webhook_url parameter. When the transfer settles, the system calls the webhook with the tx hash and confirmation count. This is the right pattern for high-volume checkout flows where polling every 5 seconds on 10,000 active intents would be wasteful.

The Full Flow

Putting the three layers together, an end-to-end checkout looks like this:

  1. The merchant's backend calls POST /wallet-custodial-transfer (or creates a marketplace_order) to initiate payment. The response includes an intent id.

  2. The backend creates a hosted action link via POST /create-hosted-action and returns the URL to the merchant's frontend.

  3. The merchant redirects the customer to the hosted action page. The customer sees the amount, the merchant name, and a single confirm button.

  4. The customer confirms. The backend receives a callback (or the frontend polls the intent status) showing confirmed.

  5. The backend starts polling GET /payment-intent-status for the intent id, or waits for the webhook via POST /transfer-notify-register.

  6. The intent transitions to completed. The backend records the tx hash and updates the order status.

The remarkable property of this flow is that step 3 never shows the customer a blockchain address, a gas fee estimate, or a transaction signing request. The checkout page looks exactly like a credit card checkout, except the settlement happens on-chain in stablecoins.

Guardrails Built Into the Flow

The hosted action flow includes two safety mechanisms that are important for production deployments.

First, the daily auto-limit is capped at $200 USD per account for agent-initiated payments. This limit applies at the API key level and resets daily. It prevents a compromised key from draining an account. Users who need higher limits can adjust them in the dashboard settings at cai.com/app.

Second, new recipients trigger a confirmation requirement. When the agent tries to send to an address or email that has never received a payment from this account before, the hosted action page shows a prominent warning about the new recipient. The user must explicitly confirm before the payment proceeds. This prevents a common attack pattern where an attacker substitutes their own address in a payment request.

The vault product (credential storage and rotation) covers the case where the agent's API key itself is compromised, but that is a separate track from the payment flow.

What This Enables

The intent-plus-hosted-action pattern is the foundation for several checkout models:

SaaS checkout. A subscription service generates a hosted action URL for each billing cycle. The user confirms once per period. No saved payment methods, no recurring charge disputes, no PCI scope.

Agent-to-agent settlement. Agent A needs to pay Agent B for a completed task. Agent A creates an intent and a hosted action link, which the user confirms. Both agents receive the settlement notification.

One-time storefront purchases. A digital goods storefront generates a hosted action link at checkout. The customer confirms in one tap. The store receives the payment in USDC within seconds.

Each of these models relies on the same three-layer architecture: create the intent, confirm via hosted action, poll until settlement. The abstraction is consistent whether the buyer is a human clicking a link or an agent calling an API.

The Polling Implementation

A practical polling implementation in Python looks like this:

import time, requests

def poll_payment_intent(intent_id, interval=5, timeout=300):
    deadline = time.time() + timeout
    while time.time() < deadline:
        resp = requests.get(
            f"https://api.cai.com/functions/v1/payment-intent-status?id={intent_id}",
            headers={"Authorization": f"Bearer {CAI_API_KEY}"}
        )
        data = resp.json()
        if data.get("status") in ("completed", "failed"):
            return data
        time.sleep(interval)
    raise TimeoutError(f"intent {intent_id} did not settle in {timeout}s")
Enter fullscreen mode Exit fullscreen mode

For production, replace the synchronous loop with an async version or use the webhook path. The synchronous version is fine for low-volume checkout flows where the merchant processes a few hundred payments per day.

Status-Code Handling

Every endpoint in the flow follows the same error contract as the rest of the CAI API. 401 means the API key is invalid or expired. 403 means the key has insufficient scope (checkouts require pay or full scope). 429 means rate limiting. Back off and retry with exponential delay.

The intent-specific error codes include a gap_id field when the requested feature is still partial-live. Treat a gap_id response the same way you treat a 503: the feature exists but has limitations. Check the capabilities page at cai.com/capabilities for the current status of each feature.

Summary

The checkout flow built on payment intents and hosted actions solves a specific problem: how does an agent initiate a payment and get confirmation from a human user without sharing private keys, without asking the user to install a wallet, and without requiring the merchant to hold crypto? The answer is a stateful intent, a single-tap confirmation page, and a polling loop. The settlement happens in USDC on chain, but neither the buyer nor the seller touches the blockchain directly.

Documentation: cai.com/skill.md ยท cai.com/developers.html ยท cai.com/capabilities.html


If You Tried the Checkout Flow and Hit a Bug

Comment below with:

  1. What you ran the curl command, the create-hosted-action request, the intent id you polled.
  2. What you expected the checkout to complete in one line, or the intent to transition to completed.
  3. What you got the error message, the unexpected status, the timeout.
  4. Your environment OS, Node or Python version, the MCP host (OpenClaw, Hermes, Codex, Cursor, or other), the CAI account tier.

Every comment on this article gets read. Bug reports will be replied to within 24 hours. Friction points shape what we document next.

Top comments (0)