Parties
Every Nora intent belongs to a party. This is the model you'll repeat most often.
A party is Nora's model of one of your end-users. You create a
party once, bind a Solana wallet to it, and then every onramp and
offramp intent you create targets that wallet — Nora resolves the
party server-side. This is the most repeated pattern in a /v2
integration: set it up once per end-user, then reuse.
Why parties
Your API key identifies you (your org, your instance). It does not
identify your end-user. Parties carry the identity that regulated rails
demand — a legal name, a Brazilian tax ID, country of residence — plus
a connected Solana wallet. Intents don't accept a partyId directly;
they accept a destinationWallet (onramp) or destinationAddress
(offramp), and Nora resolves the party from the wallet binding you
registered earlier.
This keeps compliance state attached to a durable entity rather than scattered across individual intents.
The party shape
A party has these top-level fields:
partyType—"person"or"business"(default"person").countryCode— ISO 3166-1 alpha-2 (default"BR").externalRef— an optional string you set for your own correlation, ornull.status—"pending"|"active"|"suspended"|"blocked".profile— a sub-object withdisplayName,documentType,documentMasked, andsolanaWallet(nullable).id,instanceId,createdAt,updatedAt— server-assigned.
Required on create: cpf and displayName. Everything else has
a default or is optional.
The three endpoints
POST /v2/parties — create a party. The minimum body is
{ cpf, displayName }; partyType defaults to "person" and
countryCode to "BR". Response includes id (persist this) and
the full profile echo.
GET /v2/parties/:id — fetch a party by id. Returns the same
shape as POST /v2/parties response, including
profile.solanaWallet (string when bound, null otherwise).
PATCH /v2/parties/:id/wallet — bind or replace the Solana
wallet. Body is { solanaWallet } — just the address. No chainId,
no envelope. Response is the full updated party object (same shape as
GET), with the new address visible at profile.solanaWallet.
Identity by party type
Today Nora serves Brazilian end-users. The cpf field accepts either:
- CPF (11 digits) when
partyType = "person". - CNPJ (14 digits) when
partyType = "business".
The server validates format at create time; a bad document is a 400 on the create call.
Wallet binding is replaceable
A party currently holds one Solana wallet at a time via
profile.solanaWallet. Calling PATCH /v2/parties/:id/wallet with a
new address replaces the previous binding entirely. Multi-chain wallet
binding (e.g. Polygon) is not yet in the public API — today only
solanaWallet is settable.
Gotchas
- Intents resolve the party from the wallet address. For onramp,
destinationWalletmust match the party'sprofile.solanaWallet. For offramp,destinationAddressis the fiat rail destination and Nora still resolves the party from the wallet binding on file. - Create one party per end-user. Do not share a party across multiple end-users — the identity fields are literal.
- Parties are instance-scoped. A sandbox party id does not resolve
in production, and vice versa. Your
partyId-to-user mapping must be partitioned per environment.
See also
- Parties setup — step-by-step how-to
- Parties API reference — endpoint schemas
- Idempotency — how write endpoints dedupe on retries
Authentication
How X-API-Key binds to organization and instance, and why /v2 has no cp-* headers.
Idempotency
Make intent-creation requests safe to retry. POST /v2/intents/onramp and /v2/intents/offramp accept an idempotency-key header — mint one UUID per user intent and reuse it across retries. Burn approval uses txSignature as the dedup key instead.