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
Managed wallets use request, then execute. External wallets use approve, your own transfer, then confirm. An exported private key can sign transfers outside Conto. See custody modes before exporting a key. x402 and MPP service payments useconto.x402 and conto.mpp instead. Their preAuthorize()
returns a grant (grantId, grantSignature) that record() must send back, or fixed reasons
and reasonCodes when the payment is declined or held. A record without a valid grant is flagged
as a policy bypass and alerts the owner. See x402
payments and MPP
payments.
To buy from a store with a buyer’s card, use conto.purchases instead. Conto shops, checks out
and pays in the background, and the buyer reviews each purchase. See Purchases.
Platforms use admin.purchasing.allowances to inspect and revoke existing buyer-approved store
allowances. These methods do not grant spending authority. See store allowances for permissions and revocation boundaries.
payments.request()
Request authorization for a payment. This evaluates policies without executing.Parameters
You can include invoice details in
context.invoice. Supported fields are vendorId,
vendorAddress, id, hash, sourceUrl, payload, expectedAmount, currency, and
dueDate. These fields are included in relevant payment lifecycle webhooks.If an approval workflow matches any non-denied request, Conto opens the approval request
immediately and returns
REQUIRES_APPROVAL. A production agent’s first payment to a payee Conto
has not seen is held the same way. Keep the returned requestId and check its status before
taking another payment action.Response
Currency depends on chain: The
currency field reflects the stablecoin used on the wallet’s
blockchain. On Base, Ethereum, Arbitrum, Polygon, and Solana, payments use USDC. 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
The SDK sets a newidempotencyKey on each request() call and reuses it for its own retries. Pass your own key, stored with the business action, so a retry after a crash or restart returns the original request.
- Same
idempotencyKey+ same request payload: returns the original request using the same public response shape - 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() authenticates the SDK key, verifies request ownership, enforces rate limits,
and requires the payments:execute scope. Conto rechecks applicable spend limits and payment
controls before sending and prevents the same request from being executed twice.request expires after 5 minutes. Check expiresAt before you call execute(),
and request the payment again if it has passed.
If approval in Conto already executed the payment, execute() returns the
existing receipt with alreadyExecuted: true and sends nothing. Other states return a specific
error instead of a receipt:
Denials and approval holds include
reasons, for example
This payment exceeds a configured spending limit. See
payment reasons.
Parameters
Response
A
processing response means the payment was submitted and should be checked with
payments.status(requestId). Every successful execution includes a stable Conto transaction ID.
Check settlementMode before treating the hash or explorer link as evidence of live settlement.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_DENIEDandreasons - If processing: Throws
ContoErrorwith codePAYMENT_PROCESSING. Checkpayments.status(requestId) - If requires approval: Throws
ContoErrorwith codeREQUIRES_APPROVAL, therequestId, andreasons. Approval in Conto executes the payment. Afterwards, callpayments.execute(requestId)orpayments.status(requestId)for the receipt. Do not callpay()again for the same purchase.
Example
autoExecute flag
autoExecute: true requests authorization and executes an approved payment in one API call.
How it works
- If APPROVED +
autoExecute: true: Starts execution and returnsstatus: "PROCESSING". A public receipt with the stable transaction ID, settlement mode, and hash is included when execution has produced one - If DENIED or REQUIRES_APPROVAL:
autoExecuteis ignored, normal response returned - If execution has an ambiguous result: Returns
PROCESSINGwith the payment status URL inactionUrl. Check that URL before deciding whether any further action is safe. - If execution could not start: Returns
APPROVEDwith the execute URL inactionUrl. Nothing was sent - If payment controls decline it at execution: Returns
DENIEDwithreasons
Example
payments.status()
Check the status of a payment request.Response
Example: polling for confirmation
Use bounded status polling so a declined or expired request does not leave your application waiting indefinitely. Stop on terminal states, surface review or required actions, and retain the request ID if the deadline passes.Status reference
See payment statuses for each status and what to do next. Fordeclined and review_required, reasons says why, for example
This payee needs review before payments to it can proceed automatically.
External wallet approve and 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
conto.payments.approve(input) and
conto.payments.confirm(approvalId, { txHash, approvalToken }) for these endpoints.
approve accepts amount, senderAddress, recipientAddress, numeric chainId, and optional
recipientName, purpose, category, and context. It returns approved, decision, and
requiresHumanApproval. A successful approval also supplies approvalId, approvalToken, and
expiresAt. An early budget denial may have no ID. approvalId is the payment request ID, so pass
it to confirm and status. approvalRequestId, returned when a person must review the payment,
is a different ID for that review.
Only PROD agents can pay from mainnet wallets. If the agent is DEV or STAGING and chainId is
a mainnet, approve declines with NO_WALLET and the reason “This agent’s environment is DEV. Set
it to PROD to pay from a mainnet wallet.” No wallet is created. See the
go-live checklist.
For a production agent, the first approve to a payee Conto has not seen holds the payment for
counterparty review (requiresHumanApproval: true), the same as request. If Conto cannot record
that review, approve returns HTTP 503 with COUNTERPARTY_REVIEW_UNAVAILABLE and no approval.
Retry later.
An approval reserves the amount against the agent’s spend limits, budgets, velocity limits, and
per-counterparty limits, then Conto checks policy again before it returns a token. If that check fails, approve declines and
releases the reservation. Two approvals sent at once can both decline this way, so retry one. If
Conto cannot run the check, approve returns HTTP 503 POLICY_REVALIDATION_UNAVAILABLE. An
unconfirmed approval releases its reservation when the token expires.
Wallet approvals share the agent’s daily, weekly, and monthly policy allowance with live-card
spending. Reserving an approval rechecks hard period limits under the same agent lock used by
card reservations. Individual wallet limits remain additional restrictions, and synthetic test-card
spending does not reduce this shared allowance. See Spend limits.
When approve returns requiresHumanApproval: true, nothing is reserved until a person approves.
The final approval reserves the amount and checks policy again. If a spend limit or policy no longer
allows the payment, the approval is recorded but the payment is declined. Otherwise send the
transfer and call confirm without a token within 24 hours. status returns the deadline as
expiresAt. A lapsed approval releases its reservation once, and a late confirm returns
EXPIRED.
confirm counts a payment against the agent’s spend limits exactly once. A payment its approval
already reserved is not counted again. If the transfer fails onchain, Conto releases the amount it
counted.
Only send through your signer when approval is granted and the token has not expired. Tokens expire
after 10 minutes. Persist the approval ID and the transfer hash before calling confirm.
Confirmation returns accepted: true, confirmed: false, and status: "processing". Use
conto.payments.status(approvalId) to follow settlement. Neither helper retries automatically. A
confirmation conflict must be reconciled against the original transfer. Never send again to repair
confirmation.
Conto does not check an external wallet balance. If the wallet is short, the transfer reverts
onchain and confirmation records the payment as failed.
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.Connected-card credential delivery
Use the connected-card checkout guide to request a single-use credential, acknowledge delivery, report the outcome, and handle approvals or uncertain outcomes. Start with card setup.Checkout checkpoints
Useconto.checkout when your agent completes a checkout itself, such as a Link checkout. Call
begin() right before the payment method opens and confirm() right before the payment is
submitted, then report the outcome. When the agent’s rules require approval, begin() returns
AWAITING_APPROVAL and waitForApproval() waits for the owner’s decision. See
checkout checkpoints and Conto with Stripe Link.
checkout.getLinkCard() asks Conto first, and asks Stripe Link for a card only after Conto
approves the purchase. Stripe Link issues the card. See
Stripe Link cards for agents.
Next steps
Error handling
Handle payment errors
Examples
See complete integration examples
Controlled-payment test evidence
GET /api/sdk/test-report reads selected allowed, denied, and optional review payment
request IDs. reviewOutcome is approved (default), denied, or expired. Both
transactions:read and audit:read are required. Requests must belong to the authenticated agent
and organization. Control verification additionally requires the same recorded wallet and currency.
Explicitly select the same walletId when creating each exercise; unvalidated wallet IDs are not
stored on denial. Older denied requests without wallet attribution remain incomplete evidence.
The report includes safe numeric spending controls, reviewer decisions, settlement receipts, and
audit references. It distinguishes payment completion, control verification, audit evidence, and
file persistence. An omitted review is not_tested; simulated receipts do not prove onchain
settlement. The API never executes a payment or marks a report saved. Use conto test-report
for an owner-only JSON file, or the read-only MCP tool get_test_report to inspect the snapshot.
Deploy the endpoint before publishing CLI/MCP releases that call it. New sandbox SDK keys include
the audit read scope; existing keys do not gain it retroactively.