# 1 · Quote & accept

Phase 1 of the flow. Felix asks Tesser (via Slack) for GTQ; Tesser asks Nexa
for a price, shows it to Felix, and locks it when Felix says yes. Both
operations are <N /> **Nexa-hosted, and Tesser calls them**.

<Mermaid
  chart={`sequenceDiagram
    autonumber
    participant T as Tesser (orchestrator)
    participant N as Banco Nexa

    Note over T,N: Phase 1 · Quote
    T->>N: POST /v1/quotes [N]
    N-->>T: 201 CREATED (price, quoted_gtq, usdc_amount, expires_at)
    T->>N: POST /v1/quotes/{tesser_quote_id}/accept [N]
    N-->>T: 200 ACCEPTED (deposit_deadline)`}
/>

## Step 1 · Create the quote <N />

**Operation:** [`POST /v1/quotes`: Create quote (op 1)](/api-nexa/quotes#create-quote-op-1)
· hosted by **Nexa**, called by **Tesser**.

Tesser mints `tesser_quote_id` (UUIDv4) *before* the call. It is both the
identifier every later call and webhook uses, and the idempotency key. The
request carries the customer (`FELIX` at launch) and `desired_gtq`, the GTQ
Felix wants to end up with.

Nexa replies with the price and with `usdc_amount`, **the USDC Felix must
deliver**, computed as `quoted_gtq / usdc_gtq_price`, rounded **up** at 6dp
so that delivering exactly `usdc_amount` always covers `quoted_gtq`. Tesser
echoes that number and never derives it locally: with two calculators the
sides can disagree in the sixth decimal.

- `quoted_gtq` may be **less than** `desired_gtq`. That is a partial fill
  from limited liquidity. Accepting accepts `quoted_gtq`; the remainder is a
  new quote.
- `expires_at` is the acceptance deadline. Unaccepted quotes expire
  **silently**: there is no expiry webhook. Tesser knows `expires_at` and
  times out locally.
- Retrying with the same `tesser_quote_id` and an equivalent body returns
  `201` with the quote's **current** state. The same ID with a different body
  is `409 QUOTE_CONFLICT`.

How long a window Nexa can offer, and whether it is fixed or
liquidity-dependent, is [open question 1](/open-questions).

<div className="what-can-go-wrong">

**What can go wrong: create quote**

| `error_code` | HTTP | When |
| --- | --- | --- |
| `VALIDATION_ERROR` | 400 | Malformed amount, unknown `customer`, bad UUID. |
| `UNAUTHENTICATED` | 401 | Client certificate or customer API key missing/invalid. |
| `QUOTE_CONFLICT` | 409 | Same `tesser_quote_id`, different body. |
| `NO_LIQUIDITY` | 422 | Nexa cannot quote any amount right now. |
| `INTERNAL_ERROR` | 500 | Unexpected failure on Nexa's side. |

</div>

## Step 2 · Accept the quote <N />

**Operation:** [`POST /v1/quotes/{tesser_quote_id}/accept`: Accept quote (op 2)](/api-nexa/quotes#accept-quote-op-2)
· hosted by **Nexa**, called by **Tesser**.

The body is empty (`{}`): the path identifies the quote, and acceptance is of
`quoted_gtq` by definition. Accepting locks the rate and starts the
`deposit_deadline` clock.

- `deposit_deadline` is how long Nexa holds the locked rate waiting for the
  **first** deposit report. After the deadline with nothing reported, the
  quote is `EXPIRED` and later calls get `409 QUOTE_EXPIRED`.
- Re-accepting an already-`ACCEPTED` (or later-state) quote returns `200`
  with the quote's current object. It is idempotent, not an error, so a retry
  after a network blip can never false-fail.
- An unknown `tesser_quote_id` is `404 QUOTE_NOT_FOUND`.

The deadline length, and whether an additional-funds request extends it, is
[open question 2](/open-questions).

<div className="what-can-go-wrong">

**What can go wrong: accept quote**

| `error_code` | HTTP | When |
| --- | --- | --- |
| `VALIDATION_ERROR` | 400 | Malformed path parameter or body. |
| `UNAUTHENTICATED` | 401 | Client certificate or customer API key missing/invalid. |
| `QUOTE_NOT_FOUND` | 404 | No quote with this `tesser_quote_id`. |
| `QUOTE_EXPIRED` | 409 | `expires_at` passed before acceptance. |
| `INTERNAL_ERROR` | 500 | Unexpected failure on Nexa's side. |

</div>

## Recovering state

If Tesser loses track of a quote, the optional
[`GET /v1/quotes/{tesser_quote_id}`](/api-nexa/quotes#get-quote-optional-op-8)
<N /> answers "what state do *you* think it's in?" in one call.

**Next:** [2 · Deposit reporting](/flow-nexa/deposit-reporting).
