# Testing & stubs

Every operation is served by a stub that returns **the specification's own
canonical example**. The stub bodies and the documented examples are the
same bytes. One host, two path prefixes:

| | |
| --- | --- |
| [Nexa endpoints exposed to Tesser](/api-nexa) | `https://nexa-poc-main-c82e5ac.zuplo.app/nexa/v1/...` |
| [Tesser endpoints exposed to Nexa](/api-tesser) | `https://nexa-poc-main-c82e5ac.zuplo.app/tesser/v1/...` |

That host is already selected in each API reference, so the copy-paste
`curl` samples there point straight at it.

To exercise the whole flow in one command, use the
[end-to-end test](/e2e-test).

## What the stubs are not

- **Stateless.** No sequencing is enforced. You can report a deposit against
  a quote that was never created and the stub will answer happily.
- **Unauthenticated.** No mTLS, no API keys. The
  [real auth model](/authentication) applies to the real implementations,
  not to the stub host.
- **Fixed.** Responses are always the canonical example, with the worked
  amounts: `250000.00` GTQ at `7.745100` GTQ/USDC = `32278.472841` USDC.

They exist so both teams can write and run client code against a real HTTP
endpoint before either side has an implementation. They are not a simulator.

## Magic error id

**An id ending in `dead` makes a stub return that operation's primary
error** instead of the happy path. There is one canned error variant per
operation, the one its error example in the API reference shows.

The canonical value is `9b2f6c1e-0000-4000-8000-00000000dead`. Use it as the
`tesser_quote_id` on the quote, deposit and payment operations, or as the
`nexa_payment_id` on [get payment](/api-nexa/payments#get-payment-optional-op-9)
<N />.

Every other error below is documented and schema'd in the API references,
but only the primary one per operation is reachable from a stub. Being
stateless, it cannot distinguish the conditions that produce the others.

## Error catalogue

The same envelope on both sides, on every operation:

```json
{
  "errors": [
    { "error_code": "QUOTE_EXPIRED", "error_message": "Quote expired at 2026-10-14T15:05:00Z." }
  ],
  "request_id": "req_01JA8F3K2M7Q"
}
```

<div className="error-table">

| `error_code` | HTTP | Operations |
| --- | --- | --- |
| `VALIDATION_ERROR` | 400 | all |
| `UNAUTHENTICATED` | 401 | all |
| `QUOTE_NOT_FOUND` | 404 | accept, deposits, payments, get quote |
| `PAYMENT_NOT_FOUND` | 404 | get payment |
| `NO_LIQUIDITY` | 422 | create quote |
| `QUOTE_CONFLICT` | 409 | create quote (same `tesser_quote_id`, different body) |
| `QUOTE_EXPIRED` | 409 | accept, deposits (quote in `EXPIRED`) |
| `QUOTE_NOT_ACCEPTED` | 409 | deposits (quote still `CREATED`) |
| `QUOTE_REJECTED` | 409 | deposits, payments (quote in `REJECTED`) |
| `DEPOSIT_CONFLICT` | 409 | deposits (same `tesser_deposit_id`, different body) |
| `QUOTE_NOT_CREDITED` | 409 | payments (credit not yet `CREDIT_CONFIRMED`) |
| `AMOUNT_MISMATCH` | 422 | payments (`gtq_amount` ≠ credited amount) |
| `PAYMENT_CONFLICT` | 409 | payments (quote already has a different payment) |
| `INTERNAL_ERROR` | 500 | all |

</div>

The two vendored Tesser core-API operations
([get account](/api-tesser/accounts#get-an-account-by-id) <T />,
[create withdrawal](/api-tesser/treasury#create-withdrawal) <T />) keep the
core API's own error codes (`api-0001`, `api-0004`, `api-0006`,
`counterparties-300x`) rather than this catalogue. Their contract is
already live and is not being redefined here.
