Skip to main content
A payment request is an intent to spend. Authorization permits the next step. It does not prove funds moved. A transaction records execution. Retain the request ID before executing, then use it to reconcile the outcome.

The lifecycle

1

Request authorization

Conto approves the payment, sends it for review, or denies it. If it is denied, stop and show the reason.
2

Wait for review if needed

If a person must review it, keep the request ID. After they decide, read the request status again before you continue.
3

Execute

Once the request is approved, execute it through the managed wallet or send it from your own wallet.
4

Track the result

The payment moves to processing, then to completed or failed.
Use GET /api/sdk/payments/{requestId} or conto.payments.status(requestId) with the same agent key. Status reads require transactions:read. The status endpoint returns lowercase values, request returns uppercase values, and the external-wallet approve returns ready_to_send when approved. Do not compare them interchangeably. This table maps each response status to the status read right after it. What each status read means:

What proves settlement?

Execution returns receipt.transactionId and receipt.settlementMode. live means an onchain transfer, including testnet. test means simulated settlement, with no transaction hash, explorer URL, block number, or confirmation time. Status and transaction reads repeat settlementMode. unknown means Conto has no execution evidence for the request. completed alone does not prove funds moved onchain. Preserve the receipt and request ID. For an onchain flow, wait for transaction.status: "completed" and check the intended chain. A hash alone, an approval, or HTTP 200 is not confirmation.

Bounded polling

Call this helper after submission or an uncertain result. It only reads state. Any state other than processing, including review, expiry, and denial, returns control to your application. Also configure the SDK request timeout: an in-flight HTTP call can finish after the polling deadline.
If a read fails with 429, honor Retry-After and resume later with the same request ID. A polling deadline or read failure does not cancel the payment. See rate limits.

Recovery recipes

Execution timed out

  1. Read the original request ID. A timeout is not proof that execution failed.
  2. If there is a transaction, follow that transaction to completion or failure.
  3. If the state is unresolved, retain it and investigate. Do not automatically repeat execution, even if the authorization still reads approved.
  4. Create a new intent only after establishing the original outcome and confirming the business action still needs payment. Preserve the association between both intents.
A repeated execute never sends twice. After completion it returns the existing receipt with alreadyExecuted: true. While another attempt is in flight, it returns 409 PAYMENT_PROCESSING. Reconcile the original request before any other write. Durably persist your business action ID, idempotency key, Conto request ID, execution attempt, and receipt so a restarted worker can discover an earlier execution attempt.

A human must approve

Keep REQUIRES_APPROVAL requests pending in your application. An authorized reviewer decides in the configured approval workflow. For a managed wallet, approving the payment in Conto executes it. Check the same request ID after review: payments.status() shows the transaction, and payments.execute() returns the existing receipt with alreadyExecuted: true instead of sending again. Before approval, execute() returns 409 APPROVAL_PENDING. A declined payment returns 403 PAYMENT_DENIED with reasons.

External confirmation returns a conflict

Send through your external signer only after approval. Confirm using the request ID, final hash, and the approval token when one was issued. After human workflow approval, the token can be omitted. Repeated confirmation does not duplicate recorded spend but can return 409 ALREADY_CONFIRMED. Compare the returned txHash with your saved transfer and follow statusUrl. If the hashes differ, stop and investigate. Never send a second transfer to resolve a confirmation error.

The wallet has insufficient funds

Inspect the intended wallet’s address, network, currency, and available balance. Funding another wallet or chain does not repair this request. After funding, re-read the request and check expiry and prior execution evidence before you resume or request new authorization.

A webhook is missing or repeated

Reconcile with the payment status endpoint. Verify webhook signatures and persist accepted events before returning success. Handle duplicate or out-of-order delivery without creating a new payment. Let the current resource state decide your next action. Follow webhook delivery and deduplication.

Next steps

Use server integration examples to connect recovery to an authenticated application route, and exercise these paths with payment tests.