# Off Ramp

Off-ramp operations (crypto to fiat) — sell crypto and receive fiat

## Get off-ramp price estimate

> Get indicative off-ramp pricing (crypto to fiat) without creating a quote.\
> Pricing can be driven from either side: provide \`crypto\_amount\` to compute the fiat payout, or \`fiat\_amount\` to compute the crypto required (exactly one of the two).\
> This endpoint is lightweight and does not reserve liquidity.\
> \
> Use this when you need quick pricing discovery before calling \`/partner/offramp/quote\`.\
> Optional filters: \`payment\_method\_slug\`, \`payment\_network\_slug\`, \`provider\_scope\`, \`country\_code\`.\
> This endpoint is indicative and does not apply user/payment-details ownership checks; for executable pricing use quote.\
> \
> \*\*Note:\*\* EUR, AUD, and GBP off-ramp payouts require the user's physical\
> address. Ensure the user's KYC includes \`address\`, \`city\`, and \`postal\_code\` before initiating.\
> See \`PATCH /partner/users/{user\_uuid}/kyc\` to update.<br>

```json
{"openapi":"3.0.3","info":{"title":"Unigox API Gateway","version":"1.0.0"},"tags":[{"name":"Off-Ramp","description":"Off-ramp operations (crypto to fiat) — sell crypto and receive fiat"}],"servers":[{"url":"https://api-staging.unigox.com","description":"Sandbox server"},{"url":"https://api.unigox.com","description":"Production server"}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Partner API key for authentication. Required for all partner account endpoints."}},"schemas":{"PartnerPriceEstimateRequest":{"type":"object","required":["crypto_currency","fiat_currency"],"oneOf":[{"required":["crypto_amount"]},{"required":["fiat_amount"]}],"properties":{"crypto_currency":{"type":"string","description":"Cryptocurrency code (e.g., USDT, USDC)"},"fiat_currency":{"type":"string","description":"Target fiat currency code (e.g., NGN, USD, EUR)"},"crypto_amount":{"type":"string","description":"Crypto amount to estimate from. Provide exactly one of crypto_amount or fiat_amount."},"fiat_amount":{"type":"string","description":"Fiat amount to estimate from. Provide exactly one of crypto_amount or fiat_amount."},"payment_method_slug":{"type":"string","description":"Optional payment method slug filter to narrow routing"},"payment_network_slug":{"type":"string","description":"Optional payment network slug filter to narrow routing"},"provider_scope":{"type":"string","description":"Optional provider scope filter; defaults to all","enum":["all","licensed_only","p2p_only"]},"country_code":{"type":"string","description":"Optional ISO country code (alpha-2) used for fee/routing context"}}},"APIResponse":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","description":"Indicates if the request was successful"},"data":{"description":"Response data (structure varies by endpoint) or error information if success is false"}}},"PartnerPriceEstimateResponse":{"type":"object","properties":{"rate":{"type":"string","description":"Indicative crypto-to-fiat exchange rate"},"crypto_amount":{"type":"string","description":"Estimated total crypto amount (includes fees)"},"fiat_amount":{"type":"string","description":"Estimated fiat amount"},"fee_breakdown":{"$ref":"#/components/schemas/FeeBreakdown"},"payment_method_slug":{"type":"string","description":"Matched payment method slug used for estimate"},"payment_network_slug":{"type":"string","description":"Matched payment network slug used for estimate"},"provider_scope":{"type":"string","description":"Applied provider scope"},"is_indicative":{"type":"boolean","description":"True when response is an estimate and not a reserving quote"}}},"FeeBreakdown":{"type":"object","description":"Transparent breakdown of all fee components (amounts in crypto)","properties":{"platform_fee":{"type":"string","description":"Platform fee in crypto"},"platform_fee_pct":{"type":"number","format":"double","description":"Platform fee as a percentage"},"partner_fee":{"type":"string","description":"Partner's fee markup in crypto (0 until partner fee dashboard is available)"},"partner_fee_pct":{"type":"number","format":"double","description":"Partner's fee percentage (0 until partner fee dashboard is available)"},"total_fee":{"type":"string","description":"Total fee in crypto (platform_fee + partner_fee)"}}},"PartnerErrorResponse":{"type":"object","required":["success","error"],"properties":{"success":{"type":"boolean"},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Machine-readable error code. Possible values:\n- `QUOTE_EXPIRED` — quote TTL has passed\n- `QUOTE_NOT_FOUND` — quote ID does not exist\n- `QUOTE_ALREADY_USED` — quote was already consumed by initiate\n- `PRICE_CHANGED_REQUOTE` — price changed beyond slippage tolerance, partner should request a new quote\n- `ORDER_NOT_FOUND` — order ID does not exist or belongs to another partner\n- `NO_OFFERS_AVAILABLE` — no vendor offers for the requested pair\n- `INVALID_REQUEST` — malformed request or missing fields\n- `INVALID_PAYMENT_DETAILS` — payment details not found or invalid\n- `RAIL_ROUTE_MISMATCH` — rail or route does not match payment details\n- `UNAUTHORIZED` — authentication failed\n- `INTERNAL_ERROR` — unexpected server error\n- `INVALID_STATUS` — order is not in the correct status for the requested action\n- `TRANSACTOR_ERROR` — on-chain transaction relay failed (e.g. simulation reverted)\n- `INSUFFICIENT_BALANCE` — partner wallet lacks tokens to fund escrow\n- `OPERATION_NOT_ALLOWED` — action is not permitted for the current order state\n","enum":["QUOTE_EXPIRED","QUOTE_NOT_FOUND","QUOTE_ALREADY_USED","PRICE_CHANGED_REQUOTE","ORDER_NOT_FOUND","NO_OFFERS_AVAILABLE","INVALID_REQUEST","INVALID_PAYMENT_DETAILS","RAIL_ROUTE_MISMATCH","UNAUTHORIZED","INTERNAL_ERROR","INVALID_STATUS","TRANSACTOR_ERROR","INSUFFICIENT_BALANCE","OPERATION_NOT_ALLOWED"]},"message":{"type":"string","description":"Human-readable error message"},"details":{"type":"object","nullable":true,"additionalProperties":true,"description":"Additional context (e.g., quoted_rate, current_rate for slippage errors)"}}}}}}},"paths":{"/api/v1/partner/offramp/estimate":{"post":{"tags":["Off-Ramp"],"summary":"Get off-ramp price estimate","description":"Get indicative off-ramp pricing (crypto to fiat) without creating a quote.\nPricing can be driven from either side: provide `crypto_amount` to compute the fiat payout, or `fiat_amount` to compute the crypto required (exactly one of the two).\nThis endpoint is lightweight and does not reserve liquidity.\n\nUse this when you need quick pricing discovery before calling `/partner/offramp/quote`.\nOptional filters: `payment_method_slug`, `payment_network_slug`, `provider_scope`, `country_code`.\nThis endpoint is indicative and does not apply user/payment-details ownership checks; for executable pricing use quote.\n\n**Note:** EUR, AUD, and GBP off-ramp payouts require the user's physical\naddress. Ensure the user's KYC includes `address`, `city`, and `postal_code` before initiating.\nSee `PATCH /partner/users/{user_uuid}/kyc` to update.\n","operationId":"getOffRampEstimate","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerPriceEstimateRequest"}}}},"responses":{"200":{"description":"Estimate retrieved successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/APIResponse"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerPriceEstimateResponse"}}}]}}}},"400":{"description":"Bad request — invalid parameters, missing required fields, \nunsupported cryptocurrency, or invalid provider_scope.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerErrorResponse"}}}},"401":{"description":"Unauthorized - missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerErrorResponse"}}}},"409":{"description":"No offers available for the requested pair/amount","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerErrorResponse"}}}},"500":{"description":"Internal Server Error"}}}}}}
```

## Get off-ramp quote

> Get a price quote for an off-ramp operation (crypto to fiat). Returns the exchange rate,\
> amounts, and a quote ID that can be used to initiate the order. Quotes expire after 60 seconds.\
> \
> The \`user\_uuid\` is the public UUID returned by \`POST /partner/users\` (the \`id\` field).\
> The \`payment\_details\_id\` is the ID of a previously created payment profile for the user.\
> \
> \*\*Anchor mode — provide exactly one of \`crypto\_amount\` or \`fiat\_amount\`:\*\*\
> \
> \| Field | Anchor | Guarantee |\
> \|---|---|---|\
> \| \`crypto\_amount\` | Crypto-anchored | Exact crypto cost; fiat payout varies by Switch rate at settlement |\
> \| \`fiat\_amount\` | Fiat-anchored | Exact fiat delivery to recipient; crypto cost may vary within a 1.20× safety bound |\
> \
> The response \`anchor\_type\` field tells you which side is guaranteed: \`"crypto"\` or \`"fiat"\`.\
> \
> \*\*Fiat-anchored notes:\*\*\
> \- Useful for exact-payout use cases (e.g. deliver exactly 500,000 NGN).\
> \- If Switch quotes a crypto cost exceeding 1.20× the expected amount, the order moves to \`failed\` status\
> &#x20; and the deposit is automatically refunded from escrow back to the partner wallet. Create a new quote\
> &#x20; to retry.\
> \- \`slippage\_tolerance.type=amount\` in fiat-anchored mode is evaluated as crypto-side delta, not fiat-side\
> &#x20; (fiat is always exact by construction).\
> \- Integer-minor-unit currencies (NGN, UGX, TZS, KES, GHS): \`fiat\_amount\` must be a whole number.\
> \
> \*\*Fee structure:\*\* The \`fee\_breakdown\` object shows all fee components transparently:\
> \`platform\_fee\` (our fee in crypto), \`partner\_fee\` (partner's markup, currently 0, configurable\
> via dashboard in a future release), and \`total\_fee\` (sum of both).\
> \
> \*\*Address requirement for EUR/AUD/GBP:\*\* EUR, AUD, and GBP payouts require the\
> user's physical address (street, city, postal code). If the user's\
> KYC does not include address data, the quote response will include a \`beneficiary\_requirements\`\
> field indicating which fields are needed. You can update the user's KYC with\
> \`PATCH /partner/users/{user\_uuid}/kyc\` to provide the address before or after initiating the order.\
> If you initiate without address, the order will be created with status \`pending\_address\` until\
> the address is provided.<br>

```json
{"openapi":"3.0.3","info":{"title":"Unigox API Gateway","version":"1.0.0"},"tags":[{"name":"Off-Ramp","description":"Off-ramp operations (crypto to fiat) — sell crypto and receive fiat"}],"servers":[{"url":"https://api-staging.unigox.com","description":"Sandbox server"},{"url":"https://api.unigox.com","description":"Production server"}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Partner API key for authentication. Required for all partner account endpoints."}},"schemas":{"PartnerQuoteRequest":{"type":"object","required":["user_uuid","payment_details_id","crypto_currency","fiat_currency"],"oneOf":[{"required":["crypto_amount"]},{"required":["fiat_amount"]}],"properties":{"user_uuid":{"type":"string","format":"uuid","description":"Public UUID of the end-user (returned by `POST /partner/users` as `id`)"},"payment_details_id":{"type":"integer","format":"int64","description":"ID of a previously created payment profile for the user"},"crypto_currency":{"type":"string","description":"Cryptocurrency code (e.g., USDT, USDC)"},"crypto_amount":{"type":"string","description":"Amount of crypto to sell (standard units, e.g., \"100.00\" for 100 USDT).\nProvide exactly one of `crypto_amount` or `fiat_amount`.\nWhen provided, the order is **crypto-anchored** — exact crypto cost is guaranteed, fiat payout varies by rate at settlement.\n"},"fiat_amount":{"type":"string","description":"Exact fiat amount the recipient will receive (e.g., \"500000\" for 500,000 NGN).\nProvide exactly one of `crypto_amount` or `fiat_amount`.\nWhen provided, the order is **fiat-anchored** — exact fiat delivery is guaranteed, crypto cost may vary within a 1.20× safety bound.\nFor integer-minor-unit currencies (NGN, UGX, TZS, KES, GHS), must be a whole number.\n"},"fiat_currency":{"type":"string","description":"Target fiat currency code (e.g., NGN, USD, EUR)"},"rail":{"type":"string","description":"Optional rail hint; must be compatible with payment details if provided"},"route":{"type":"string","description":"Optional route hint; must be compatible with payment details if provided"},"provider_scope":{"type":"string","description":"Optional provider scope filter; defaults to all","enum":["all","licensed_only","p2p_only"]},"slippage_tolerance":{"allOf":[{"$ref":"#/components/schemas/SlippageTolerance"}],"description":"Optional. Maximum tolerated price movement between `/quote` and `/initiate`,\nand between `/initiate` and vendor acceptance. When omitted, the default is\n`percent` / `0` — any rate movement returns `PRICE_CHANGED_REQUOTE` and the\npartner must request a fresh quote. Set this to opt in to silent execution\nfor small market movements.\n"},"has_fiat_settlement_notification":{"type":"boolean","description":"When `true`, only match liquidity providers that send fiat settlement\nnotifications. Orders from these providers complete automatically when\nfiat is delivered. When omitted or `false`, all providers are eligible.\n"}}},"SlippageTolerance":{"type":"object","properties":{"type":{"type":"string","enum":["percent","amount","fiat"],"description":"Tolerance type:\n- `percent` — percentage of the quoted rate (e.g., 1.5 = 1.5%)\n- `amount` — absolute fiat amount (e.g., 50.00 = 50 NGN)\n- `fiat` — alias of `amount`\n"},"value":{"type":"number","format":"double","description":"Tolerance value (percentage or fiat amount depending on type)"}}},"APIResponse":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","description":"Indicates if the request was successful"},"data":{"description":"Response data (structure varies by endpoint) or error information if success is false"}}},"PartnerQuoteResponse":{"type":"object","properties":{"quote_id":{"type":"string","format":"uuid","description":"Unique quote identifier — use this to initiate the order"},"anchor_type":{"type":"string","enum":["crypto","fiat"],"description":"Indicates which amount is guaranteed at settlement.\n- `crypto` — `crypto_amount` is exact; `fiat_amount` may vary by Switch rate at settlement.\n- `fiat` — `fiat_amount` is exact; `crypto_amount` may vary within a 1.20× safety bound.\n"},"rate":{"type":"string","description":"Crypto-to-fiat exchange rate at quote time"},"crypto_amount":{"type":"string","description":"Total crypto amount the user sends (includes all fees)"},"fiat_amount":{"type":"string","description":"Fiat amount the recipient receives"},"fee_breakdown":{"$ref":"#/components/schemas/FeeBreakdown"},"payment_details_id":{"type":"integer","format":"int64","description":"Payment details ID used for this quote"},"payment_method_id":{"type":"integer","format":"int64","description":"Matched payment method ID"},"payment_network_id":{"type":"integer","format":"int64","description":"Matched payment network ID"},"expires_at":{"type":"string","format":"date-time","description":"Quote expiration timestamp (30 seconds from creation)"},"has_fiat_settlement_notification":{"type":"boolean","description":"Whether the matched liquidity provider sends fiat settlement notifications.\nWhen `true`, the order will complete automatically when fiat is delivered\nto the recipient. When `false`, you must call `confirm-fiat-received`.\nYou can call `confirm-fiat-received` at any time regardless of this value.\n"}}},"FeeBreakdown":{"type":"object","description":"Transparent breakdown of all fee components (amounts in crypto)","properties":{"platform_fee":{"type":"string","description":"Platform fee in crypto"},"platform_fee_pct":{"type":"number","format":"double","description":"Platform fee as a percentage"},"partner_fee":{"type":"string","description":"Partner's fee markup in crypto (0 until partner fee dashboard is available)"},"partner_fee_pct":{"type":"number","format":"double","description":"Partner's fee percentage (0 until partner fee dashboard is available)"},"total_fee":{"type":"string","description":"Total fee in crypto (platform_fee + partner_fee)"}}},"PartnerErrorResponse":{"type":"object","required":["success","error"],"properties":{"success":{"type":"boolean"},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Machine-readable error code. Possible values:\n- `QUOTE_EXPIRED` — quote TTL has passed\n- `QUOTE_NOT_FOUND` — quote ID does not exist\n- `QUOTE_ALREADY_USED` — quote was already consumed by initiate\n- `PRICE_CHANGED_REQUOTE` — price changed beyond slippage tolerance, partner should request a new quote\n- `ORDER_NOT_FOUND` — order ID does not exist or belongs to another partner\n- `NO_OFFERS_AVAILABLE` — no vendor offers for the requested pair\n- `INVALID_REQUEST` — malformed request or missing fields\n- `INVALID_PAYMENT_DETAILS` — payment details not found or invalid\n- `RAIL_ROUTE_MISMATCH` — rail or route does not match payment details\n- `UNAUTHORIZED` — authentication failed\n- `INTERNAL_ERROR` — unexpected server error\n- `INVALID_STATUS` — order is not in the correct status for the requested action\n- `TRANSACTOR_ERROR` — on-chain transaction relay failed (e.g. simulation reverted)\n- `INSUFFICIENT_BALANCE` — partner wallet lacks tokens to fund escrow\n- `OPERATION_NOT_ALLOWED` — action is not permitted for the current order state\n","enum":["QUOTE_EXPIRED","QUOTE_NOT_FOUND","QUOTE_ALREADY_USED","PRICE_CHANGED_REQUOTE","ORDER_NOT_FOUND","NO_OFFERS_AVAILABLE","INVALID_REQUEST","INVALID_PAYMENT_DETAILS","RAIL_ROUTE_MISMATCH","UNAUTHORIZED","INTERNAL_ERROR","INVALID_STATUS","TRANSACTOR_ERROR","INSUFFICIENT_BALANCE","OPERATION_NOT_ALLOWED"]},"message":{"type":"string","description":"Human-readable error message"},"details":{"type":"object","nullable":true,"additionalProperties":true,"description":"Additional context (e.g., quoted_rate, current_rate for slippage errors)"}}}}}}},"paths":{"/api/v1/partner/offramp/quote":{"post":{"tags":["Off-Ramp"],"summary":"Get off-ramp quote","description":"Get a price quote for an off-ramp operation (crypto to fiat). Returns the exchange rate,\namounts, and a quote ID that can be used to initiate the order. Quotes expire after 60 seconds.\n\nThe `user_uuid` is the public UUID returned by `POST /partner/users` (the `id` field).\nThe `payment_details_id` is the ID of a previously created payment profile for the user.\n\n**Anchor mode — provide exactly one of `crypto_amount` or `fiat_amount`:**\n\n| Field | Anchor | Guarantee |\n|---|---|---|\n| `crypto_amount` | Crypto-anchored | Exact crypto cost; fiat payout varies by Switch rate at settlement |\n| `fiat_amount` | Fiat-anchored | Exact fiat delivery to recipient; crypto cost may vary within a 1.20× safety bound |\n\nThe response `anchor_type` field tells you which side is guaranteed: `\"crypto\"` or `\"fiat\"`.\n\n**Fiat-anchored notes:**\n- Useful for exact-payout use cases (e.g. deliver exactly 500,000 NGN).\n- If Switch quotes a crypto cost exceeding 1.20× the expected amount, the order moves to `failed` status\n  and the deposit is automatically refunded from escrow back to the partner wallet. Create a new quote\n  to retry.\n- `slippage_tolerance.type=amount` in fiat-anchored mode is evaluated as crypto-side delta, not fiat-side\n  (fiat is always exact by construction).\n- Integer-minor-unit currencies (NGN, UGX, TZS, KES, GHS): `fiat_amount` must be a whole number.\n\n**Fee structure:** The `fee_breakdown` object shows all fee components transparently:\n`platform_fee` (our fee in crypto), `partner_fee` (partner's markup, currently 0, configurable\nvia dashboard in a future release), and `total_fee` (sum of both).\n\n**Address requirement for EUR/AUD/GBP:** EUR, AUD, and GBP payouts require the\nuser's physical address (street, city, postal code). If the user's\nKYC does not include address data, the quote response will include a `beneficiary_requirements`\nfield indicating which fields are needed. You can update the user's KYC with\n`PATCH /partner/users/{user_uuid}/kyc` to provide the address before or after initiating the order.\nIf you initiate without address, the order will be created with status `pending_address` until\nthe address is provided.\n","operationId":"getOffRampQuote","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerQuoteRequest"}}}},"responses":{"200":{"description":"Quote retrieved successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/APIResponse"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerQuoteResponse"}}}]}}}},"400":{"description":"Bad request — invalid parameters or missing required fields","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerErrorResponse"}}}},"401":{"description":"Unauthorized — invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerErrorResponse"}}}},"404":{"description":"User or payment details not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerErrorResponse"}}}},"409":{"description":"No offers available for the requested pair","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerErrorResponse"}}}}}}}}}
```

## Initiate off-ramp order

> Initiate an off-ramp order using a previously obtained quote. The quote must still be active\
> (not expired or already used). The system refreshes the price and checks slippage tolerance\
> before creating the order.\
> \
> If the price has moved beyond the slippage tolerance, a \`PRICE\_CHANGED\_REQUOTE\` error is returned\
> with details about the old and new rates, so the partner can request a new quote.\
> \
> On success, returns the created order with status \`awaiting\_liquidity\_provider\`.\
> \
> \*\*Address requirement for EUR/AUD/GBP:\*\* For EUR/AUD/GBP orders, if the\
> user's KYC does not include a physical address, the order will be created with status\
> \`pending\_address\`. The response will include \`required\_fields: \["address", "city", "postal\_code"]\`\
> and \`next\_action: "provide\_address"\`. Call \`PATCH /partner/users/{user\_uuid}/kyc\` to add the\
> address, and the order will automatically advance.\
> \
> \*\*Recommended flow for EUR/AUD/GBP:\*\*\
> 1\. Submit KYC with address/city/postal\_code (\`POST /kyc-submissions\`)\
> 2\. Create payment details (\`POST /payment-details\`) — address auto-populated from KYC\
> 3\. Get quote (\`POST /offramp/quote\`)\
> 4\. Initiate (\`POST /offramp/initiate\`) — proceeds without delays<br>

```json
{"openapi":"3.0.3","info":{"title":"Unigox API Gateway","version":"1.0.0"},"tags":[{"name":"Off-Ramp","description":"Off-ramp operations (crypto to fiat) — sell crypto and receive fiat"}],"servers":[{"url":"https://api-staging.unigox.com","description":"Sandbox server"},{"url":"https://api.unigox.com","description":"Production server"}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Partner API key for authentication. Required for all partner account endpoints."}},"schemas":{"PartnerInitiateRequest":{"type":"object","required":["quote_id"],"properties":{"quote_id":{"type":"string","format":"uuid","description":"Quote ID from the quote endpoint. Payment details are taken from the quote."},"reference_id":{"type":"string","description":"Optional partner-supplied external reference ID (e.g. your internal order ID). Stored on the order and echoed back in responses."}}},"APIResponse":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","description":"Indicates if the request was successful"},"data":{"description":"Response data (structure varies by endpoint) or error information if success is false"}}},"PartnerInitiateResponse":{"type":"object","properties":{"order_id":{"type":"string","format":"uuid","description":"Unique order identifier for tracking"},"status":{"$ref":"#/components/schemas/PartnerOrderStatus"},"quote_id":{"type":"string","format":"uuid","description":"Quote ID that was consumed"},"reference_id":{"type":"string","nullable":true,"description":"Partner-supplied external reference ID (echoed from initiate request)"},"order_type":{"type":"string","enum":["offramp","onramp"],"description":"Direction of the order"},"amounts":{"$ref":"#/components/schemas/PartnerOrderAmounts"},"fee_breakdown":{"$ref":"#/components/schemas/FeeBreakdown"},"timeline":{"type":"array","items":{"$ref":"#/components/schemas/TimelineEntry"}},"created_at":{"type":"string","format":"date-time"}}},"PartnerOrderStatus":{"type":"string","description":"Order status.\n\n**Off-ramp statuses (crypto → fiat):**\n- `created` — order created, quote locked\n- `awaiting_liquidity_provider` — order initiated, waiting for vendor acceptance\n- `awaiting_crypto_transfer_authorization` — partner must authorize crypto transfer via EIP-712 signature\n- `crypto_transfer_authorization_pending` — authorization submitted, awaiting on-chain confirmation\n- `crypto_received` — crypto locked in escrow\n- `fiat_payment_started` — buyer submitted fiat payment proof\n- `awaiting_fiat_received_confirmation` — waiting for partner to confirm fiat receipt\n- `fiat_received_confirmed` — fiat confirmed, crypto release pending\n- `crypto_release_pending` — escrow release in progress\n- `completed` — order fully completed\n- `cancelled` — order cancelled\n- `failed` — terminal error\n- `price_changed_requote_needed` — slippage exceeded at initiation (not a trade status)\n\n**On-ramp statuses (fiat → crypto):**\n- `awaiting_liquidity_provider` — order initiated, waiting for vendor acceptance\n- `awaiting_vendor_escrow_funding` — vendor accepted, funding escrow with crypto\n- `awaiting_fiat_transfer` — crypto locked in escrow, partner must confirm payment sent\n- `fiat_transfer_pending` — fiat payment confirmed, awaiting vendor confirmation of receipt\n- `completed` — vendor confirmed fiat received, crypto released to end-user wallet\n- `cancelled` — order cancelled\n- `failed` — escrow error\n\n**On-ramp send-out statuses (optional post-trade cross-chain transfer):**\n\n> After an on-ramp order reaches `completed`, the partner may optionally initiate a\n> **send-out** — a cross-chain transfer of the received crypto to a destination network\n> (e.g. XAI → Arbitrum). This is an additional flow and does not affect the core trade lifecycle.\n> The `completed` status reflects that the trade itself is done; send-out statuses track\n> the subsequent cross-chain transfer only.\n\n- `send_out_pending` — send-out initiated, cross-chain transfer in progress\n- `send_out_completed` — cross-chain transfer successfully delivered to destination\n- `send_out_failed` — cross-chain transfer failed; the on-ramp trade itself remains completed\n","enum":["created","awaiting_liquidity_provider","awaiting_crypto_transfer_authorization","crypto_transfer_authorization_pending","crypto_received","fiat_payment_started","awaiting_fiat_received_confirmation","fiat_received_confirmed","crypto_release_pending","awaiting_vendor_escrow_funding","awaiting_fiat_transfer","fiat_transfer_pending","completed","cancelled","failed","price_changed_requote_needed","send_out_pending","send_out_completed","send_out_failed"]},"PartnerOrderAmounts":{"type":"object","properties":{"crypto_amount":{"type":"string","description":"Total crypto amount (standard units)"},"fiat_amount":{"type":"string","description":"Fiat amount the user receives"},"crypto_currency":{"type":"string","description":"Cryptocurrency code"},"fiat_currency":{"type":"string","description":"Fiat currency code"},"rate":{"type":"string","description":"Crypto-to-fiat exchange rate"}}},"FeeBreakdown":{"type":"object","description":"Transparent breakdown of all fee components (amounts in crypto)","properties":{"platform_fee":{"type":"string","description":"Platform fee in crypto"},"platform_fee_pct":{"type":"number","format":"double","description":"Platform fee as a percentage"},"partner_fee":{"type":"string","description":"Partner's fee markup in crypto (0 until partner fee dashboard is available)"},"partner_fee_pct":{"type":"number","format":"double","description":"Partner's fee percentage (0 until partner fee dashboard is available)"},"total_fee":{"type":"string","description":"Total fee in crypto (platform_fee + partner_fee)"}}},"TimelineEntry":{"type":"object","properties":{"status":{"$ref":"#/components/schemas/PartnerOrderStatus"},"timestamp":{"type":"string","format":"date-time","description":"When this status was reached"},"description":{"type":"string","description":"Human-readable description of the status change"}}},"PartnerErrorResponse":{"type":"object","required":["success","error"],"properties":{"success":{"type":"boolean"},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Machine-readable error code. Possible values:\n- `QUOTE_EXPIRED` — quote TTL has passed\n- `QUOTE_NOT_FOUND` — quote ID does not exist\n- `QUOTE_ALREADY_USED` — quote was already consumed by initiate\n- `PRICE_CHANGED_REQUOTE` — price changed beyond slippage tolerance, partner should request a new quote\n- `ORDER_NOT_FOUND` — order ID does not exist or belongs to another partner\n- `NO_OFFERS_AVAILABLE` — no vendor offers for the requested pair\n- `INVALID_REQUEST` — malformed request or missing fields\n- `INVALID_PAYMENT_DETAILS` — payment details not found or invalid\n- `RAIL_ROUTE_MISMATCH` — rail or route does not match payment details\n- `UNAUTHORIZED` — authentication failed\n- `INTERNAL_ERROR` — unexpected server error\n- `INVALID_STATUS` — order is not in the correct status for the requested action\n- `TRANSACTOR_ERROR` — on-chain transaction relay failed (e.g. simulation reverted)\n- `INSUFFICIENT_BALANCE` — partner wallet lacks tokens to fund escrow\n- `OPERATION_NOT_ALLOWED` — action is not permitted for the current order state\n","enum":["QUOTE_EXPIRED","QUOTE_NOT_FOUND","QUOTE_ALREADY_USED","PRICE_CHANGED_REQUOTE","ORDER_NOT_FOUND","NO_OFFERS_AVAILABLE","INVALID_REQUEST","INVALID_PAYMENT_DETAILS","RAIL_ROUTE_MISMATCH","UNAUTHORIZED","INTERNAL_ERROR","INVALID_STATUS","TRANSACTOR_ERROR","INSUFFICIENT_BALANCE","OPERATION_NOT_ALLOWED"]},"message":{"type":"string","description":"Human-readable error message"},"details":{"type":"object","nullable":true,"additionalProperties":true,"description":"Additional context (e.g., quoted_rate, current_rate for slippage errors)"}}}}}}},"paths":{"/api/v1/partner/offramp/initiate":{"post":{"tags":["Off-Ramp"],"summary":"Initiate off-ramp order","description":"Initiate an off-ramp order using a previously obtained quote. The quote must still be active\n(not expired or already used). The system refreshes the price and checks slippage tolerance\nbefore creating the order.\n\nIf the price has moved beyond the slippage tolerance, a `PRICE_CHANGED_REQUOTE` error is returned\nwith details about the old and new rates, so the partner can request a new quote.\n\nOn success, returns the created order with status `awaiting_liquidity_provider`.\n\n**Address requirement for EUR/AUD/GBP:** For EUR/AUD/GBP orders, if the\nuser's KYC does not include a physical address, the order will be created with status\n`pending_address`. The response will include `required_fields: [\"address\", \"city\", \"postal_code\"]`\nand `next_action: \"provide_address\"`. Call `PATCH /partner/users/{user_uuid}/kyc` to add the\naddress, and the order will automatically advance.\n\n**Recommended flow for EUR/AUD/GBP:**\n1. Submit KYC with address/city/postal_code (`POST /kyc-submissions`)\n2. Create payment details (`POST /payment-details`) — address auto-populated from KYC\n3. Get quote (`POST /offramp/quote`)\n4. Initiate (`POST /offramp/initiate`) — proceeds without delays\n","operationId":"initiateOffRamp","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerInitiateRequest"}}}},"responses":{"201":{"description":"Off-ramp order created successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/APIResponse"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerInitiateResponse"}}}]}}}},"400":{"description":"Bad request — missing required fields","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerErrorResponse"}}}},"401":{"description":"Unauthorized — invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerErrorResponse"}}}},"404":{"description":"Quote not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerErrorResponse"}}}},"409":{"description":"Quote expired, already used, or slippage exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerErrorResponse"}}}}}}}}}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://developers.unigox.com/api-reference/off-ramp.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
