Quick start
Felix Pago sells USDC to Banco Nexa for GTQ. Tesser orchestrates: it quotes with Nexa, reports the USDC arriving at Nexa's BitGo custody, and instructs the GTQ payout. Felix and Nexa never talk to each other.
Scope of this integration: one fixed beneficiary, no last mile
Nexa always pays the same beneficiary, agreed out-of-band and never present in an API payload. That beneficiary is the payout partner, which performs the last-mile payout to the individual remittance receivers.
Nexa is not involved in the last mile. There are no per-recipient beneficiary details anywhere in this contract, no destination objects and no beneficiary compliance fields, because every payout goes to the same place. Nexa taking on last-mile remittance payouts is a later stage of the programme, out of scope here.
The parties
| Party | Role in this integration |
|---|---|
| Banco Nexa | Quotes the rate, buys the USDC, credits the GTQ and executes the payout. Hosts the quote, deposit and payment API. |
| Tesser | Orchestrator. Mints every quote, watches Nexa's BitGo custody wallet, reports deposits with risk screening, instructs the payout. Hosts the webhook receiver Nexa calls, plus the core API Nexa uses for treasury. |
| Felix Pago | The remitter buying GTQ. Interacts through Tesser's Slack bot at launch. Felix never calls Nexa. |
All ten operations
Throughout the portal, [N] means Nexa hosts it, Tesser calls it and [T] means Tesser hosts it, Nexa calls it. Direction below is caller → receiver.
| # | Operation | Direction | Purpose |
|---|---|---|---|
| 1 | POST /v1/quotes [N] | T→N | Tesser requests a GTQ quote for a customer |
| 2 | POST /v1/quotes/{tesser_quote_id}/accept [N] | T→N | Lock the quote |
| 3 | POST /v1/quotes/{tesser_quote_id}/deposits [N] | T→N | Tesser reports USDC received at custody (+ risk screening) |
| 4 | POST/HEAD /v1/webhooks/nexa/{organizationId} [T] | N→T | Nexa → Tesser events: credit decisions, payout failures |
| 5 | POST /v1/payments [N] | T→N | Instruct GTQ payout to the fixed beneficiary |
| 6 | GET /v1/accounts/{id} [T] | N→T | Existing core API: Nexa's USDC balance |
| 7 | POST /v1/treasury/withdrawals [T] | N→T | Existing core API: withdraw USDC |
| 8 | GET /v1/quotes/{tesser_quote_id} [N] (optional) | T→N | Reconciliation / state recovery |
| 9 | GET /v1/payments/{nexa_payment_id} [N] (optional) | T→N | Reconciliation / state recovery |
| 10 | POST /v1/webhooks/tesser [N] | T→N | Tesser → Nexa events: withdrawal status (supports op 7) |
"Optional" on 8 and 9 refers to Nexa's implementation obligation only. Both are fully specified, stubbed and tested here either way.
The four connections
Those ten operations ride on just four connections. Two parties, two kinds of traffic (API calls and webhooks), each going both ways. That is the whole integration.
Get those four working, one call each, and everything else is more of the same.
Build them in this order
Each milestone is one working call, end to end, in the real security model. Later milestones reuse the credentials the earlier ones prove out, so the order is not arbitrary.
| # | Connection | Direction | The one call that proves it |
|---|---|---|---|
| 1 | API call, Tesser to Nexa, over mTLS | T→N | Create quote [N] returns 201 |
| 2 | Webhook, Nexa to Tesser | N→T | Receive Nexa webhook [T] returns 2xx |
| 3 | API call, Nexa to Tesser | N→T | Get an account by ID [T] returns 200 |
| 4 | Webhook, Tesser to Nexa | T→N | Receive Tesser webhook [N] returns 2xx |
1 · mTLS, then create a quote [N]
The first milestone is the certificate exchange, not the endpoint. Everything else in this integration is blocked on it, so do it first and prove it with the simplest call in the contract.
- Nexa stands up
POST /v1/quotesbehind mTLS, and decides how to validate Tesser's client certificate: pin the leaf fingerprint, or validate against a Tesser-provided issuing CA. Nexa also issues Felix's Nexa API key. - Tesser presents that client certificate plus
Authorization: Bearer <felix_api_key>on every call.
Done when: one POST /v1/quotes returns 201 with a price,
quoted_gtq and usdc_amount over a mutually authenticated connection.
Details: Authentication · 1 · Quote & accept
2 · Nexa sends a webhook to Tesser [T]
Every Nexa decision in this flow is asynchronous, so this leg carries more of the integration than any other single piece.
- Nexa registers its client certificate's SHA-256 fingerprint against
its Tesser API key during onboarding, then delivers to
POST /v1/webhooks/nexa/{organizationId}. - Check connectivity first with
HEAD /v1/webhooks/nexa/{organizationId}[T], which uses the same path and the same auth, with no body and no event consumed.
Done when: one real event is delivered and acked 2xx.
quote.credit.sent is the natural choice.
Details: Webhooks guide · 3 · Credit decision
3 · Nexa reads its USDC balance [T]
This is an existing, live Tesser core-API endpoint, and it reuses exactly the certificate milestone 2 registered, so it is the cheapest possible confirmation that Nexa's outbound credentials work for API calls and not just webhook posts.
- Nexa calls
GET /v1/accounts/{id}with the wallet account ID shared during onboarding.
Done when: one 200 with the account's USDC balance.
Details: 5 · Treasury
4 · Tesser sends a webhook to Nexa [N]
Last, because it is the only leg with new Tesser platform work behind it: Tesser's webhook sender signs with Ed25519 today and is being extended to present a client certificate for Nexa deliveries.
- Nexa stands up
POST /v1/webhooks/tesserand subscribes to withdrawal status events.
Done when: one withdrawal-status event is delivered and acked 2xx.
Details: Authentication · Webhooks guide
Then: the rest of the flow
With the four connections proven, every remaining operation is one of those same two patterns, so the work is the flow rather than the plumbing. Pick them up at accept quote [N] and walk them in order on the flow pages. Before you write any of it, read the four contract rules. They decide most of this integration's edge cases.
Where everything lives
| Flow: Nexa's perspective | Scope, glossary, the state machine, then one page per phase with diagrams and failure modes |
| Authentication | mTLS both directions, certificate exchange and rotation |
| Webhooks guide | Envelope, event catalogue, dedupe, ordering rules |
| End-to-end test | One script that walks the whole flow and checks every step |
| Testing & stubs | The stub host and the magic error id |
| Open questions | Ten items, the agenda for the technical meeting |
| Nexa endpoints exposed to Tesser | API reference for everything Nexa hosts |
| Tesser endpoints exposed to Nexa | API reference for everything Tesser hosts |
Read Open questions before milestone 1. Several of them (the quote validity window and Nexa's certificate-validation choice) need an answer before the first call can succeed.