---
title: "Create an intent for a governed action"
method: POST
path: "/intents"
tags: ["intents"]
---

# Create an intent for a governed action

`POST /intents`

## Request

- Base URL: `https://api.catena.com/v1/agent-api`
- URL: `https://api.catena.com/v1/agent-api/intents`
- Auth: HTTP bearer

## Headers

- `Idempotency-Key` string

## Request body

- object
  - `action` union, required
    - object
      - `type` 'send', required
      - `accountId` string
      - `counterpartyRailId` string, required
      - `amount` object, required
        - `amount` string, required
        - `assetId` 'USD', required
      - `description` string: Internal note stored in Catena
      - `method` 'ach', required
      - `memo` string: Reference sent with the ACH payment. When the organization has set a default ACH memo, that memo is sent instead.
    - object
      - `type` 'send', required
      - `accountId` string
      - `counterpartyRailId` string, required
      - `amount` object, required
        - `amount` string, required
        - `assetId` 'USD', required
      - `description` string: Internal note stored in Catena
      - `method` 'wire', required
      - `memo` string: Reference sent with the wire payment
    - object
      - `type` 'send', required
      - `accountId` string
      - `counterpartyRailId` string, required
      - `amount` object, required
        - `amount` string, required
        - `assetId` 'USD', required
      - `description` string: Internal note stored in Catena
      - `method` 'on-chain', required
      - `memo` string: Deprecated for on-chain sends and not sent to the blockchain; use description for an internal note
      - `payRequestId` string: The payment request this send pays. Catena links the send to the request as it creates it, and refuses it while the request already has a payment on its way.
    - object
      - `type` 'transfer', required
      - `accountId` string, required
      - `toAccountId` string, required
      - `amount` object, required
        - `amount` string, required
        - `assetId` 'USD', required
      - `memo` string
      - `description` string
    - union
      - object
        - `type` 'create_counterparty', required
        - `name` string, required
        - `email` string, email
        - `rail` union, required
          - object
            - `type` 'bank', required
            - `bankName` string, required
            - `routingNumber` string, required
            - `accountNumber` string, required
            - `accountType` 'checking' | 'savings'
            - `address` string
            - `addressStreet` string, required
            - `addressCity` string, required
            - `addressState` string, required
            - `addressPostalCode` string, required
            - `addressCountry` string
          - object
            - `type` 'wallet', required
            - `walletAddress` string, required
            - `network` 'base' | 'arc' | 'tempo', required
            - `disabledNetworks` string[]
            - `asset` string
      - object
        - `type` 'create_counterparty', required
        - `name` string, required
        - `email` string, email, required
        - `rail` union
          - object
            - `type` 'bank', required
            - `bankName` string, required
            - `routingNumber` string, required
            - `accountNumber` string, required
            - `accountType` 'checking' | 'savings'
            - `address` string
            - `addressStreet` string, required
            - `addressCity` string, required
            - `addressState` string, required
            - `addressPostalCode` string, required
            - `addressCountry` string
          - object
            - `type` 'wallet', required
            - `walletAddress` string, required
            - `network` 'base' | 'arc' | 'tempo', required
            - `disabledNetworks` string[]
            - `asset` string
    - object
      - `type` 'request_counterparty_details', required
      - `counterpartyId` string, required
      - `methods` object, required
        - `bank` boolean, required
        - `wallet` boolean, required
    - object
      - `type` 'x402', required
      - `accountId` string, required
      - `paymentRequirements` object, required
        - `scheme` string, required
        - `network` unknown, required
        - `asset` string, required
        - `amount` string, required
        - `payTo` string, required
        - `maxTimeoutSeconds` integer, required
        - `extra` object
      - `resource` object
        - `url` string, required
        - `description` string
        - `mimeType` string
        - `serviceName` string
        - `tags` string[]
        - `iconUrl` string
      - `authorization` object
        - `from` string, required: A 20-byte hex address, either all-lowercase or spelled with a valid EIP-55 checksum.
        - `to` string, required: A 20-byte hex address, either all-lowercase or spelled with a valid EIP-55 checksum.
        - `value` string, required
        - `validAfter` string, required
        - `validBefore` string, required
        - `nonce` string, required
      - `signedOffer` union
        - object
          - `format` 'eip712', required
          - `acceptIndex` integer
          - `payload` object, required
            - `version` unknown, required
            - `resourceUrl` string, required
            - `scheme` string, required
            - `network` string, required
            - `asset` string, required
            - `payTo` string, required
            - `amount` string, required
            - `validUntil` integer
          - `signature` string, required
        - object
          - `format` 'jws', required
          - `acceptIndex` integer
          - `signature` string, required
    - object
      - `type` 'mpp', required
      - `accountId` string, required
      - `challenge` unknown, required
      - `resource` object
        - `url` string, required
        - `serviceName` string
    - object
      - `type` 'policy_override', required
      - `schemaVersion` unknown, required
      - `operations` union[], required
        - union
          - object
            - `type` 'increase_limit', required
            - `accountId` string, required
            - `capability` 'send' | 'transfer', required
            - `counterpartyId` string
            - `limitType` 'per_transaction_amount' | 'daily_amount' | 'weekly_amount' | 'monthly_amount', required
            - `newLimit` object, required
              - …
          - object
            - `type` 'increase_limit', required
            - `accountId` string, required
            - `capability` 'send' | 'transfer', required
            - `counterpartyId` string
            - `limitType` 'hourly_count' | 'daily_count', required
            - `newLimit` object, required
              - …
      - `durationSeconds` integer, required
      - `reason` string, required
    - object
      - `type` 'policy_override', required
      - `schemaVersion` unknown, required
      - `operations` union[], required
        - union
          - object
            - `type` 'increase_limit', required
            - `capability` 'send' | 'transfer', required
            - `limitType` 'per_transaction_amount' | 'daily_amount' | 'weekly_amount' | 'monthly_amount', required
            - `scope` union, required
              - …
            - `newLimit` object, required
              - …
          - object
            - `type` 'increase_limit', required
            - `capability` 'send' | 'transfer', required
            - `limitType` 'hourly_count' | 'daily_count', required
            - `scope` union, required
              - …
            - `newLimit` object, required
              - …
      - `durationSeconds` integer, required
      - `reason` string, required
    - object
      - `type` 'policy_proposal', required
      - `schemaVersion` unknown, required
      - `rationale` string, required
      - `proposedCounterparties` object[]
        - `ref` string, required
        - `name` string, required
        - `email` string, email
        - `rails` union[]
          - union
            - object
              - …
            - object
              - …
      - `policy` object, required
        - `name` string, required
        - `description` string
        - `counterpartyAccess` union, required
          - object
            - `mode` 'open', required
          - object
            - `mode` 'restricted', required
            - `counterparties` union[], required
              - …
        - `permissions` union[], required
          - union
            - object
              - …
            - object
              - …
            - object
              - …
            - object
              - …
            - object
              - …
            - object
              - …
        - `rules` object, required
          - `send` object[], required
            - `scope` object, required
              - …
            - `limit` object, required
              - …
            - `action` union, required
              - …
          - `transfer` object[], required
            - `scope` object, required
              - …
            - `limit` object, required
              - …
            - `action` union, required
              - …
        - `evidenceRequirements` object
          - `fields` object, required
          - `requiredByCapability` object, required
            - `send` string[]
            - `transfer` string[]
            - `create_counterparty` string[]
            - `request_counterparty_details` string[]
            - `policy_override` string[]
  - `evidence` object: Evidence for the action, keyed by the field keys the agent's policy requires for its capability: text, or `{ attachmentId }` for a file uploaded through POST /evidence-attachments. Leave it out, or send an empty object, when the policy requires none. x402 and mpp payments carry send evidence as text only, which a payment that resumes a pending approval or grant ignores. policy_proposal actions cannot carry evidence

## Response `200`

Intent created and evaluated. A `send` or wallet-source `transfer` from a non-custodial (Turnkey) account returns an additional `nextAction` object (a `submit_stamp` envelope) with the unsigned body to stamp and submit to POST /intents/{intentId}/submit-stamp. A `policy_proposal` needs no policy and always waits for a person to approve it; it carries `proposal.reason`, as GET /intents/{id} does, and replaces the agent's earlier proposal if one is still pending.

- object
  - `id` string, required
  - `accountId` string: Resolved source account, pinned when the intent was created. Omitted for actions without an account.
  - `type` 'send' | 'transfer' | 'create_counterparty' | 'request_counterparty_details' | 'x402' | 'mpp' | 'policy_override' | 'policy_proposal' | 'card_purchase', required
  - `status` 'pending' | 'processing' | 'completed' | 'blocked' | 'failed', required
  - `resolution` union, required: Exact terminal outcome, or null while the intent is nonterminal. Known values are completed, blocked, denied, expired, failed, and cancelled; clients must preserve future nonblank strings.
    - 'completed' | 'blocked' | 'denied' | 'expired' | 'failed' | 'cancelled'
    - string
  - `reasons` string[], required
  - `expiresAt` string, date-time, nullable, required
  - `replayed` boolean, required
  - `data` union, required
    - object
      - `counterparty` object, required
        - `id` string, required
        - `name` string, required
        - `email` string
        - `createdAt` string, required
        - `status` 'ready' | 'awaiting_details', required
        - `rails` union[], required
          - union
            - object
              - …
            - object
              - …
    - object
      - `transaction` object, required
        - `id` string, required
        - `type` 'operator-send' | 'operator-transfer' | 'operator-deposit' | 'operator-refund' | 'microdeposit', required
        - `action` string, required
        - `status` 'pending' | 'processing' | 'completed' | 'failed' | 'reversed', required
        - `movementStatus` 'initiated' | 'provider_pending' | 'processing' | 'completed' | 'failed' | 'reversed' | 'manual_review', required
        - `direction` 'incoming' | 'outgoing', required
        - `amount` object, required: USD amount. With tokenDetails, the settled recipient credit, or zero for a movement within the same account. Otherwise the recorded transaction amount.
          - `amount` string, required
          - `assetId` string, required
          - `asset_id` string, required
        - `fee` object, required: With tokenDetails, the net account debit beyond amount, in USD at par, including fees, conversion cost and customer-paid gas. A negative value is a net conversion credit. Otherwise the recorded Catena fee.
          - `amount` string, required
          - `assetId` string, required
          - `asset_id` string, required
        - `tokenDetails` object: Optional settled token detail for this account. Primary amounts are USD at par; balance holdings use the same asset vocabulary. Omitted when token provenance is unavailable or the organization has no held-asset entitlement.
          - `debited` object[], required: Tokens netted out of the viewed account, one entry per token and network
            - `asset` string, required: Token symbol in the same lower-case vocabulary as balance holdings.asset
            - `network` string, required: Network on which the token left or arrived
            - `amount` object, required: Net quantity of this token on this network, valued in USD at par
              - …
          - `credited` object[], required: Tokens netted into the viewed account, including conversion surplus, one entry per token and network
            - `asset` string, required: Token symbol in the same lower-case vocabulary as balance holdings.asset
            - `network` string, required: Network on which the token left or arrived
            - `amount` object, required: Net quantity of this token on this network, valued in USD at par
              - …
          - `paid` string: Destination token symbol, only when a payment converted a different funding token
        - `currency` string, required
        - `network` string
        - `counterpartyName` string
        - `method` 'ach' | 'wire' | 'on-chain'
        - `memo` string
        - `description` string
        - `txHash` string
        - `createdAt` string, date-time, required
        - `completedAt` string, date-time
    - object
      - `counterpartyId` string, required
      - `counterpartyName` string, required
      - `invite` unknown, required
  - `nextAction` object
    - `type` 'submit_stamp', required
    - `signingRequestId` string, required
    - `signer` object, required
      - `algorithm` 'p256', required
      - `publicKeyHex` string, required
    - `preparedBody` object, required
      - `body` string, required
      - `bodyHash` string, required
      - `expiresAt` string, date-time, required
      - `prepareToken` string, required
    - `movement` object
      - `planId` string, required
      - `signerAddress` string, required
      - `digests` string[], required
      - `instructions` object[], required
        - `kind` string, required
        - `network` string, required
        - `signerAddress` string, required
        - `digest` string, required
        - `payload` object, required
        - `validBefore` string, date-time, nullable, required
    - `steps` union[]
      - union
        - object
          - `id` string, required
          - `description` string, required
          - `preparedBody` object, required
            - `body` string, required
            - `bodyHash` string, required
            - `expiresAt` string, date-time, required
            - `prepareToken` string, required
          - `effects` union[], required
            - union
              - …
          - `encoding` 'turnkey_sign_raw_payloads', required
          - `payloads` object[], required
            - `instructionId` string, required
            - `legId` string, required
            - `network` string, required
            - `kind` 'evm_raw_transaction' | 'eip3009_transfer_authorization' | 'eip7702_delegation' | 'eip712_execution' | 'tempo_transaction', required
            - `digest` string, required
            - `payload` object, required
        - object
          - `id` string, required
          - `description` string, required
          - `preparedBody` object, required
            - `body` string, required
            - `bodyHash` string, required
            - `expiresAt` string, date-time, required
            - `prepareToken` string, required
          - `effects` union[], required
            - union
              - …
          - `encoding` 'turnkey_execute_swap', required
          - `swap` object, required
            - `instructionId` string, required
            - `legId` string, required
            - `network` string, required
            - `onchainNonce` string, nullable, required: The gas station nonce the swap is pinned to, as `station:<n>`. Null on a network that sponsors the swap natively, such as Tempo
            - `quoteId` string, required
            - `inputToken` string, required
            - `inputAmount` string, required
            - `outputToken` string, required
            - `quotedOutputAmount` string, required
            - `minOutputAmount` string, required
            - `quoteExpiresAt` string, date-time, required
    - `totals` object
      - `amount` string, required
      - `maxTotalDebit` string, required
      - `destination` object, required
        - `recipient` string, required
        - `network` string, required
        - `asset` string, required
        - `amount` string
      - `signerAddress` string, required
      - `destinations` object[]: Every location the payment arrives at, one per network and asset, when that is not just `destination`. Transfers between the organization's own accounts deliver each admitted asset separately on its source's network. Mixed-asset deliveries include the exact amount at each location; only locations that receive funds are listed.
        - `recipient` string, required
        - `network` string, required
        - `asset` string, required
        - `amount` string
      - `sponsoredFees` object[]: The crossing fee Catena restores to the signing wallet on each network a staged crossing lands on. Not part of `maxTotalDebit`, which counts only what the wallet pays.
        - `network` string, required
        - `amount` string, required
  - `proposal` object
    - `reason` string, nullable, required: Whether this policy proposal can go ahead, without naming the resource or its state. While `status` is pending, null means the proposal passes every check made before a person reviews it; wait on awaiting_resources (something it needs, such as an active account, is not ready yet) and temporarily_unavailable (the check could not finish; read again later), and resubmit on referenced_resource_unavailable (an account or counterparty it names cannot be used) and proposal_invalid (it cannot be approved as written). While it is pending, it does not reflect whether a counterparty the proposal would create matches an existing one or shares an existing one's name, or what a wallet name (ENS) resolves to; only the person reviewing it sees those. When `resolution` is blocked, it is the reason the approval attempt recorded. In every other state it is null, and `status` and `resolution` give the outcome.
  - `evidence` object: The evidence submitted with the intent, keyed by field key: text exactly as sent, or a file's details. Absent when the intent carries none
  - `metadata` object: Present while the intent waits for a person's approval: dataUrl is the console page where they decide it
    - `dataUrl` string, nullable, required

## Other responses

- `400`: Input invalid, including an accountless send when multiple source accounts are allowed (`policy_send_account_required`), a policy proposal that could never be approved as written (`policy_proposal_invalid`, with each problem in `details.issues`), or a policy proposal naming an account or counterparty it cannot use (`policy_proposal_reference_unavailable`, which does not say which)
- `403`: Account not accessible under this policy, an accountless send has no allowed source (`policy_send_account_unavailable`), or policy proposals, or evidence requirements for a proposal that carries them, are not enabled for the organization (`feature_disabled`)
- `404`: Agent or referenced counterparty resource not found. Payment recipient failures may include `details.requiredRail`, which can be submitted in a create-counterparty intent
- `409`: Intent conflicts, including idempotency-key reuse with changed request parameters, an inferred source that changed during submission (`policy_send_account_changed`), another payment from the same wallet still being approved (`movement_wallet_approval_open`; `details.heldUntil` is when the payment holding the wallet frees it, with the seconds until then in `Retry-After`, and is null when that cannot be told, as while that payment is being signed; wait until then, or a few seconds when it is null, then send the request again, on a new idempotency key when `details.intentCancelled` is true, because this request's intent was cancelled and its key answers with that cancelled intent, and on the same key when it is false, because the intent is still open (if it has been cancelled since, that resend returns it and nothing moved, so start again on a new key); `details.blockingIntentId` names the payment holding the wallet when it is one of your own intents, and cancelling it frees the wallet at once), a bank payment held back behind an earlier one for the same amount, because the payment provider matches deposits by amount (`movement_bank_payment_blocked`; nothing was created; `details.blocker` is `funding` while the earlier payment's deposit is on its way, `unsent` while it waits to be signed or approved, and `review` while an operator has to act on it; after `Retry-After` seconds send the request again on the same idempotency key, and on `review`, which has no `Retry-After`, stop and tell an operator; a payment of a different amount is not held back), a replacement quote whose Catena fee or conversion cost exceeds the original approval (`movement_requote_cost_increased`; `details.intentId` identifies the existing payment; `details.approved` and `details.quoted` each carry `catenaFee` and `conversionCost` dollar decimal strings; cancel the existing intent successfully before starting a new payment on a new idempotency key for policy and approval again; if cancellation fails, follow and reconcile the existing intent before another attempt), a retried send that now crosses networks while its earlier request is still open (`cross_chain_send_movement_required`; have the earlier request denied or let it expire, or have Catena support close it if it was already approved, then start the payment again on a new idempotency key), a network Catena has made temporarily unavailable (`onchain_network_closed`, with no `Retry-After`; the request changed nothing and payments already in progress complete; do not retry on that network, and use one named in `details.alternatives` instead), an existing payment details request with safe invitation state, or evidence naming an attachment that is not ready yet (`evidence_attachments_not_ready`; `details.fields` names its fields; send the same request again, on the same idempotency key, once the attachment's status is ready). Also refused when the agent's policy cannot be read (`policy_unreadable`) until an admin saves its full configuration or, for a temporary override, revokes it and grants a new one; nothing changed, and GET /policy shows it with `invalidReason`
- `422`: The request's `evidence` does not meet the agent's policy's evidence requirements for the action (`evidence_requirements_not_met`, with every problem in `details.issues`), or its attachments together hold more than one intent may (`evidence_attachments_too_large`, with the limit in `details.maxBytes`). The submitted values are never echoed. Nothing was created, so send the request again with corrected evidence, on the same idempotency key. A payment that needs a step the account's wallet cannot sign yet answers `movement_instruction_unsupported` instead: nothing moved, the intent is closed as failed, and its idempotency key answers with that intent
- `429`: Too many bank payments were requested for the organization (`rate_limited`). Each bank payment counts against a per-organization budget of 60 a minute by default, shared by its agents and members, with no daily cap; send the request again after the `Retry-After` seconds
- `503`: Non-custodial wallet sending is not enabled, or a service the request needs is temporarily unavailable (`movement_admission_paused`, or `temporarily_unavailable` with the service in `details`). The request changed nothing, and payments already in progress complete; send it again after the `Retry-After` seconds

---

[API](https://skmtc.dev/catena/apis/catena-agent-api.md) - [All operations](https://skmtc.dev/catena/apis/catena-agent-api/llms.txt) - [OpenAPI document](https://skmtc.dev/catena/apis/catena-agent-api/revisions/9b72e32d21f1?raw)
