Lock a signed quote
POST /quotes - Lock a one-time quote for entitled server-side rail flows.
0Gate is the primary public integration path for hosted payment, ramp, and swap experiences. Keep secret-key operations on your server and hand only browser-safe values to the widget.
Endpoint
| Field | Value |
|---|---|
| Method | POST |
| Path | /v1/quotes |
| Area | Rails |
| Operation id | lockQuote |
| Auth boundary | Secret key from your server. |
Use it for
Lock a rate for a single, server-side rail flow. The call returns an HMAC-signed, one-time-redeemable LockedQuote: its id is the quote_id you pass to POST /rails/pay_ins or POST /rails/pay_outs, and it can also be passed as quote_preview_id when creating a hosted session. The rate holds for a short configured TTL. This route requires account-gated rail access.
Use this endpoint only for the partner-scoped resource it describes. Store your own reference id, the returned quote id, the request id, timestamps, and the current status so support and reconciliation do not depend on browser callbacks alone.
Production rules
- Keep secret keys on your server. This endpoint requires a
sk_*key. Publishablepk_*keys are browser-only and cannot lock quotes. - Validate environment, mode, entitlement, asset, network, and amount before the call.
- Treat the lock as single-use. Each
LockedQuoteredeems exactly once; a second redemption is a conflict. - Branch on machine-readable status, error code, object id, and request id.
- Treat examples and placeholder ids as fake data only.
Request body
| Field | Required | Type | Use it for |
|---|---|---|---|
currency | Yes | string (ISO 4217) | Three-letter fiat code, for example EUR. Pattern ^[A-Za-z]{3}$. |
asset | Yes | string | Crypto asset symbol, for example USDC. Pattern ^[A-Za-z0-9]{2,12}$. |
amount | Yes | decimal string | Positive amount, up to 8 decimal places. Always a string, never a float. |
side | Yes | on_ramp/off_ramp | Direction of the conversion. on_ramp buys crypto with fiat; off_ramp sells crypto for fiat. |
payment_method | Yes | string (max 64) | The method to lock pricing for, for example sepa_credit_transfer. Pick one from the matching preview entry. |
country_code | No | string (ISO 3166-1) | Two-letter region used to resolve corridor support. Pattern ^[A-Z]{2}$. |
Money and rate values are strings
Every monetary value in the request and the returned quote is a decimal string, never a float. Fee and rate fields are strings too.
Response
201 returns a signed LockedQuote (object: signed_quote). It captures the locked economics, the redemption status, and the signature you can verify out-of-band.
| Field | Type | Use it for |
|---|---|---|
object | string | Always signed_quote. |
id | string | The locked-quote id. Pass it as quote_id to a pay-in or pay-out, and as quote_preview_id to a session. |
status | active/consumed/expired | Redemption state. active is redeemable; consumed was already redeemed; expired lapsed. |
side | on_ramp/off_ramp | Echoes the locked direction. |
currency | string | Locked fiat code. |
asset | string | Locked crypto asset. |
payment_method | string | Locked method. |
fiat_amount | decimal string | Fiat leg of the locked conversion. |
crypto_amount | decimal string | Crypto leg of the locked conversion. |
exchange_rate | decimal string | Locked all-in rate for the chosen side. |
fees | object | Fee breakdown: spread, fixed, total (all decimal strings). |
fiat_pay_or_receive | decimal string | The fiat the user pays (on_ramp) or receives (off_ramp). |
usd_amount | decimal string | USD-normalised value of the conversion. |
signature | string | HMAC-SHA256 over the canonical quote payload, format t=<unix_seconds>,v1=<hex>. Verify with your quote-signing secret. |
expires_at | string (date-time) | When the lock lapses. Redeem before this time. |
created_at | string (date-time) | When the quote was locked. |
The quote id is the rail key
The returned id is the only thing a rail call needs — amount, currency, asset, and method are derived authoritatively from the locked quote, never re-sent. See Create a pay-in.
Examples
curl -X POST https://gate-api.0bit.app/v1/quotes \
-H "Authorization: Bearer sk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"currency": "EUR",
"asset": "USDC",
"amount": "1000.00",
"side": "on_ramp",
"payment_method": "sepa_credit_transfer",
"country_code": "DE"
}'{
"object": "signed_quote",
"id": "qt_test_9f8e7d6c5b4a",
"status": "active",
"side": "on_ramp",
"currency": "EUR",
"asset": "USDC",
"payment_method": "sepa_credit_transfer",
"fiat_amount": "1000.00",
"crypto_amount": "1067.40",
"exchange_rate": "1.0674",
"fees": {
"spread": "5.30",
"fixed": "0.50",
"total": "5.80"
},
"fiat_pay_or_receive": "1000.00",
"usd_amount": "1073.10",
"signature": "t=1767225600,v1=4a7f...e9b2",
"expires_at": "2026-01-01T00:00:30Z",
"created_at": "2026-01-01T00:00:00Z"
}Single-use. Store id and expires_at, then redeem once via POST /rails/pay_ins (or pay_outs) before the lock lapses.
Errors
All errors use the unified envelope and carry an X-Request-Id response header. Branch on code/type/statusCode, not on the free-form message.
{
"type": "forbidden",
"code": "forbidden",
"message": "Example authorization error using fake data.",
"request_id": "req_test_000000000123",
"doc_url": null,
"statusCode": 403
}| Status | type | When it happens |
|---|---|---|
400 | invalid_request | Bad body — missing a required field, a malformed amount, or an unknown currency/asset/method. |
401 | unauthorized | Missing or invalid secret key. |
403 | forbidden | Credential rejected, mode mismatch, or account-gated rail access is not enabled for this key. |
409 | conflict | State conflict — for example a quote already consumed. |
429 | rate_limited | Request throttled. Back off and retry. |
5xx | server_error | Transient server or upstream failure. Retry with bounded backoff. |
Public boundary
This reference covers partner-scoped endpoint behavior, authentication, idempotency, webhook verification, and support-safe records. Internal operations, settlement venues, provider details, administrative routes, and unsupported availability claims are outside the public API contract.