---
title: "Deprecated: create plus an informational fresh quote"
method: POST
path: "/agent/swap/execute"
tags: ["agent", "swap"]
deprecated: true
---

# Deprecated: create plus an informational fresh quote

`POST /agent/swap/execute`

## Request

- Base URL: `https://xmrtousdc.xyz/api/v1`
- URL: `https://xmrtousdc.xyz/api/v1/agent/swap/execute`
- Auth: none declared. Required headers: `idempotency-key`

> **Deprecated.**

Deprecated; use /agent/swap/create. Same request (quote-bound, idempotency-key required) and response as create, plus an informational fresh data.quote. The quoteId governs, not the fresh quote.

## Headers

- `idempotency-key` string, required

## Request body

- AgentSwapRequest: Quote-bound create: the body can be just { "quoteId": "q_..." }. direction, chain, amount and to come from the stored quote; any that are sent must match it (409 quote_mismatch, details.mismatched). to is required only when the quote had none (400 destination_required). A quote opens at most one order (409 quote_already_used under another key).
  - `quoteId` string, required: data.quoteId from POST /agent/swap/quote. Required by default (400 quote_required).
  - `to` string: Destination, needed only when the quote had none. x2u: EVM address (0x + 40 hex; a mixed-case address must pass EIP-55; never the zero address or a token contract). u2x: Monero mainnet address (95 or 106 chars, checksum verified). Surrounding spaces are trimmed.
  - `direction` 'x2u' | 'u2x': Optional; must match the quote.
  - `chain` union: Optional; must match the quote.
    - string
    - integer
  - `amount` string: Optional; must equal the quote's data.amount.
  - `quoteIntentDigest` string: Optional: data.intentCommitment.digest from the quote (requires quoteId). Swapper compares it with the stored quote before creating a new order; a mismatch is 409 quote_intent_mismatch.
  - `sessionId` string, uuid: Optional grouping UUID. Usually omit it: the BFF derives one from the idempotency-key, and GET /agent/swap/orders?idempotencyKey= finds the order either way.

## Response `200`

Execute response (create response plus an informational data.quote)

- CreateResponse
  - `ok` boolean
  - `operation` string
  - `state` 'waiting_for_funding' | 'deposit_detected' | 'swap_processing' | 'refunding' | 'completed' | 'terminal_failure' | 'unknown' | 'queued': waiting_for_funding for a new order; a replayed or recovered create reports the live state. queued only on a 202 (QueuedCreateResponse): no order yet.
  - `terminal` boolean: Live value (false for a new order).
  - `outcome` 'completed' | 'refunded' | 'failed' | 'expired' | 'null', nullable
  - `replayed` boolean: true when this replays an earlier create under the same idempotency-key: state, terminal, outcome, next and payable are read from the live order.
  - `recovered` boolean: true when the order was found after an unknown provider outcome; live state as for replayed.
  - `data` object
    - `order` object: { orderId, status, depositAddress, depositAmount, depositTokenSymbol, depositChainId, expectedOutput, expiresAt, ... }
    - `origin` 'agent_quote' | 'other': agent_quote: created by this endpoint from a quote, with the provider's order verified against it. Only agent_quote orders can be payable or attested.
    - `destination` string, nullable: The payout address recorded with the order at create (what the provider was asked to pay). Show it to the user.
    - `payable` boolean: true only when deposit.payment may be paid: origin agent_quote, provider order verified, state waiting_for_funding, no deposit seen, no reconfirmation pending, expiry known and at least 600 s away. With payableReason upstream_mismatch, deposit is null.
    - `payableReason` 'null' | 'replay_check_status' | 'deposit_seen' | 'not_waiting' | 'expiry_unknown' | 'expired' | 'expiring' | 'reconfirm_required' | 'status_unavailable' | 'upstream_mismatch' | 'unverified_origin' | 'queued', nullable: Why payable is false (null while payable). queued: a 202 create waiting in the order line (no order exists yet). On create and status, the first failing check names it: status_unavailable (the live order could not be read, e.g. on a replay or recovery, or a long-poll ended on cached data; read the status again without waitSec), upstream_mismatch (the provider's order did not match the request; deposit is null), unverified_origin (the order was not created through this API from a quote: origin other, e.g. one made by the removed raw POST /orders route or one Swapper has no record of; never fund it, create your own order from a quote), deposit_seen (a deposit was already seen; never send again), not_waiting (the order no longer waits for funding), reconfirm_required (the provider's expected output differs materially from the quote; nothing clears it: next.action requote_and_create_new_order), expiry_unknown (the deposit expiry is unknown), expired (the deposit window has closed while the provider still shows the order waiting), expiring (less than 600 s to expiry). On lists: replay_check_status (a list never offers a payment; read the order's status).
    - `evidence` Evidence, nullable: Transaction hashes the provider reports (source: provider): its statements, not chain checks by Swapper; verify them on an explorer. null when nothing is known. A completed order with payout null has no payout transaction reported yet (data.attention payout_unconfirmed): check the destination wallet before treating the funds as received.
      - `source` 'provider'
      - `deposit` object, nullable
        - `txHash` string
        - `explorerUrl` string, nullable
      - `payout` object, nullable
        - `txHash` string
        - `explorerUrl` string, nullable: Arbitrum One: arbiscan.io; Monero: xmrchain.net.
        - `asset` string, nullable: The payout token contract (lowercase) or XMR.
        - `symbol` string, nullable
        - `atomic` string, nullable
        - `decimals` integer, nullable
        - `deliveredAsDifferentToken` boolean: true when the provider reports the payout in another token than requested; amounts are in that token's units.
      - `refund` object, nullable
        - `txHash` string
        - `explorerUrl` string, nullable
    - `received` string, nullable: The payout the provider reports, in the payout asset's own decimals (human units); null until reported or when the unit is unknown.
    - `receivedDetail` ReceivedDetail, nullable: The payout as the provider reports it, in the payout asset's own decimals (the delivered token's when the provider delivered a different token). null until the provider reports an output.
      - `atomic` string
      - `decimals` integer, nullable
      - `asset` string, nullable
      - `symbol` string, nullable
      - `chainId` integer, nullable
      - `deliveredAsDifferentToken` boolean
      - `source` 'provider'
    - `receivedBelowMinimum` boolean, nullable: Whether the reported payout is below the quote's published minimum; null without a published minimum (always on Wagyu v2).
    - `attention` object[], nullable: What the user must know or do that the state alone does not say, in Swapper's words (never provider text); null when nothing. Show each note. Extra fields by kind: deposit_below_expected (observedDepositAtomic, expectedDepositAtomic), deposit_below_minimum (observedDepositAtomic, minimumDepositAtomic), action_required (action: refund_address | payout_address | coinflip, how: contact_support; Swapper cannot submit it), refund_recovery (recovery), delivered_different_token (deliveredToken).
      - `kind` 'deposit_below_expected' | 'deposit_below_minimum' | 'action_required' | 'refund_pending' | 'refund_recovery' | 'provider_review' | 'delivered_different_token' | 'coinflip_lost' | 'payout_unconfirmed' | 'refund_unconfirmed', required
      - `note` string, required
      - `action` string, nullable
      - `how` 'contact_support'
    - `trackUrl` string, nullable: <public origin>/#order=<orderId>: the order in the swap app.
    - `sessionId` string, uuid
    - `idempotencyKey` string, nullable
    - `idempotentReplay` boolean
    - `quoteId` string, nullable: The quote the order was created from (on a replay, the original quote, never the request's).
    - `quoteBound` boolean: true when the request's quoteId is the order's original quote; false on a replay whose request named another one.
    - `intentCommitment` object, nullable: The original quote's commitment.
    - `intentDigestChecked` boolean: true only when a quoteIntentDigest was checked against the original quote.
    - `reconfirmRequired` boolean: true when the provider's expected output differs materially from the quote; payable is then false for good (payableReason reconfirm_required, next.action requote_and_create_new_order).
    - `reconfirmReason` 'expected_output_below_confirmed_minimum' | 'expected_output_below_confirmed_estimate' | 'expected_output_unknown' | 'null', nullable: below_confirmed_minimum: under the published minimum the user confirmed; below_confirmed_estimate: no published minimum and more than RECONFIRM_MAX_SHORTFALL_BPS (default 2%) under the confirmed estimate; unknown: the provider gave no expected output.
    - `deposit` Deposit, nullable
      - `address` string, nullable
      - `amount` string, nullable: Exact amount to send, human units.
      - `amountAtomic` string, nullable
      - `decimals` integer, nullable
      - `tokenSymbol` string, nullable
      - `chainId` integer, nullable: 0 = Monero, 42161 = Arbitrum One.
      - `chainName` string, nullable
      - `expiresAt` string, date-time, nullable: Canonical ISO-8601 UTC: the earlier of the expiry recorded at create and the provider's live one.
      - `expiresInSec` integer, nullable
      - `exactAmountRequired` boolean
      - `confirmations` object: { required, current, source: provider, note } as the provider reports them (progress, not a guarantee); nulls when it reports none.
      - `afterExpiry` string
      - `refunds` string
      - `payment` union: Wallet-ready transfer of exactly this deposit. Non-null only while the response's payable is true; null otherwise (see payableReason).
        - MoneroPayment
          - `kind` 'monero', required
          - `uri` string, required: monero:<address>?tx_amount=<amount> (exact human decimal, never rounded).
          - `address` string, required
          - `amount` string, required
          - `gas` PaymentGas
            - `symbol` string
            - `chain` string
            - `note` string
          - `signing` string
        - Erc20TransferPayment
          - `kind` 'erc20_transfer', required
          - `chainId` 42161, required
          - `token` string, required: Native USDC contract on Arbitrum One.
          - `to` string, required: The deposit address.
          - `amountAtomic` string, required: Exact deposit in atomic units (USDC: 6 decimals).
          - `eip681` string, required: ethereum:<token>@42161/transfer?address=<to>&uint256=<amountAtomic>
          - `tx` object, required: Unsigned transaction request to the token contract, ready for a wallet: data = 0xa9059cbb + recipient (lowercase, left-padded to 32 bytes) + amount (hex, left-padded to 64 chars); chainId 42161. Check it before signing (MCP get_swap_status payment -> paymentCheck).
            - `to` string
            - `value` '0x0'
            - `data` string
            - `chainId` 42161
          - `gas` PaymentGas
            - `symbol` string
            - `chain` string
            - `note` string
          - `signing` string: Only the sender's wallet signs and sends it; Swapper never signs or holds keys.
      - `attestation` OrderAttestation, nullable: swapper.order-attestation.v1 envelope (docs/TRANSPARENCY.md): Swapper's Ed25519 signature over this order's deposit instructions and payout address. Present only for orders created through create from a quote (origin agent_quote) whose provider order matched the request, and only when signing is configured (capabilities.attestation.status active); signed once and returned unchanged on replays and status; null otherwise. Verify it against a key set you obtained yourself (keys/attestation-keys.json in the source): it shows what Swapper's API stated, not that the provider controls the deposit address.
        - `v` 'swapper.order-attestation.v1'
        - `kid` string
        - `claim` object: issuedAt, provider, orderId, quoteId, quoteDigest, direction, depositChainId, depositAsset, depositAddress, depositAtomic, depositExpiresAt, payoutChainId, payoutAsset, destination, expectedPayoutAtomic (strings or null).
        - `sig` string: Ed25519 signature, base64url.
    - `next` NextHint: What to do next. action: user_sends_deposit_then_poll_status (only while payable is true) | poll_status | poll_status_then_contact_support (state unknown) | contact_support (data.attention has action_required or coinflip_lost: the provider needs an action Swapper cannot take; open a support ticket with the orderId, never share seed phrases or private keys) | requote_and_create_new_order (reconfirm_required: this order can never be funded through Swapper; re-quote with requote.body and create a new order) | done (completed) | contact_support_if_unexpected (terminal failure).
      - `action` 'user_sends_deposit_then_poll_status' | 'poll_status' | 'poll_status_then_contact_support' | 'contact_support' | 'requote_and_create_new_order' | 'done' | 'contact_support_if_unexpected'
      - `statusPath` string, nullable
      - `statusUrl` string, nullable
      - `orderPath` string, nullable
      - `supportPath` string
      - `pollIntervalSec` integer, nullable: null when terminal (and for requote_and_create_new_order).
      - `maxPollIntervalSec` integer, nullable
      - `backoff` string, nullable
      - `recheckAfterSec` integer, nullable: Terminal but mayChange (failed/expired): re-check after this many seconds if funds were sent.
      - `terminal` boolean
      - `reason` string, nullable: requote_and_create_new_order only: the reconfirmReason.
      - `note` string: requote_and_create_new_order only: why the order cannot be funded.
      - `requote` object: requote_and_create_new_order only: { path: /api/v1/agent/swap/quote, body: { requoteOf: <the order's quoteId> } | null }.
    - `fromQueue` boolean: true on the first 200 after the create waited in the order line (earlier answers were 202 queued).
    - `fundingAuthorization` string: Creating an order does not move funds; funds move only when the sender's wallet (the user's, or an agent's acting for the user) sends the deposit, and only while payable is true.
    - `lastKnown` object, nullable: Present when a replay or recovery could not read the live order: { status, state, observedAt }; payable is then false.
    - `verification` object: Present when the provider's order does not match the quote-bound request: { ok: false, mismatched }.
    - `fundingInstructions` object, nullable: Legacy shape of the same deposit data; null unless payable.

## Other responses

- `202`: Queued: the provider's order limits are reached, so the create waits its turn in the order line. No order exists yet and nothing may be funded (data.payable false, data.payableReason queued). Send the same create again (same body and idempotency-key) after Retry-After / data.next.retryAfterSec: it answers 202 with the current place until Swapper has created the order on its turn, then 200 with the order and data.fromQueue true. Nothing is created twice. GET /agent/swap/queue?idempotencyKey= shows the same place.
- `400`: idempotency_key_required (no idempotency-key header), quote_required (no quoteId), destination_required (the quote had no destination and the body has no to; details.field = to), or invalid_request (details.field, details.reason; destination reasons as on quote, invalid_quote_id, or a malformed quoteIntentDigest).
- `404`: quote_not_found: quote unknown or too old; re-quote.
- `409`: quote_already_used: this quote already opened an order under another idempotency-key (details.orderId, null while that first create's outcome is still unknown; not retryable): check that order, or retry the first create with its own key, instead of creating another. idempotency_conflict: same key, different body (details.orderId and details.state of the existing order, disclosed only for the same destination or quote; otherwise details.reason key_owned_by_other_request: use a new random key): check it before creating another. idempotency_in_progress: retryable; retry shortly with the same key. idempotency_unresolved: the provider outcome stayed unknown and no order was found under this key; the key is closed (details.keyClosed), nothing under it can ever be funded: quote again and use a new key. quote_mismatch: the body differs from the quote (details.mismatched). quote_intent_mismatch: quoteIntentDigest differs from the stored quote's commitment. queue_requote_needed: the quote expired while this create waited in the order line and the new estimate is worse than the accepted tolerance (details.queueId, oldQuoteId, newQuoteId, newEstimate, acceptedEstimate, position, createBody; not retryable as is): create with details.createBody (the new quoteId, same idempotency-key) to keep the place in line, or stop.
- `410`: quote_expired: the quote's acceptance window passed; re-quote (requoteOf) and create from the new quote.
- `415`: unsupported_media_type: the body must be JSON (content-type: application/json).
- `422`: below_provider_minimum: the quote is below the provider minimum, or the provider refused the amount (details.minimumSwapUsd); quote a larger amount. no_route: the provider has no route for this swap right now (retryable, Retry-After); try another amount or later.
- `429`: Rate limited (honor Retry-After), or queue_full: the order line is full (details.length, maxLength, etaSec; retryable after Retry-After).
- `451`: geo_blocked: service unavailable in the caller's region.
- `502`: upstream_mismatch: the provider's order does not match the quote-bound request (details.orderId, details.mismatched). No deposit instructions are issued and the key never creates another order; do not fund anything; not retryable; contact support with the orderId. upstream_forbidden: the provider refused Swapper's request (a configuration problem, not the user's input; not retryable). Other upstream_* provider errors (details.upstreamCode, details.upstreamStatus): retry later with the same idempotency-key.
- `503`: order_outcome_unknown: the order may exist (also when the provider reported a conflict or a failed step after the request was sent); retry with the SAME idempotency-key until it resolves (details.resolvesAt; at most UNKNOWN_OUTCOME_RESOLVE_MS, default 10 min). No deposit instructions were issued under this key unless a previous response returned them (details.note); never fund an address you did not receive. state_unavailable: retryable. service_paused: new orders are paused (retryable); with details.reason provider_api_deprecated or provider_maintenance the provider refuses new orders on this API (not retryable); with details.orderId and details.reason deposit_address_reused, a safety check blocked this key's order (not retryable): do not fund it, contact support. queue_timeout: this create waited in the order line longer than the maximum wait (details.queueId, details.waitedSec; retryable): send the same create again to rejoin the line (a new quote may be needed).

---

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