Operations and errors
Handle approvals, settlement, receipts and uncertain outcomes
Token transfers and paid API calls return an Operation. Its id identifies the request; operationHash binds the reviewed terms; status describes its current state.
Read and wait
const operation = await client.operations.get(operationId)
const requests = await client.operations.list() // current credential's scope
const agentRequests = await client.operations.list(agentId)
const result = await client.operations.wait(operationId, {
timeoutMs: 120_000,
intervalMs: 1500,
signal: AbortSignal.timeout(120_000),
})wait polls while the operation is queued, submitting or submitted. It returns when approval is needed, the outcome is unknown, or the operation is terminal. It does not approve, sign again or resend a payment. Cancelling or timing out the polling request does not cancel the payment.
Statuses
| Status | What your application should do |
|---|---|
pending_approval | Show the owner approvalUrl; wait for their decision |
queued | Authorized and waiting for execution; track the same operation |
submitting | Signing/submission is in progress; do not create a replacement |
submitted | Transaction submitted; await reconciliation |
unknown | Outcome is unresolved; keep the original ID/key and inspect it |
confirmed | Read the settlement receipt and, for paid APIs, the HTTP response |
failed | Inspect the error and receipt; actual network fees may have been spent |
denied, rejected, expired | Read the reason; do not automatically request another payment |
Owner approval
In Ask mode, the normal path is to give the owner the dashboard approval link. Agent credentials cannot approve or reject their own requests.
An owner-facing application can use a separate owner client after the user reviews the exact operation:
const reviewed = await ownerClient.operations.get(operationId)
// Present the recipient, amount, fees and expiry to the owner first.
const approved = await ownerClient.operations.approve(reviewed.id, reviewed.operationHash)
// Or, when the owner chooses rejection:
// await ownerClient.operations.reject(reviewed.id, reviewed.operationHash)Use the hash of the operation the user actually reviewed. A policy change or expired approval requires a fresh review; do not silently approve a replacement request.
Receipts and responses
A settled operation can include:
transactionHashandreceipt.chainIdfor the transaction.receipt.blockNumberandreceipt.successfor settlement.receipt.feeAtomicwithreceipt.feeAtomicDecimalsfor the fee in native/protocol precision.receipt.feePaymentfor the fee asset, token precision and payer when applicable. Prefer itsamountAtomicanddecimalswhen displaying the token-denominated fee.usdReservedMicrosandusdSettledMicrosfor USD accounting.httpResponse.status,headersandbodyBase64for a paid API's response.errorfor a request or execution problem.
feeAtomic and feePayment.amountAtomic describe the same charge at potentially different precisions; do not add them together. For example, on Tempo, feeAtomic: "16000000000000" with feeAtomicDecimals: 18 and feePayment: { amountAtomic: "16", decimals: 6, asset: "erc20:…" } both represent a fee of 0.000016 fee tokens. Solana's protocol precision is 9 decimals. Older server responses may omit feeAtomicDecimals; never assume it equals the payment token's decimals.
A successful chain payment does not guarantee a successful provider response. Likewise, an HTTP timeout does not prove that no payment happened. Operation polling tracks Agentis execution, not a provider's asynchronous job; use the returned provider job ID or link according to that provider's API.
Idempotency and retry behavior
SDK payment methods require { idempotencyKey }. Generate a UUID once for each intended payment and persist it with the task before the first request. A separate new payment needs a new key, even if its amount and recipient are identical. Users do not need to invent these keys.
After a lost response, read the original operation or repeat the request using the same credential, key and exact terms. The backend returns the existing operation rather than authorizing a replacement. Reusing a key with different terms is rejected. Unknown submissions retain their budget reservation until reconciled.
The CLI generates and saves keys and supports explicit retries. MCP accepts an optional key: when omitted, each tool call gets a fresh server-generated UUID and is not deduplicated against other calls. Never automatically repeat a keyless MCP payment after a timeout or missing result.
Handle API errors
import { AgentisApiError } from '@agentis-hq/sdk'
try {
const operation = await client.operations.get(operationId)
} catch (error) {
if (error instanceof AgentisApiError) {
// error.status: HTTP status
// error.code: machine-readable Agentis error code
// error.message: explanation suitable for the request's caller
} else {
// Transport failure or timeout: inspect the original request before retrying.
}
}A missing permission, insufficient balance or policy denial requires the appropriate owner action, not another wallet or relaxed limits. Keep tokens, private approval links and payment credentials out of logs.
Card purchases use checkout IDs and card status methods, rather than crypto operation IDs.