0Bit Documentation

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

FieldValue
MethodPOST
Path/v1/quotes
AreaRails
Operation idlockQuote
Auth boundarySecret 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. Publishable pk_* 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 LockedQuote redeems 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

FieldRequiredTypeUse it for
currencyYesstring (ISO 4217)Three-letter fiat code, for example EUR. Pattern ^[A-Za-z]{3}$.
assetYesstringCrypto asset symbol, for example USDC. Pattern ^[A-Za-z0-9]{2,12}$.
amountYesdecimal stringPositive amount, up to 8 decimal places. Always a string, never a float.
sideYeson_ramp/off_rampDirection of the conversion. on_ramp buys crypto with fiat; off_ramp sells crypto for fiat.
payment_methodYesstring (max 64)The method to lock pricing for, for example sepa_credit_transfer. Pick one from the matching preview entry.
country_codeNostring (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.

FieldTypeUse it for
objectstringAlways signed_quote.
idstringThe locked-quote id. Pass it as quote_id to a pay-in or pay-out, and as quote_preview_id to a session.
statusactive/consumed/expiredRedemption state. active is redeemable; consumed was already redeemed; expired lapsed.
sideon_ramp/off_rampEchoes the locked direction.
currencystringLocked fiat code.
assetstringLocked crypto asset.
payment_methodstringLocked method.
fiat_amountdecimal stringFiat leg of the locked conversion.
crypto_amountdecimal stringCrypto leg of the locked conversion.
exchange_ratedecimal stringLocked all-in rate for the chosen side.
feesobjectFee breakdown: spread, fixed, total (all decimal strings).
fiat_pay_or_receivedecimal stringThe fiat the user pays (on_ramp) or receives (off_ramp).
usd_amountdecimal stringUSD-normalised value of the conversion.
signaturestringHMAC-SHA256 over the canonical quote payload, format t=<unix_seconds>,v1=<hex>. Verify with your quote-signing secret.
expires_atstring (date-time)When the lock lapses. Redeem before this time.
created_atstring (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
}
StatustypeWhen it happens
400invalid_requestBad body — missing a required field, a malformed amount, or an unknown currency/asset/method.
401unauthorizedMissing or invalid secret key.
403forbiddenCredential rejected, mode mismatch, or account-gated rail access is not enabled for this key.
409conflictState conflict — for example a quote already consumed.
429rate_limitedRequest throttled. Back off and retry.
5xxserver_errorTransient 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.

On this page