agent
swap

Create the order for a quote and return deposit instructions

Quote-bound: the body can be just { quoteId } (plus to when the quote had none); direction, chain, amount and to come from the stored quote, and any that are sent must match it (409 quote_mismatch). Each quote opens at most one order: the same quoteId under another idempotency-key returns 409 quote_already_used. The idempotency-key header is required. Returns state "waiting_for_funding" for a new order, data.order.orderId, data.origin (agent_quote), data.destination (the payout address recorded with the order), data.payable and data.payableReason, data.deposit {address, amount (exact human units), amountAtomic, tokenSymbol, chainId, expiresAt, expiresInSec, confirmations, payment, attestation}, data.evidence, data.attention, data.trackUrl, data.next {action, statusPath, statusUrl (absolute), pollIntervalSec}, data.sessionId and data.reconfirmRequired (true when the provider's expected output differs materially from the quote: below the quoted minimum, or, with no published minimum, more than RECONFIRM_MAX_SHORTFALL_BPS (default 2%) below the quoted estimate, or unknown; data.reconfirmReason says which). Nothing clears it: payable stays false and data.next.action is requote_and_create_new_order (data.next.requote.body {requoteOf}). data.deposit.payment is a wallet-ready transfer of exactly this deposit (Monero URI, or EIP-681 URI plus an unsigned ERC-20 transaction {to, value, data, chainId}), non-null only while payable is true; a wallet (the user's, or an agent's acting for the user) sends it once, only if data.destination is the user's own address and POST /agent/swap/payment-check returned data.paymentCheck.safeToSend true for the unsigned payment. The same idempotency-key and body replay the same order (header idempotent-replayed: true, top-level replayed: true) with the live state: a replay offers payment only if the order still waits for funding. Never pay from a replayed or recovered create without a status read showing payable: true. While the provider's order limits are reached the create answers 202 state queued (QueuedCreateResponse, Retry-After): send the same create again; the first 200 after waiting carries data.fromQueue true. Creating an order moves no funds; whether to confirm with the user first follows the user's and the client's policy. Funds move only when the user's wallet signs. Swapper never signs.

post/agent/swap/create

Request

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

Headers

idempotency-keystring required

Required on agent create and execute (400 idempotency_key_required without it). Use a new random value per order, such as a UUID from crypto.randomUUID(); never a guessable one, because anyone who knows the key can look the order up. Reuse it only to retry this same create: the same key and body return the same order (header idempotent-replayed: true) with its live state. Retry with it after network errors, timeouts and 503 order_outcome_unknown.

Request body

quoteIdstring required

data.quoteId from POST /agent/swap/quote. Required by default (400 quote_required).

tostring

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.

amountstring

Optional; must equal the quote's data.amount.

quoteIntentDigeststring

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.

sessionIdstring 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.

Example request

{
  "to": "0x1111111111111111111111111111111111111111"
}

Response

Create response (a new order, or a replay / recovery reporting the live order; data.fromQueue true on the first answer after waiting in the order line)

okboolean
operationstring
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.

terminalboolean

Live value (false for a new order).

outcome'completed' | 'refunded' | 'failed' | 'expired' | 'null' nullable
replayedboolean

true when this replays an earlier create under the same idempotency-key: state, terminal, outcome, next and payable are read from the live order.

recoveredboolean

true when the order was found after an unknown provider outcome; live state as for replayed.

Example response

{
  "operation": "swap.create",
  "data": {
    "deposit": {
      "payment": {
        "uri": "monero:<deposit address>?tx_amount=0.5",
        "gas": {
          "symbol": "ETH",
          "chain": "Arbitrum One"
        }
      }
    }
  }
}

Changes

No recorded changes to this operation across all 1 revision of this API.