> For the complete documentation index, see [llms.txt](https://developers.unigox.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developers.unigox.com/api-reference/on-ramp.md).

# On Ramp

On-ramp operations (fiat to crypto) — partner initiates a buy order, vendor provides liquidity, end-user sends fiat

## Get on-ramp price estimate

> Get indicative on-ramp pricing (fiat to crypto) without creating a quote.\
> This endpoint is lightweight and does not reserve liquidity.\
> \
> Use this when you need quick pricing discovery before calling \`/partner/onramp/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 (which still requires an API key).\
> \
> \*\*Authentication:\*\* None. This is a public endpoint — no API key required (same as \`/partner/liquidity\`).<br>

```json
{"openapi":"3.0.3","info":{"title":"Unigox API Gateway","version":"1.0.0"},"tags":[{"name":"On-Ramp","description":"On-ramp operations (fiat to crypto) — partner initiates a buy order, vendor provides liquidity, end-user sends fiat"}],"servers":[{"url":"https://api-staging.unigox.com","description":"Sandbox server"},{"url":"https://api.unigox.com","description":"Production server"}],"security":[],"paths":{"/api/v1/partner/onramp/estimate":{"post":{"tags":["On-Ramp"],"summary":"Get on-ramp price estimate","description":"Get indicative on-ramp pricing (fiat to crypto) without creating a quote.\nThis endpoint is lightweight and does not reserve liquidity.\n\nUse this when you need quick pricing discovery before calling `/partner/onramp/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 (which still requires an API key).\n\n**Authentication:** None. This is a public endpoint — no API key required (same as `/partner/liquidity`).\n","operationId":"getOnRampEstimate","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"}}}},"409":{"description":"No offers available for the requested pair/amount","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerErrorResponse"}}}},"500":{"description":"Internal Server Error"}}}}},"components":{"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"},"partner_fee_pct":{"type":"string","pattern":"^[0-9]+(\\.[0-9]+)?$","description":"Optional per-order markup you charge your end user, applied on the same crypto base\nas the platform fee (non-compounding). Percent units: `1` means 1%. Omitting the field\nfrom this request payload — or sending `0` — means no markup; the markup cannot be\nnegative. Fiat-anchored offramp orders have no cap — the deposit grows to cover the\nmarkup. Every other case hits a formula domain bound, not a business cap: onramp\nrejects `partner_fee_pct >= 100` (the withheld or grossed-up fee would consume the\nentire buyer amount, leaving nothing delivered); crypto-anchored offramp rejects it\nwhen the fee would consume the entire pinned deposit.\n\nNote: you send this value as a JSON **string**, but it round-trips as a JSON **number**\nin `fee_breakdown.partner_fee_pct` and in the webhook `data.partner_fee_pct` field —\ndon't parse those as strings.\n\n**Rejection reference:**\n\n| Rejection | `error.code` | HTTP status |\n|---|---|---|\n| `partner_fee_pct` is non-numeric, negative, `NaN`, or `Inf` | `INVALID_REQUEST` | 400 |\n| Onramp: fee would consume the entire buyer amount (`partner_fee_pct >= 100`) | `INVALID_REQUEST` | 400 |\n| Crypto-anchored offramp: fee would consume the entire pinned deposit | `INVALID_REQUEST` | 400 |\n| Fiat-anchored offramp: the computed fee is not a finite amount | `INVALID_REQUEST` | 400 |\n| Crypto-anchored offramp: the carved fee-exclusive gross falls outside the matched offer's min/max | `NO_OFFERS_AVAILABLE` | 409 |\n","default":"0"}}},"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 — the fee-exclusive gross: the user's amount plus the platform\nfee. Partner-fee-exclusive, except for a crypto-anchored onramp estimate (request declares\n`crypto_amount`): there the amount is sized up so the delivered amount stays exact after the\npartner fee is withheld, so this field includes the markup. A crypto-anchored offramp\nestimate carves both fees out of the pinned deposit instead, so this field and\n`fee_breakdown.platform_fee` both shrink relative to a no-markup request, even though the\nmarkup itself stays out of this field. Every other case is unaffected — the markup is\nreported separately in `fee_breakdown.partner_fee`.\n\nOn-ramp: the end user would receive `crypto_amount − fee_breakdown.platform_fee −\nfee_breakdown.partner_fee` for a fiat-anchored estimate, or exactly this `crypto_amount` for a\ncrypto-anchored estimate. Off-ramp: the amount you would actually fund at initiation is\n`crypto_amount + fee_breakdown.partner_fee`.\n"},"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":"Your markup in crypto, computed from the `partner_fee_pct` you declared on the\nquote/estimate request, on the same base as the platform fee. `0` when no markup was declared.\n"},"partner_fee_pct":{"type":"number","format":"double","description":"Your markup percentage as declared on the request (`1` means 1%); `0` when none."},"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- `KYC_NOT_CLEARED` — the customer is not KYC-cleared for this transaction; `error.details.kyc_status` carries their current partner-facing status\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","KYC_NOT_CLEARED"]},"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)"}}}}}}}}
```

## Get on-ramp quote

> Get a price quote for an on-ramp operation (fiat to crypto). 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).\
> \
> \*\*Fee structure:\*\* The \`fee\_breakdown\` object shows all fee components transparently:\
> \`platform\_fee\` (our fee in crypto), \`partner\_fee\` (your per-order markup in crypto — declare it\
> by passing \`partner\_fee\_pct\` on the quote/estimate request, where \`1\` means 1%), \`partner\_fee\_pct\`\
> (the percentage applied), and \`total\_fee\` (sum of both). The partner fee is computed on the same\
> crypto base as the platform fee (non-compounding). There's no separate transfer leg for on-ramp:\
> at release the full buyer amount lands in your partner wallet, and the markup is captured by\
> withholding it when the crypto bridges out at send-out (for a crypto-anchored order, the trade is\
> sized up beforehand so the requested amount still arrives net of the withheld fee). What the end\
> user receives: \`crypto\_amount − fee\_breakdown.platform\_fee − fee\_breakdown.partner\_fee\` for a\
> fiat-anchored order, or exactly the \`crypto\_amount\` you requested for a crypto-anchored order.\
> \
> Apply your fee on the \*\*same side\*\* the platform does, or your receipts won't reconcile with our\
> orders. \`crypto\_amount\` in responses and webhooks stays partner-fee-exclusive, with two\
> exceptions: a crypto-anchored onramp order (request declares \`crypto\_amount\`) is sized up so the\
> delivered amount stays exact after the markup is withheld, so \`crypto\_amount\` there includes the\
> markup; a crypto-anchored offramp order (request declares \`crypto\_amount\`) carves both fees out of\
> the pinned deposit, so \`crypto\_amount\` and \`platform\_fee\` both shrink relative to a no-markup\
> request even though the markup itself stays out of \`crypto\_amount\`. Every other case is unaffected\
> — the markup shows only in \`fee\_breakdown\` (and in the funding amount returned by transfer\
> authorization for offramp). Fiat-anchored offramp orders have no cap on the markup — the deposit\
> grows to cover it. Every other case hits a formula domain bound, not a business cap: onramp\
> rejects \`partner\_fee\_pct >= 100\` (the withheld or grossed-up fee would consume the entire buyer\
> amount, leaving nothing delivered); crypto-anchored offramp rejects it when the fee would consume\
> the entire pinned deposit.\
> \
> \*\*Payment method:\*\* Provide \`payment\_method\_slug\` and/or \`payment\_network\_slug\` to narrow routing.\
> If omitted, the best available option for the pair is selected automatically.\
> \
> \*\*KYC requirement:\*\* The customer must be KYC-cleared before you can quote or transact for them.\
> If the customer is not cleared, this endpoint returns \`422 KYC\_NOT\_CLEARED\` and\
> \`error.details.kyc\_status\` carries their current partner-facing KYC status. A customer still in\
> compliance review is reported as \`IN\_PROGRESS\` — treat \`IN\_PROGRESS\` as "not cleared yet" and\
> keep polling \`GET /api/v1/partner/users/{user\_uuid}/verification-status\`.<br>

```json
{"openapi":"3.0.3","info":{"title":"Unigox API Gateway","version":"1.0.0"},"tags":[{"name":"On-Ramp","description":"On-ramp operations (fiat to crypto) — partner initiates a buy order, vendor provides liquidity, end-user sends 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":{"PartnerOnRampQuoteRequest":{"type":"object","required":["user_uuid","fiat_currency","crypto_currency"],"oneOf":[{"required":["fiat_amount"]},{"required":["crypto_amount"]}],"properties":{"user_uuid":{"type":"string","format":"uuid","description":"Public UUID of the end-user (returned by `POST /partner/users` as `id`)"},"fiat_amount":{"type":"string","description":"Fiat amount the end-user will send. Provide exactly one of fiat_amount or crypto_amount."},"crypto_amount":{"type":"string","description":"Crypto amount to receive. Provide exactly one of fiat_amount or crypto_amount."},"fiat_currency":{"type":"string","description":"Source fiat currency code (ISO 4217)"},"crypto_currency":{"type":"string","description":"Target cryptocurrency code (e.g., USDT, USDC)"},"payment_method_slug":{"type":"string","description":"Optional payment method slug to narrow routing"},"payment_network_slug":{"type":"string","description":"Optional payment network slug 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 2-letter country code for fee/routing context"},"partner_fee_pct":{"type":"string","pattern":"^[0-9]+(\\.[0-9]+)?$","description":"Optional per-order markup you charge your end user, applied on the same crypto base\nas the platform fee (non-compounding). Percent units: `1` means 1%. Omitting the field\nfrom this request payload — or sending `0` — means no markup; the markup cannot be\nnegative. Fiat-anchored offramp orders have no cap — the deposit grows to cover the\nmarkup. Every other case hits a formula domain bound, not a business cap: onramp\nrejects `partner_fee_pct >= 100` (the withheld or grossed-up fee would consume the\nentire buyer amount, leaving nothing delivered); crypto-anchored offramp rejects it\nwhen the fee would consume the entire pinned deposit.\n\nNote: you send this value as a JSON **string**, but it round-trips as a JSON **number**\nin `fee_breakdown.partner_fee_pct` and in the webhook `data.partner_fee_pct` field —\ndon't parse those as strings.\n\n**Rejection reference:**\n\n| Rejection | `error.code` | HTTP status |\n|---|---|---|\n| `partner_fee_pct` is non-numeric, negative, `NaN`, or `Inf` | `INVALID_REQUEST` | 400 |\n| Onramp: fee would consume the entire buyer amount (`partner_fee_pct >= 100`) | `INVALID_REQUEST` | 400 |\n| Crypto-anchored offramp: fee would consume the entire pinned deposit | `INVALID_REQUEST` | 400 |\n| Fiat-anchored offramp: the computed fee is not a finite amount | `INVALID_REQUEST` | 400 |\n| Crypto-anchored offramp: the carved fee-exclusive gross falls outside the matched offer's min/max | `NO_OFFERS_AVAILABLE` | 409 |\n","default":"0"}}},"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"}}},"PartnerOnRampQuoteResponse":{"type":"object","properties":{"quote_id":{"type":"string","format":"uuid","description":"Unique quote identifier — use this to initiate the order"},"rate":{"type":"string","description":"Fiat-to-crypto exchange rate (fiat units per 1 crypto unit)"},"crypto_amount":{"type":"string","description":"Total crypto amount for the order — the end-user's amount plus the platform fee.\nPartner-fee-exclusive on a fiat-anchored quote (request declared `fiat_amount`): the markup\nis reported separately in `fee_breakdown.partner_fee` and is not added here — the end user\nreceives `crypto_amount − fee_breakdown.platform_fee − fee_breakdown.partner_fee`, withheld\nfrom this amount at bridge send-out. On a crypto-anchored quote (request declared\n`crypto_amount`), the trade is sized up so this field includes the markup, and the end user\nreceives exactly the requested `crypto_amount`, unchanged by the markup.\n"},"fiat_amount":{"type":"string","description":"Fiat amount the end-user sends"},"fee_breakdown":{"$ref":"#/components/schemas/FeeBreakdown"},"payment_method_slug":{"type":"string","description":"Matched payment method slug used for this quote"},"payment_network_slug":{"type":"string","description":"Matched payment network slug used for this quote"},"expires_at":{"type":"string","format":"date-time","description":"Quote expiration timestamp (60 seconds from creation)"}}},"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":"Your markup in crypto, computed from the `partner_fee_pct` you declared on the\nquote/estimate request, on the same base as the platform fee. `0` when no markup was declared.\n"},"partner_fee_pct":{"type":"number","format":"double","description":"Your markup percentage as declared on the request (`1` means 1%); `0` when none."},"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- `KYC_NOT_CLEARED` — the customer is not KYC-cleared for this transaction; `error.details.kyc_status` carries their current partner-facing status\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","KYC_NOT_CLEARED"]},"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/onramp/quote":{"post":{"tags":["On-Ramp"],"summary":"Get on-ramp quote","description":"Get a price quote for an on-ramp operation (fiat to crypto). 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).\n\n**Fee structure:** The `fee_breakdown` object shows all fee components transparently:\n`platform_fee` (our fee in crypto), `partner_fee` (your per-order markup in crypto — declare it\nby passing `partner_fee_pct` on the quote/estimate request, where `1` means 1%), `partner_fee_pct`\n(the percentage applied), and `total_fee` (sum of both). The partner fee is computed on the same\ncrypto base as the platform fee (non-compounding). There's no separate transfer leg for on-ramp:\nat release the full buyer amount lands in your partner wallet, and the markup is captured by\nwithholding it when the crypto bridges out at send-out (for a crypto-anchored order, the trade is\nsized up beforehand so the requested amount still arrives net of the withheld fee). What the end\nuser receives: `crypto_amount − fee_breakdown.platform_fee − fee_breakdown.partner_fee` for a\nfiat-anchored order, or exactly the `crypto_amount` you requested for a crypto-anchored order.\n\nApply your fee on the **same side** the platform does, or your receipts won't reconcile with our\norders. `crypto_amount` in responses and webhooks stays partner-fee-exclusive, with two\nexceptions: a crypto-anchored onramp order (request declares `crypto_amount`) is sized up so the\ndelivered amount stays exact after the markup is withheld, so `crypto_amount` there includes the\nmarkup; a crypto-anchored offramp order (request declares `crypto_amount`) carves both fees out of\nthe pinned deposit, so `crypto_amount` and `platform_fee` both shrink relative to a no-markup\nrequest even though the markup itself stays out of `crypto_amount`. Every other case is unaffected\n— the markup shows only in `fee_breakdown` (and in the funding amount returned by transfer\nauthorization for offramp). Fiat-anchored offramp orders have no cap on the markup — the deposit\ngrows to cover it. Every other case hits a formula domain bound, not a business cap: onramp\nrejects `partner_fee_pct >= 100` (the withheld or grossed-up fee would consume the entire buyer\namount, leaving nothing delivered); crypto-anchored offramp rejects it when the fee would consume\nthe entire pinned deposit.\n\n**Payment method:** Provide `payment_method_slug` and/or `payment_network_slug` to narrow routing.\nIf omitted, the best available option for the pair is selected automatically.\n\n**KYC requirement:** The customer must be KYC-cleared before you can quote or transact for them.\nIf the customer is not cleared, this endpoint returns `422 KYC_NOT_CLEARED` and\n`error.details.kyc_status` carries their current partner-facing KYC status. A customer still in\ncompliance review is reported as `IN_PROGRESS` — treat `IN_PROGRESS` as \"not cleared yet\" and\nkeep polling `GET /api/v1/partner/users/{user_uuid}/verification-status`.\n","operationId":"getOnRampQuote","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerOnRampQuoteRequest"}}}},"responses":{"200":{"description":"Quote retrieved successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/APIResponse"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerOnRampQuoteResponse"}}}]}}}},"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 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"}}}},"422":{"description":"The customer is not KYC-cleared. `error.details.kyc_status` carries the customer's current partner-facing KYC status — one of `IN_PROGRESS`, `UNDER_REVIEW`, `PENDING_DOCUMENTS`, `VERIFICATION_REJECTED`, or `NOT_INITIATED`. `UNDER_REVIEW` means a manual compliance review (up to 24h); keep the customer waiting and re-check. The message stays generic.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerErrorResponse"}}}}}}}}}
```

## Initiate on-ramp order

> Initiate an on-ramp order using a previously obtained quote. The quote must still be active\
> (not expired or already used).\
> \
> On success, returns the created order with status \`awaiting\_liquidity\_provider\`.\
> Once a vendor accepts, the order moves to \`awaiting\_vendor\_escrow\_funding\`, then\
> \`awaiting\_fiat\_transfer\` when the crypto is locked in escrow.\
> \
> At \`awaiting\_fiat\_transfer\` the partner must call \`confirm-payment-sent\` to advance the order.\
> If the order type is \`payment\_request\`, call \`submit-payer-details\` first before confirming.\
> \
> \*\*KYC requirement:\*\* The customer must be KYC-cleared, as described on \`POST /onramp/quote\`.\
> If they are not, this endpoint returns \`422 KYC\_NOT\_CLEARED\` with the customer's partner-facing\
> status in \`error.details.kyc\_status\`. The check is re-applied as the order moves toward payout.<br>

```json
{"openapi":"3.0.3","info":{"title":"Unigox API Gateway","version":"1.0.0"},"tags":[{"name":"On-Ramp","description":"On-ramp operations (fiat to crypto) — partner initiates a buy order, vendor provides liquidity, end-user sends 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) — the fee-exclusive gross: the buy/sell amount plus the\nplatform fee. Partner-fee-exclusive, except for a crypto-anchored onramp order (originally\nquoted with `crypto_amount`): there the amount was sized up so the delivered amount stays\nexact after the partner fee is withheld, so this field includes the markup. A crypto-anchored\nofframp order carves both fees out of the pinned deposit instead, so this field and\n`fee_breakdown.platform_fee` both shrink relative to a no-markup request, even though the\nmarkup itself stays out of this field. Every other case is unaffected — the markup is\nreported separately in `fee_breakdown.partner_fee`.\n\nOn-ramp — what the end user receives: `crypto_amount − fee_breakdown.platform_fee −\nfee_breakdown.partner_fee` for a fiat-anchored order, or exactly this `crypto_amount` for a\ncrypto-anchored order (both fees are withheld at bridge send-out without changing this\nfield). Off-ramp — what the partner funds at transfer authorization:\n`crypto_amount + fee_breakdown.partner_fee`.\n"},"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":"Your markup in crypto, computed from the `partner_fee_pct` you declared on the\nquote/estimate request, on the same base as the platform fee. `0` when no markup was declared.\n"},"partner_fee_pct":{"type":"number","format":"double","description":"Your markup percentage as declared on the request (`1` means 1%); `0` when none."},"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- `KYC_NOT_CLEARED` — the customer is not KYC-cleared for this transaction; `error.details.kyc_status` carries their current partner-facing status\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","KYC_NOT_CLEARED"]},"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/onramp/initiate":{"post":{"tags":["On-Ramp"],"summary":"Initiate on-ramp order","description":"Initiate an on-ramp order using a previously obtained quote. The quote must still be active\n(not expired or already used).\n\nOn success, returns the created order with status `awaiting_liquidity_provider`.\nOnce a vendor accepts, the order moves to `awaiting_vendor_escrow_funding`, then\n`awaiting_fiat_transfer` when the crypto is locked in escrow.\n\nAt `awaiting_fiat_transfer` the partner must call `confirm-payment-sent` to advance the order.\nIf the order type is `payment_request`, call `submit-payer-details` first before confirming.\n\n**KYC requirement:** The customer must be KYC-cleared, as described on `POST /onramp/quote`.\nIf they are not, this endpoint returns `422 KYC_NOT_CLEARED` with the customer's partner-facing\nstatus in `error.details.kyc_status`. The check is re-applied as the order moves toward payout.\n","operationId":"initiateOnRamp","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerInitiateRequest"}}}},"responses":{"201":{"description":"On-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 or already used","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerErrorResponse"}}}},"422":{"description":"The customer is not KYC-cleared. `error.details.kyc_status` carries the customer's current partner-facing KYC status — one of `IN_PROGRESS`, `UNDER_REVIEW`, `PENDING_DOCUMENTS`, `VERIFICATION_REJECTED`, or `NOT_INITIATED`. `UNDER_REVIEW` means a manual compliance review (up to 24h); keep the customer waiting and re-check. The message stays generic.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerErrorResponse"}}}}}}}}}
```

## Submit payer details

> Submit the end-user's payment details for a \`payment\_request\` on-ramp order.\
> Only required when the order type is \`payment\_request\` — regular on-ramp orders do not need this step.\
> \
> \*\*Order types:\*\*\
> \- \*\*Regular on-ramp\*\* — the vendor sends fiat to the end-user directly. No payer details needed.\
> &#x20; The partner receives vendor payment details in the \`vendor\_payment\_details\` field of \`GET /orders/:id\`\
> &#x20; and forwards them to the end-user to initiate the transfer.\
> \- \*\*\`payment\_request\` on-ramp\*\* — the vendor sends a payment request (e.g. M-Pesa push) to the end-user.\
> &#x20; The end-user's account details must be submitted here first so the vendor knows where to send the request.\
> &#x20; Check \`payment\_request: true\` in the order response to identify this type.\
> \
> Call this endpoint after initiating the order and before calling \`confirm-payment-sent\`.\
> The order must be in \`awaiting\_fiat\_transfer\` status.\
> \
> Provide either a \`payment\_details\_id\` (referencing a previously saved payment profile) or\
> an inline \`payment\_details\` object with the payer's account information.<br>

```json
{"openapi":"3.0.3","info":{"title":"Unigox API Gateway","version":"1.0.0"},"tags":[{"name":"On-Ramp","description":"On-ramp operations (fiat to crypto) — partner initiates a buy order, vendor provides liquidity, end-user sends 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":{"SubmitPayerDetailsRequest":{"type":"object","description":"Submit the payer's payment details for a `payment_request` on-ramp order.\nProvide exactly one of `payment_details_id` (saved profile) or `payment_details` (inline object).\n","oneOf":[{"required":["payment_details_id"],"properties":{"payment_details_id":{"type":"integer","format":"int64","description":"ID of a previously saved payment profile for the end-user"}}},{"required":["payment_details"],"properties":{"payment_details":{"type":"object","required":["payment_method_id","payment_network_id","details"],"properties":{"payment_method_id":{"type":"integer","format":"int64","description":"Payment method ID"},"payment_network_id":{"type":"integer","format":"int64","description":"Payment network ID"},"details":{"type":"object","description":"Payer's account details (fields depend on payment method)","additionalProperties":true},"country_code":{"type":"string","description":"Optional ISO 2-letter country code"}}}}}]},"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"}}},"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- `KYC_NOT_CLEARED` — the customer is not KYC-cleared for this transaction; `error.details.kyc_status` carries their current partner-facing status\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","KYC_NOT_CLEARED"]},"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/orders/{order_id}/submit-payer-details":{"post":{"tags":["On-Ramp"],"summary":"Submit payer details","description":"Submit the end-user's payment details for a `payment_request` on-ramp order.\nOnly required when the order type is `payment_request` — regular on-ramp orders do not need this step.\n\n**Order types:**\n- **Regular on-ramp** — the vendor sends fiat to the end-user directly. No payer details needed.\n  The partner receives vendor payment details in the `vendor_payment_details` field of `GET /orders/:id`\n  and forwards them to the end-user to initiate the transfer.\n- **`payment_request` on-ramp** — the vendor sends a payment request (e.g. M-Pesa push) to the end-user.\n  The end-user's account details must be submitted here first so the vendor knows where to send the request.\n  Check `payment_request: true` in the order response to identify this type.\n\nCall this endpoint after initiating the order and before calling `confirm-payment-sent`.\nThe order must be in `awaiting_fiat_transfer` status.\n\nProvide either a `payment_details_id` (referencing a previously saved payment profile) or\nan inline `payment_details` object with the payer's account information.\n","operationId":"submitPayerDetails","parameters":[{"name":"order_id","in":"path","required":true,"description":"Order UUID returned by the initiate endpoint","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitPayerDetailsRequest"}}}},"responses":{"200":{"description":"Payer details submitted successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/APIResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"message":{"type":"string"}}}}}]}}}},"400":{"description":"Bad request — invalid or missing payer details","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":"Order not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerErrorResponse"}}}},"409":{"description":"Order is not in the correct status or not a payment_request order","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerErrorResponse"}}}}}}}}}
```

## Confirm fiat payment sent

> Confirm that the end-user has initiated the fiat payment to the vendor.\
> This advances the order from \`awaiting\_fiat\_transfer\` to \`fiat\_transfer\_pending\`,\
> signalling to the vendor that the fiat is on its way.\
> \
> For \`payment\_request\` orders, call \`submit-payer-details\` first.\
> No request body required.<br>

```json
{"openapi":"3.0.3","info":{"title":"Unigox API Gateway","version":"1.0.0"},"tags":[{"name":"On-Ramp","description":"On-ramp operations (fiat to crypto) — partner initiates a buy order, vendor provides liquidity, end-user sends 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":{"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"}}},"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- `KYC_NOT_CLEARED` — the customer is not KYC-cleared for this transaction; `error.details.kyc_status` carries their current partner-facing status\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","KYC_NOT_CLEARED"]},"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/orders/{order_id}/confirm-payment-sent":{"post":{"tags":["On-Ramp"],"summary":"Confirm fiat payment sent","description":"Confirm that the end-user has initiated the fiat payment to the vendor.\nThis advances the order from `awaiting_fiat_transfer` to `fiat_transfer_pending`,\nsignalling to the vendor that the fiat is on its way.\n\nFor `payment_request` orders, call `submit-payer-details` first.\nNo request body required.\n","operationId":"confirmPaymentSent","parameters":[{"name":"order_id","in":"path","required":true,"description":"Order UUID","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Payment confirmed successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/APIResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"order_id":{"type":"string","format":"uuid"},"status":{"type":"string"}}}}}]}}}},"400":{"description":"Bad Request","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":"Order not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerErrorResponse"}}}},"409":{"description":"Order is not in awaiting_fiat_transfer status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerErrorResponse"}}}}}}}}}
```

## Get send-out authorization parameters

> After an on-ramp order reaches \`completed\` status (crypto on master wallet), the partner\
> can optionally initiate a cross-chain send-out to transfer the USDT to an external network\
> (e.g. Arbitrum).\
> \
> This endpoint returns the EIP-712 \`ForwardRequest\` parameters required to authorize the\
> transfer. The partner must sign the request locally and submit the signature to\
> \`POST /api/v1/partner/orders/{order\_id}/authorize-bridge\`.\
> \
> \*\*Prerequisites:\*\*\
> \- Order status must be \`completed\` (internal: \`escrow\_released\_to\_buyer\`)\
> \- \`destination\_chain\` must be a supported chain slug (e.g. \`arbitrum\`, \`ethereum\`)\
> \- \`destination\_address\` must be a valid EVM address\
> \
> \*\*Response includes:\*\*\
> \- \`forward\_request\` — pre-filled EIP-712 ForwardRequest object to sign\
> \- \`eip712\_domain\` — domain parameters for EIP-712 signing\
> \- \`quote\_id\` — Relay quote ID embedded in the transfer calldata\
> \- \`to\_amount\` — estimated USDT amount to be received on the destination chain (atomic units)\
> \- \`forwarder\_nonce\` — current on-chain nonce for the master wallet<br>

```json
{"openapi":"3.0.3","info":{"title":"Unigox API Gateway","version":"1.0.0"},"tags":[{"name":"On-Ramp","description":"On-ramp operations (fiat to crypto) — partner initiates a buy order, vendor provides liquidity, end-user sends 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":{"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- `KYC_NOT_CLEARED` — the customer is not KYC-cleared for this transaction; `error.details.kyc_status` carries their current partner-facing status\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","KYC_NOT_CLEARED"]},"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/orders/{order_id}/bridge-authorization-parameters":{"get":{"tags":["On-Ramp"],"summary":"Get send-out authorization parameters","description":"After an on-ramp order reaches `completed` status (crypto on master wallet), the partner\ncan optionally initiate a cross-chain send-out to transfer the USDT to an external network\n(e.g. Arbitrum).\n\nThis endpoint returns the EIP-712 `ForwardRequest` parameters required to authorize the\ntransfer. The partner must sign the request locally and submit the signature to\n`POST /api/v1/partner/orders/{order_id}/authorize-bridge`.\n\n**Prerequisites:**\n- Order status must be `completed` (internal: `escrow_released_to_buyer`)\n- `destination_chain` must be a supported chain slug (e.g. `arbitrum`, `ethereum`)\n- `destination_address` must be a valid EVM address\n\n**Response includes:**\n- `forward_request` — pre-filled EIP-712 ForwardRequest object to sign\n- `eip712_domain` — domain parameters for EIP-712 signing\n- `quote_id` — Relay quote ID embedded in the transfer calldata\n- `to_amount` — estimated USDT amount to be received on the destination chain (atomic units)\n- `forwarder_nonce` — current on-chain nonce for the master wallet\n","operationId":"getBridgeAuthorizationParameters","parameters":[{"name":"order_id","in":"path","required":true,"description":"Order UUID","schema":{"type":"string","format":"uuid"}},{"name":"destination_chain","in":"query","required":true,"description":"Destination chain slug (e.g. `arbitrum`, `ethereum`)","schema":{"type":"string"}},{"name":"destination_address","in":"query","required":true,"description":"Recipient EVM address on the destination chain","schema":{"type":"string"}}],"responses":{"200":{"description":"Authorization parameters returned successfully","content":{"application/json":{}}},"400":{"description":"Invalid destination_chain or destination_address","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":"Order not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerErrorResponse"}}}},"409":{"description":"Order is not in `completed` status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerErrorResponse"}}}}}}}}}
```

## Authorize send-out (cross-chain transfer)

> Submit the signed EIP-712 ForwardRequest to initiate a cross-chain send-out.\
> The signature is verified on-chain via the XAI forwarder contract.\
> \
> Call \`GET bridge-authorization-parameters\` first to get the parameters to sign.\
> \
> After submission the order transitions to \`send\_out\_pending\`. A background worker\
> monitors the Relay bridge and updates the status to \`send\_out\_completed\` or\
> \`send\_out\_failed\` when the transfer settles. The partner receives a webhook for\
> each transition.\
> \
> When the send-out completes, the \`send\_out\_tx\_hash\` field in \`GET /orders/:id\`\
> contains the destination-chain transaction hash.\
> \
> \*\*This endpoint is idempotent at the on-chain level\*\* — the EIP-712 nonce prevents\
> duplicate submissions. A second call with the same nonce will be rejected.<br>

```json
{"openapi":"3.0.3","info":{"title":"Unigox API Gateway","version":"1.0.0"},"tags":[{"name":"On-Ramp","description":"On-ramp operations (fiat to crypto) — partner initiates a buy order, vendor provides liquidity, end-user sends 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":{"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- `KYC_NOT_CLEARED` — the customer is not KYC-cleared for this transaction; `error.details.kyc_status` carries their current partner-facing status\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","KYC_NOT_CLEARED"]},"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/orders/{order_id}/authorize-bridge":{"post":{"tags":["On-Ramp"],"summary":"Authorize send-out (cross-chain transfer)","description":"Submit the signed EIP-712 ForwardRequest to initiate a cross-chain send-out.\nThe signature is verified on-chain via the XAI forwarder contract.\n\nCall `GET bridge-authorization-parameters` first to get the parameters to sign.\n\nAfter submission the order transitions to `send_out_pending`. A background worker\nmonitors the Relay bridge and updates the status to `send_out_completed` or\n`send_out_failed` when the transfer settles. The partner receives a webhook for\neach transition.\n\nWhen the send-out completes, the `send_out_tx_hash` field in `GET /orders/:id`\ncontains the destination-chain transaction hash.\n\n**This endpoint is idempotent at the on-chain level** — the EIP-712 nonce prevents\nduplicate submissions. A second call with the same nonce will be rejected.\n","operationId":"authorizeBridge","parameters":[{"name":"order_id","in":"path","required":true,"description":"Order UUID","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["destination_chain","destination_address","forward_request","signature"],"properties":{"destination_chain":{"type":"string","description":"Destination chain slug (must match the value used in bridge-authorization-parameters)"},"destination_address":{"type":"string","description":"Recipient EVM address on the destination chain"},"forward_request":{"type":"object","description":"EIP-712 ForwardRequest object (values from bridge-authorization-parameters)","required":["from","to","value","gas","nonce","deadline","data"],"properties":{"from":{"type":"string"},"to":{"type":"string"},"value":{"type":"string"},"gas":{"type":"string"},"nonce":{"type":"string"},"deadline":{"type":"string"},"data":{"type":"string"}}},"signature":{"type":"string","description":"EIP-712 signature of the ForwardRequest (hex, with 0x prefix)"}}}}}},"responses":{"200":{"description":"Send-out submitted successfully","content":{"application/json":{}}},"400":{"description":"Invalid request — bad forward_request, nonce mismatch, or invalid destination","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":"Order not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerErrorResponse"}}}},"409":{"description":"Order is not in `completed` status or send-out already in progress","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerErrorResponse"}}}},"502":{"description":"Transactor service unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerErrorResponse"}}}}}}}}}
```
