curl -X POST https://checkout.noxpay.io/v2/rfq/accept \
-H "api-key: <key>" \
-H "Content-Type: application/json" \
-d '{
"rfq_id": "NOXabc123...",
"settlement_day": "d0",
"wallet_id": "..."
}'
import requests
response = requests.post(
"https://checkout.noxpay.io/v2/rfq/accept",
headers={
"api-key": "<key>",
"Content-Type": "application/json",
},
json={
"rfq_id": "NOXabc123...",
"settlement_day": "d0",
"wallet_id": "...",
},
)
print(response.json())
const response = await fetch("https://checkout.noxpay.io/v2/rfq/accept", {
method: "POST",
headers: {
"api-key": "<key>",
"Content-Type": "application/json",
},
body: JSON.stringify({
rfq_id: "NOXabc123...",
settlement_day: "d0",
wallet_id: "...",
}),
});
const data = await response.json();
{
"rfq_id": "NOXabc123...",
"state": "DONE",
"amount_from": 1000.0,
"amount_to": 5050.0,
"rate": 5.050,
"quote_pair": "USDT (TRX)/BRL (PIX)",
"settlement_eta": "2026-07-01T19:00:00Z"
}
{ "state": "EXPIRED", "reason": "Rate expired and was renewed — fetch the latest quote before retrying" }
{ "state": "DEPRECATED", "reason": "Renewal window closed — create a new conversion" }
{ "state": "REFUSED", "reason": "Insufficient balance" }
{ "state": "QUOTE", "reason": "forex rate unavailable" }
Conversions
Accept Conversion
POST /v2/rfq/accept — accept an existing quote, choosing the settlement day and destination.
curl -X POST https://checkout.noxpay.io/v2/rfq/accept \
-H "api-key: <key>" \
-H "Content-Type: application/json" \
-d '{
"rfq_id": "NOXabc123...",
"settlement_day": "d0",
"wallet_id": "..."
}'
import requests
response = requests.post(
"https://checkout.noxpay.io/v2/rfq/accept",
headers={
"api-key": "<key>",
"Content-Type": "application/json",
},
json={
"rfq_id": "NOXabc123...",
"settlement_day": "d0",
"wallet_id": "...",
},
)
print(response.json())
const response = await fetch("https://checkout.noxpay.io/v2/rfq/accept", {
method: "POST",
headers: {
"api-key": "<key>",
"Content-Type": "application/json",
},
body: JSON.stringify({
rfq_id: "NOXabc123...",
settlement_day: "d0",
wallet_id: "...",
}),
});
const data = await response.json();
{
"rfq_id": "NOXabc123...",
"state": "DONE",
"amount_from": 1000.0,
"amount_to": 5050.0,
"rate": 5.050,
"quote_pair": "USDT (TRX)/BRL (PIX)",
"settlement_eta": "2026-07-01T19:00:00Z"
}
{ "state": "EXPIRED", "reason": "Rate expired and was renewed — fetch the latest quote before retrying" }
{ "state": "DEPRECATED", "reason": "Renewal window closed — create a new conversion" }
{ "state": "REFUSED", "reason": "Insufficient balance" }
{ "state": "QUOTE", "reason": "forex rate unavailable" }
Accepts a quote created via Create Conversion, locking in the rate and scheduling delivery. The balance in
Response
This is the one endpoint in the API that returns
When a
from_currency must be available at the moment of accept — it is not reserved when the quote is created.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
rfq_id | string | Yes | ID returned by Create Conversion |
settlement_day | string | Yes | "d0" or "d1" |
wallet_id | string | Conditional | Destination crypto wallet ID (crypto payout) |
pix_key | string | Conditional | Destination Pix key (BRL payout) |
Response 200 OK (accepted)
| Field | Type | Description |
|---|---|---|
rfq_id | string | ID of the conversion |
state | string | DONE once accepted |
amount_from | float | Confirmed amount sent |
amount_to | float | Confirmed amount to be received |
rate | float | Confirmed rate |
quote_pair | string | Quote pair, as <from display name>/<to display name> — display names, not currency codes (e.g. USDT (TRX)/BRL (PIX)) |
settlement_eta | string | Confirmed settlement time (RFC 3339) |
wallet_id | string | Destination wallet, echoed back when the payout is crypto |
pix_key | string | Destination Pix key, echoed back when the payout is BRL |
reason | string | Empty on 200; carries the explanation on a refusal or failure |
state, because state is what carries the outcome of the accept. Everywhere else, read status / substatus instead.
A 200 here means the trade is locked and delivery is scheduled — it does not mean funds have already landed. Delivery completes at settlement_eta; see Get Conversion or the webhook to track it.
Outcomes
POST /v2/rfq/accept is the only committing operation in the conversion flow, so it has the widest set of outcomes. Every one of them is listed here.
| HTTP | State | Meaning | Retry? |
|---|---|---|---|
200 OK | DONE | Accepted. amount_from, amount_to, rate, settlement_eta are populated | Done |
202 Accepted | — | Still being evaluated — this is not an acceptance. reason is deliberately empty: it is a wait, not a refusal | Yes, by re-reading |
400 Bad Request | — | Invalid JSON, unknown field, empty rfq_id, or settlement_day outside d0 / d1 | No |
401 Unauthorized | — | Missing or invalid api-key | No |
404 Not Found | — | The conversion does not exist, or belongs to another account. The two cases are deliberately indistinguishable | No |
409 Conflict | EXPIRED | The rate expired and the quote was renewed — re-read the conversion and resubmit with the new rate | Yes, after re-reading |
410 Gone | DEPRECATED | The renewal window closed — create a new conversion | No |
422 Unprocessable Entity | REFUSED or ERROR | Refused. reason carries the cause: insufficient balance, D0 unavailable, or a limit ceiling | No |
503 Service Unavailable | — | The rate provider is unavailable. Nothing was decided — the conversion is still in QUOTE and can be accepted again | Yes |
500 Internal Server Error | — | Failed to re-read the conversion or its attributes | Sometimes |
A
202 is not a success. It means the accept is still being evaluated and no decision has been recorded. Do not treat it as confirmation and do not resubmit the accept — re-read Get Conversion or wait for the webhook until you reach a terminal outcome.422 is caused by a limit ceiling rather than balance, GET /v2/rfq/{id} exposes limit_block_source to tell you which gate blocked it — operational for the desk ceiling, compliance for the KYB tier. The field is absent when the refusal was for insufficient balance.
503 and 202 are safe to retry. 409 needs a re-read first. 410 and 422 are final for that rfq_id.
Status codes: 200, 202, 400, 401, 404, 409, 410, 422, 500, 503
curl -X POST https://checkout.noxpay.io/v2/rfq/accept \
-H "api-key: <key>" \
-H "Content-Type: application/json" \
-d '{
"rfq_id": "NOXabc123...",
"settlement_day": "d0",
"wallet_id": "..."
}'
import requests
response = requests.post(
"https://checkout.noxpay.io/v2/rfq/accept",
headers={
"api-key": "<key>",
"Content-Type": "application/json",
},
json={
"rfq_id": "NOXabc123...",
"settlement_day": "d0",
"wallet_id": "...",
},
)
print(response.json())
const response = await fetch("https://checkout.noxpay.io/v2/rfq/accept", {
method: "POST",
headers: {
"api-key": "<key>",
"Content-Type": "application/json",
},
body: JSON.stringify({
rfq_id: "NOXabc123...",
settlement_day: "d0",
wallet_id: "...",
}),
});
const data = await response.json();
{
"rfq_id": "NOXabc123...",
"state": "DONE",
"amount_from": 1000.0,
"amount_to": 5050.0,
"rate": 5.050,
"quote_pair": "USDT (TRX)/BRL (PIX)",
"settlement_eta": "2026-07-01T19:00:00Z"
}
{ "state": "EXPIRED", "reason": "Rate expired and was renewed — fetch the latest quote before retrying" }
{ "state": "DEPRECATED", "reason": "Renewal window closed — create a new conversion" }
{ "state": "REFUSED", "reason": "Insufficient balance" }
{ "state": "QUOTE", "reason": "forex rate unavailable" }

