# Buy & sell

**Status:** Available

Quote and route a crypto purchase or sale across every connected provider — one best executable route, with the fee breakdown shown up front, not discovered at checkout.

## Concept

A quote prices a side (buy or sell), an asset pair and an amount across every connected provider and returns the best executable route. An order is what you get when a quote is turned into an actual attempt to transact with that provider.

## Resources

| Object   | What it is                                                                                                                                                                                                |
| -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `quotes` | `rate`, `final_out`, `final_in`, `total_fees`, `expires_at`, plus the winning `offer_id`, every priced `offers[]` route, and terminal per-provider outcomes (`quoted`, `no_offer`, `paused`, `error`) from the same quote call. |
| `orders` | The attempt itself — `status` is the public lifecycle; **`money_state`** (`never_authorized` / `hold_placed` / `captured`) is the actual money truth, never inferred from `status` alone. Published only once a row has been classified — absent otherwise, and absence is never `never_authorized` or any other value. |

## Lifecycle

Quotes are `ready` or `indicative` and always carry an `expires_at` — re-quote rather than reuse a stale one. An order's public `status` is the one lifecycle field on the wire (`draft`/`quote_ready`/etc. is not a separate enum — the hand-off to a provider's own checkout is read from whether `provider_url` is present); `money_state`, when classified, is reconciled with it on the same object, never a second, disagreeing status field.

`checkout_readiness` carries provider-specific handoff and freshness facts. `ready` means the quote passed those checks; it does not guarantee the final provider price, verification eligibility or settlement. An indicative quote retains its actual `indicative_as_of`; re-quote live before acting.

`providers` contains one row per provider observed in the existing call, ordered by provider ID. A usable offer wins over a failure on another payment method for the same provider. Each quoted row includes its selectable `offer.offer_id`. A route with no usable offer returns `409 capability_unavailable`, with safe terminal outcomes in `error.details.providers` when available. Raw diagnostics and provider payloads are private.

`offers[]` contains every fully priced route from that fan-out, including sibling payment methods. Each entry binds the
canonical `payment_method` when known and opaque `offer_id` to its provider, readiness, expiry, resolved limits and
non-PII `execution_context` (assets, amount side, and supplied route selectors). Use entries with a known method when
presenting or selecting a method; unknown methods remain visible but are never inferred. Capabilities are not substituted
for missing quotes.

`rate` — on the winner, every `offers[]` entry and every `providers[].offer` echo — is each offer's own `final_in`
decimal value ÷ `final_out` decimal value, computed BEFORE either is rounded into the published `Money` fields, one
basis (`from_asset` per `to_asset`) computed the same way for every offer regardless of provider or side. Because
the derivation runs pre-rounding, `rate` × the published `final_out` amount can differ from the published `final_in`
amount in the last digit(s) when an asset's published decimals are coarser than the server's own internal
precision — treat that cross-check as approximate, not exact. It is informational only: never rank or compare offers by `rate`. For a
`from_amount` request, compare by `final_out` instead — every offer targets a different `final_out` for the same pay
amount, so the largest wins. For a `to_amount` request, compare by `final_in` only among offers whose `final_out`
equals the requested amount — the lowest pay amount among those wins. An offer whose `final_out` differs from the
requested amount is not comparable this way: not every provider honors an exact-output target.

The winner and each provider offer may include `payment_method`, the canonical method priced
for that specific `offer_id`. Preserve both when presenting or selecting an offer. A different
offer from the same provider can use another method; missing `payment_method` means unknown,
so do not copy it from the request or winning offer. The field describes the priced payment
intent and does not establish customer eligibility or guarantee the eventual settlement rail.

For Transak offers, specify the crypto network (`to_network` when buying, `from_network`
when selling). An unspecified network cannot authorize its checkout. Other usable provider
offers remain available; when none remain, `409 capability_unavailable` includes
`QUOTE_NETWORK_REQUIRED` and the network field to provide.

Anonymous quoting stays on the existing public widget transport. Test-mode `/v1/quotes` requests currently return `503 temporarily_unavailable` before any provider request; sandbox dispatch remains pending.

When sent, `country` must be an uppercase ISO 3166-1 alpha-2 code; a comprehensively-sanctioned or unsupported one is refused with `409 capability_unavailable` before pricing runs, even when `country` is omitted and only inferred from a forwarded geo header.

## Minimal flow

1. Authenticate with a business key or bearer holding `orders.write`, then call `POST /v1/quotes` — or the MCP tool `get_quote` — with side, assets, amount, `to_network` (required later to create a Bridge-native order from this quote) and (when known) country and payment method.
2. Present the route and its fees to the person; quotes expire, so don't hold one past its `expires_at`. A quote or selectable `offer_id` alone never authorizes execution — the person must see the route and its fees first (see [What an agent can and cannot do here](#what-an-agent-can-and-cannot-do-here) below).
3. Create the order: `POST /v1/orders` with `quote_id`, the chosen `offer_id`, and `wallet_address` — required for a Bridge-native buy. A test-mode (`sk_test_`) key cannot create an order — `orders.create` answers `503 temporarily_unavailable` ("Test-mode orders are not available yet") before any other check, and releases the idempotency reservation; see [Test mode](/test-mode). Step 3 is a live-key call that moves real money. It completes in place, landing directly on an active `status`, with no separate hand-off page. This release only a **Bridge-native BUY** offer creates this way: check the offer's own `checkout_readiness.handoff_mode` before calling create — only `backend_native` is live here today. Every other selectable offer is a typed, non-retryable refusal, not a bug:
   - A quote priced without `to_network` cannot become a Bridge-native order: answers `409 capability_unavailable` (`param: quote_id`), "This quote has no to_network — request a new quote with to_network set before creating a Bridge-native order". Re-quote with `to_network` set and retry.
   - A **Bridge-native SELL** offer (same `handoff_mode`, `side: sell`) answers `409 capability_unavailable` (code `capability_unavailable`), "Bridge-native sell orders are not created through this endpoint yet" — a Sell's payout destination has no field on `OrderCreateRequest` yet. Never retry as-is.
   - Any **hosted-provider** offer — `checkout_readiness.handoff_mode` other than `backend_native` — answers `409 capability_unavailable` (code `capability_unavailable`), "`<Provider>` checkout is not available through this endpoint yet". Never retry as-is; a future release returns a `provider_url` to open instead.
   - A **Paybis** offer answers `409 capability_unavailable` (code `provider_paused`) — Paybis's checkout path is frozen pending a product/security review, defense-in-depth on top of `POST /v1/quotes` already excluding Paybis from every response. Never retry as-is.

   The `wallet_address` itself can also be refused by destination screening: `403 permission_error` (code `destination_blocked`) on a flagged address — never retry as-is — or `503 temporarily_unavailable` (code `destination_check_unavailable`) when screening is itself unavailable and the fail-closed policy blocks the create — retry after the response's `Retry-After`.

   Two more answers are conflicts, not bugs, and worth telling apart from an actual defect: a **stale offer** — past its own `expires_at` or `checkout_readiness.checkout_fresh_until` — answers `409 conflict` (code `requote_required`); request a fresh quote and create the order from the new `offer_id`. A create that **reconciled onto an already-existing order** — made by a prior attempt under a different `Idempotency-Key`, recovered through Bridge's own provider-side idempotency key, rather than creating a second one — still answers `201`, but carries `Idempotent-Replayed: true`; read that header before booking this call as having made a new order. See [Errors](/errors) for the full code reference.
4. `GET /v1/orders/{id}` — read `money_state`, when present, before ever telling a user money moved.

> **Never conclude money moved from the status label alone — or from money_state's absence**
>
> `get_order_status` and `get_payment` both say this explicitly: a classified order can read `completed` while
> `money_state` reads `never_authorized`. `money_state` itself is published only once a row is classified — most
> rows never get one, so an ABSENT `money_state` means "never classified," not "not charged." Read the field's
> presence and value together; never the display status, and never treat absence as a value.

## What an agent can and cannot do here

`get_quote` prices a route; nothing about pricing a quote authorizes spending. An order is created from a quote, never chained straight from a quote response without the person seeing the route and its fees first — the same human-in-the-loop rule every money-preparation tool follows (see [Agents & MCP](/agents-mcp#what-a-tool-can-never-do)).

Next: the [reference](/reference) for the full quote and order schemas · [Providers & coverage](/providers-coverage) for which providers and countries are actually live.
