Payment Flow

How payments move through Axon: from bot-signed intent to on-chain execution across three response paths.

Payment Flow

Every payment in Axon starts with a bot signing an EIP-712 intent and ends with an ERC-20 transfer from the vault. What happens in between depends on the transaction's risk profile. There are three paths: fast, AI scan, and human review.

Overview

Bot signs PaymentIntent (EIP-712)
  |
  v
POST /v1/payments (to Relayer API)
  |
  v
Relayer validates signature + deadline + bot whitelist
  |
  v
Simulation via eth_call (pre-flight check)
  |
  v
Policy Engine evaluates
  |
  +--> Fast Path ---------> executePayment() --> txHash (sync)
  |
  +--> AI Scan Path ------> 3-agent verification --> consensus? --> executePayment()
  |                                                       |
  |                                                       +--> no consensus --> Human Review
  |
  +--> Human Review Path --> Owner notified --> approve/reject (async)

Step-by-Step

1. Bot Signs Intent

The bot creates a PaymentIntent and signs it with its private key using EIP-712 typed structured data:

const intent = {
  bot: botAddress,
  to: recipientAddress,
  token: USDC_ADDRESS,
  amount: 50_000_000n,    // 50 USDC
  deadline: Math.floor(Date.now() / 1000) + 300, // 5 minutes
  ref: keccak256(toUtf8Bytes('invoice-001')),
};
 
const signature = await bot.signTypedData(domain, types, intent);

The bot's private key never leaves its environment. The signed intent is submitted to the relayer API.

2. Relayer Intake

The relayer receives the intent via POST /v1/payments and performs initial validation:

  • Signature format -- is it a valid ECDSA signature?
  • Deadline -- is the intent still within its validity window?
  • Bot whitelist -- is this bot registered and active in the target vault?
  • Idempotency -- has this idempotencyKey been seen before? If yes, return the original response.

If any check fails, the relayer returns an error immediately. No gas is spent.

3. Simulation

Before submitting anything on-chain, the relayer dry-runs the transaction via eth_call:

  • Catches expired deadlines
  • Catches invalid or already-used signatures
  • Catches insufficient vault balance
  • Catches destination contract reverts

If simulation fails, the relayer returns a descriptive error to the bot. No gas is spent.

4. Policy Engine

The relayer evaluates the intent against the bot's configured policies (read from the on-chain BotConfig):

  • Per-transaction limit -- does the amount exceed maxPerTxAmount?
  • Spending limits -- does this transaction push the bot over any of its SpendingLimit windows (e.g., daily, hourly)?
  • Velocity check -- has this bot's spending velocity spiked in the recent window?
  • Destination whitelist -- is the recipient in the vault's allowed destinations?

Based on the evaluation, the intent is routed to one of three paths.

Path 1: Fast Path

Trigger: Transaction is below all thresholds, no velocity anomaly, destination is whitelisted or no whitelist is configured.

Behavior: Synchronous. The relayer submits the transaction on-chain immediately.

Bot --> Relayer --> executePayment() --> txHash

Response:

{
  "requestId": "req_abc123",
  "status": "approved",
  "txHash": "0x...",
  "chainId": 8453
}

Timing: Typically under 2 seconds end-to-end (signature verification + simulation + on-chain confirmation on L2).

HTTP 402 flows must use the fast path. If a payment is destined for an HTTP 402 resource unlock, the bot needs a synchronous txHash before the resource is released. Owners should pre-whitelist trusted 402 destinations so these payments are never routed to AI scan or human review.

Path 2: AI Scan Path

Trigger: One or more of these conditions:

  • Single transaction amount exceeds the bot's aiTriggerThreshold
  • Bot's spending velocity in the recent window exceeds the configured velocity threshold
  • Bot has requireAiVerification set to true

Behavior: The relayer holds the transaction and sends it through the 3-agent AI verification system (Safety, Behavioral, Reasoning agents running in parallel).

Bot --> Relayer --> Policy Engine (threshold triggered)
                      |
                      v
                 AI Verification (3 agents, parallel)
                      |
                      +--> 2/3 consensus: APPROVE --> executePayment() --> txHash
                      |
                      +--> 2/3 consensus: REJECT --> rejected, bot notified
                      |
                      +--> No consensus --> routed to Human Review

If consensus is reached quickly (under ~30 seconds): the response is still synchronous. The bot gets a txHash or rejection in the same HTTP response.

{
  "requestId": "req_def456",
  "status": "approved",
  "txHash": "0x...",
  "chainId": 8453,
  "verification": {
    "triggered": true,
    "result": "approved",
    "agents": {
      "safety": "approve",
      "behavioral": "approve",
      "reasoning": "approve"
    },
    "latencyMs": 18200
  }
}

If no consensus is reached: the intent is routed to human review (Path 3).

Timing: Target p95 latency under 30 seconds for the AI scan itself.

Path 3: Human Review Path

Trigger: One of:

  • AI verification returned no consensus (e.g., 1 approve, 1 reject, 1 abstain)
  • The owner has configured manual review for this bot
  • An AI agent flagged a high-severity concern

Behavior: Asynchronous. The intent is placed in the owner's review queue. The owner is notified via push notification (PWA) and can approve or reject with a single tap.

Bot --> Relayer --> Policy Engine / AI Scan --> no consensus
                                                  |
                                                  v
                                          Human Review Queue
                                                  |
                                          Owner notified (push)
                                                  |
                                          Approve / Reject
                                                  |
                                                  v
                                          executePayment() or rejection

Immediate response to bot:

{
  "requestId": "req_ghi789",
  "status": "pending_review",
  "pollUrl": "/v1/payments/req_ghi789",
  "estimatedResolutionMs": 300000
}

The bot can poll for the payment status:

GET /v1/payments/req_ghi789

Timing: Depends on the owner's response time. The intent's deadline still applies -- if the owner does not act before the deadline expires, the intent becomes invalid and must be re-signed by the bot.

Payment Request and Response Format

Request

POST /v1/payments
{
  "bot": "0x...",
  "to": "0x...",
  "token": "0x...",
  "amount": "50000000",
  "deadline": "1752000000",
  "ref": "0x...",
  "signature": "0x...",
  "chainId": 8453,
  "vaultAddress": "0x...",
 
  "idempotencyKey": "550e8400-e29b-41d4-a716-446655440000",
  "memo": "Payment for 100 API calls",
  "resourceUrl": "https://api.example.com/v1/data"
}

Fields above the blank line (bot, to, token, amount, deadline, ref, signature, chainId, vaultAddress) are verified on-chain. Fields below (idempotencyKey, memo, resourceUrl) are stored in PostgreSQL only and never touch the blockchain.

The idempotencyKey is mandatory. If the same key is submitted twice, the relayer returns the original response without re-executing.

Status Check

GET /v1/payments/{requestId}
{
  "requestId": "req_ghi789",
  "status": "approved",
  "txHash": "0x...",
  "chainId": 8453,
  "resolvedAt": "2026-07-15T14:30:00Z"
}

Possible status values: "approved", "pending_review", "rejected".