---
title: "Submit a stamped Turnkey body for a non-custodial send intent (second phase of a wallet send)"
method: POST
path: "/intents/{intentId}/submit-stamp"
tags: ["intents"]
---

# Submit a stamped Turnkey body for a non-custodial send intent (second phase of a wallet send)

`POST /intents/{intentId}/submit-stamp`

## Request

- Base URL: `https://api.catena.com/v1/agent-api`
- URL: `https://api.catena.com/v1/agent-api/intents/{intentId}/submit-stamp`
- Auth: HTTP bearer

## Path parameters

- `intentId` string, required

## Request body

- union: Send either the prepared body with its stamp, or a stamp for every step of the next action, never both.
  - object
    - `preparedBody` object, required
      - `body` string, required
      - `bodyHash` string, required
      - `expiresAt` string, date-time, required
      - `prepareToken` string, required
    - `stamp` object, required
      - `publicKeyHex` string, required
      - `stampHeaderName` string, required
      - `stampHeaderValue` string, required
  - object
    - `stamps` object[], required
      - `stepId` string, required
      - `preparedBody` object, required
        - `body` string, required
        - `bodyHash` string, required
        - `expiresAt` string, date-time, required
        - `prepareToken` string, required
      - `stamp` object, required
        - `publicKeyHex` string, required
        - `stampHeaderName` string, required
        - `stampHeaderValue` string, required

## Response `200`

Stamp submitted. Wallet sends answer with the external status; payment intents answer with status `completed` plus `paymentCredential` — the credential to present to the seller (for x402, the PAYMENT-SIGNATURE header value to retry the 402 with). The `x402` object is a deprecated protocol-named copy of the same value. A payment from a linked bank account answers with status `executing`, `completed`, `failed` or `manual_review`.

- object
  - `intentId` string, required
  - `signingRequestId` string, required
  - `status` 'user_presence_required' | 'consensus_needed' | 'broadcast' | 'confirmed' | 'rejected' | 'manual_review' | 'failed' | 'completed' | 'authorized' | 'executing' | 'cancelled' | 'expired', required
  - `planId` string
  - `x402` object
    - `paymentSignature` string, required
  - `paymentCredential` object
    - `protocol` 'x402' | 'mpp', required: Payment protocol the credential belongs to
    - `value` string, required: Opaque credential to present to the seller; for x402, the PAYMENT-SIGNATURE header value
  - `intent` 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
    - `movement` object
      - `planId` string, required
      - `nextStep` 'wait' | 'done' | 'blocked', 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
    - `data` union, required
      - object
      - object: A completed policy proposal: the policy its approval created, and the counterparty each proposed reference became. A counterparty deleted since, or one the current policy does not list, is left out.
        - `policy` object, required
          - `id` string, required
          - `name` string, required
        - `counterparties` object[], required
          - `ref` string, required
          - `id` string, required
          - `name` string, required
          - `outcome` 'created' | 'reused', required
    - `metadata` object, required
      - `dataUrl` string, nullable, required

## Other responses

- `403`: Wallet-send intent does not belong to the calling agent
- `404`: Wallet-send intent or prepared row not found
- `409`: Wallet-send is no longer prepared, prepared body does not match, or Catena has made its network temporarily unavailable (`onchain_network_closed`, with no `Retry-After`). On a closed network nothing was submitted and no money moved, and a wallet send's intent failed with its hold released. Read the intent before creating a new one on a network named in `details.alternatives`. 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`
- `503`: Non-custodial wallet sending is not enabled, or sending is temporarily unavailable (`temporarily_unavailable`, with the service in `details`). Nothing was submitted and no money moved. The intent may have failed with its hold released; read it, and create a new intent once sending is available again. A submit an earlier attempt already claimed answers 409 instead

---

[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)
