Payments API
The payments API allows agents to request authorization and execute stablecoin payments.Overview
The payment flow has two steps:- Request - Request authorization and policy evaluation
- Execute - Execute the approved payment onchain
autoExecute: true to request and execute in a single API call. You can also use the SDK convenience method pay() to do both in one call.
Choose The Right Flow
| Wallet model | Use this flow | What Conto can stop |
|---|---|---|
Managed (PRIVY, SPONGE) | request -> execute | Conto stays in the execution path and can block the spend |
External (EXTERNAL) | approve -> transfer -> confirm | Conto governs the Conto-routed flow, but cannot block a direct self-signed transfer outside Conto |
payments.request()
Request authorization for a payment. This evaluates policies without executing.Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
amount | number | Yes | Payment amount |
recipientAddress | string | Yes | Wallet address. 0x + 40 hex chars for EVM, or base58 (32-44 chars) for Solana. Validated via Zod schema. |
recipientName | string | No | Human-readable name |
purpose | string | No | Why this payment is needed |
category | string | No | Spending category |
context | object | No | Additional metadata |
walletId | string | No | Specific wallet to use |
urgency | string | No | LOW, NORMAL, HIGH, CRITICAL |
autoExecute | boolean | No | If true, automatically execute the payment when approved. Returns the transaction result directly instead of requiring a separate execute() call. |
targetContractAddress | string | No | Smart contract address for contract interaction policy evaluation |
functionSelector | string | No | 4-byte function selector (e.g., 0xa9059cbb) for contract allowlist rules |
idempotencyKey | string | No | Client-supplied key that makes retries safe. Reusing the same key with the same request returns the original request; reusing it with different request parameters returns a conflict. |
External wallet users: If your agent controls its own wallet keys and uses the
/api/sdk/payments/approve endpoint instead, chainId is a required parameter. See the Agent
Skills guide (OpenClaw and Hermes) for the external-wallet flow.If you include
context.invoice, Conto preserves that payload in the raw request context and also
stores a typed invoice record on the PaymentRequest. Supported invoice fields are vendorId,
vendorAddress, id, hash, sourceUrl, payload, expectedAmount, currency, and
dueDate. Delta verification workflows and payment.executed webhook payloads prefer this typed
invoice record for new requests. For a full setup and live test example, see Delta Verification
Setup and Delta Smoke Test.Delta access is currently enabled by Conto during onboarding. If you want to use this flow,
contact sales@conto.finance.
If an approval workflow matches any non-denied request, Conto opens the approval request
immediately and includes
approvalRequestId in the API response. This includes workflows that
intentionally gate otherwise policy-approved payments, such as the Delta pilot workflow.External Wallet Approve/Confirm Flow
If your agent holds the signing keys, use the external-wallet flow instead ofpayments.execute():
- Call
POST /api/sdk/payments/approveto evaluate policy and receive anapprovalToken - Submit the onchain transfer through your own signer or wallet integration
- Call
POST /api/sdk/payments/{requestId}/confirmwith the finaltxHash, plus theapprovalTokenwhen the original/approveresponse returned one
Confirmation atomically claims an approved external-wallet payment before creating the transaction
record and updating spend tracking. A repeated confirmation for the same request returns a
conflict instead of creating duplicate ledger effects.
If the
senderAddress has not been seen before, Conto auto-creates an external wallet record for
that address and links it to the agent’s organization. That record still counts toward the
organization’s wallet limit, so approval can fail with WALLET_LIMIT_REACHED when the org is
already at capacity.The external-wallet
approve flow accepts the same optional context.invoice object as
payments.request() and stores the same typed invoice fields on the resulting PaymentRequest.If an approval workflow matches,
/api/sdk/payments/approve returns requiresHumanApproval: true
and includes approvalRequestId in the response instead of issuing an approvalToken. After that
workflow approves the payment, POST /api/sdk/payments/{requestId}/confirm can be called with
just the final txHash.Response
code values when Conto can
classify the stop reason, such as BUDGET_EXCEEDED for budget denials or VERIFICATION_REQUIRED
for merchant identity step-up.
All payment responses now include
currency (e.g., “USDC”, “USDT”, “USDC.e”, or “pathUSD”) and
chain (with chainId, chainName, chainType, and explorerUrl) so agents know exactly which network
and token the payment uses.Currency depends on chain: The
currency field reflects the stablecoin used on the wallet’s
blockchain. On Base, Ethereum, Arbitrum, and Polygon, both USDC and USDT are supported. On
Tempo Testnet, it is pathUSD. On Tempo Mainnet, it is USDC.e. The amount is always denominated
in the stablecoin configured for the payment.Idempotent Retries
UseidempotencyKey when your caller may retry the same payment request because of network timeouts or uncertain client state.
- Same
idempotencyKey+ same request payload: returns the originalrequestIdwithidempotent: true - Same
idempotencyKey+ different request payload: returns HTTP409withcode: "IDEMPOTENCY_CONFLICT" - Different
idempotencyKey: creates a new payment request
Example
payments.execute()
Execute an approved payment request.payments.execute() now delegates execution to Conto’s shared payment execution service. The
route still authenticates the SDK key, checks ownership, enforces rate limits, and requires
payments:execute; custody dispatch, claim locking, spend-limit revalidation, retry accounting,
provisional ledger effects, and confirmation-job dispatch all happen in the shared service used by
Conto Pay and dashboard approval flows.Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
requestId | string | Yes | The requestId from payment request |
Response
Sandbox organizations always execute in simulated mode: the policy check, receipt, spend
tracking, and audit log are real, no onchain transfer happens, and the response is finalized
immediately with
status: 'COMPLETED' and simulated: true. Real organizations execute onchain
and return status: 'CONFIRMING' with simulated: false while chain confirmation is tracked in
the background.Example
payments.pay()
Convenience method that requests and executes in one call.Behavior
- If approved: Executes immediately and returns result
- If denied: Throws
ContoErrorwith codePAYMENT_DENIED - If requires approval: Throws
ContoErrorwith codeREQUIRES_APPROVAL
Example
autoExecute Flag
TheautoExecute flag lets you request authorization and execute the payment in a single API call, without needing a separate execute() call.
How It Works
- If APPROVED +
autoExecute: true: Executes immediately, returnsstatus: "EXECUTED"withexecutionobject containingtxHash - If DENIED or REQUIRES_APPROVAL:
autoExecuteis ignored, normal response returned - If execution fails: Returns
status: "APPROVED"withautoExecuteErrormessage andexecuteUrlfor manual retry
Example
payments.status()
Check the status of a payment request.Response
Example: Polling for Confirmation
Status Reference
Payment requests and transactions use different status values:| Payment Request Status | Description |
|---|---|
APPROVED | Policies passed, ready to execute |
DENIED | Blocked by a policy or wallet limit |
REQUIRES_APPROVAL | Needs manual human approval |
EXECUTED | Auto-executed successfully (when autoExecute: true) |
PENDING | Awaiting human approval (maps to REQUIRES_APPROVAL in SDK response) |
COMPLETED | Payment fully confirmed onchain |
EXPIRED | Approval window elapsed without execution |
| Transaction Status | Description |
|---|---|
PENDING | Transaction submitted, awaiting confirmation |
EXECUTING | Transaction is being processed |
CONFIRMING | Transaction broadcast, awaiting block confirmation |
CONFIRMED | Transaction confirmed onchain |
FAILED | Transaction failed |
REJECTED | Transaction rejected |
CANCELLED | Transaction cancelled before execution |
| Policy Result | Description |
|---|---|
ALLOWED | All policies passed |
DENIED | At least one policy denied the payment |
REQUIRES_APPROVAL | Policies passed but approval threshold triggered |
FLAGGED | Payment flagged for review |
PENDING | Policy evaluation not yet complete |
Categories
Use standard categories for better analytics:| Category | Description |
|---|---|
INFRASTRUCTURE | Cloud, hosting, compute |
AI_SERVICES | AI APIs, model training |
MARKETING | Advertising, promotions |
OPERATIONS | General operations |
VENDOR | Vendor payments |
EMPLOYEE | Employee reimbursements |
TESTING | Test transactions |
Urgency Levels
| Level | Description |
|---|---|
LOW | Can wait, batch if possible |
NORMAL | Standard priority (default) |
HIGH | Process quickly |
CRITICAL | Immediate processing |
Best Practices
Always Include Purpose
Always Include Purpose
Including purpose improves audit trails and analytics:
Handle Expiration
Handle Expiration
Approvals from
/request expire after 5 minutes. Approvals from /approve (external wallets) expire after 10 minutes. Check expiration before executing:Use Two-Step for Complex Flows
Use Two-Step for Complex Flows
Use separate request/execute when you need to:
- Validate before executing
- Show user confirmation
- Handle requires_approval status
Add Context for Debugging
Add Context for Debugging
Use the context field for debugging info:
Next Steps
Error Handling
Handle payment errors gracefully
Examples
See complete integration examples