Offramp intent
Redeem BRS tokens for a BRL payout via PIX. Your backend creates the intent, your user signs an SPL Approve on Solana, and the payout settles.
An offramp intent (labelled Burn in the Nora dashboard) redeems
BRS for a BRL payout. Unlike onramp, offramp requires
an on-chain signature from the token-holder's wallet after the intent
is created. The signing hand-off has a gap in the current public /v2
contract — see Burn signing for details.
Prerequisite
You need a party with a connected Solana wallet that holds enough BRS. See Parties setup — Nora resolves the party from the wallet binding on file.
Flow
Create the intent
POST /v2/intents/offramp
| Field | Type | Required | Notes |
|---|---|---|---|
amountCents | integer | Yes | BRL payout amount in cents. Must be > 0. |
destinationAddress | string | Yes | PIX key or payout reference. Not a wallet — this is the fiat destination, named differently from onramp's destinationWallet. 1–255 chars. |
chainId | string | Yes | "solana" or "polygon" accepted on request; responses emit "solana" only today. |
clientReference | string | No | Your correlation ID. Up to 255 chars. |
Headers:
| Header | Value |
|---|---|
X-API-Key | Your API key. |
idempotency-key | Recommended. UUID format-validated. See Idempotency. |
The body does not take a
partyId. Nora resolves the party from the wallet binding on file.
Example:
curl -X POST https://staging.api.nora.finance/v2/intents/offramp \
-H "X-API-Key: $NORA_API_KEY" \
-H "Content-Type: application/json" \
-H "idempotency-key: $(uuidgen)" \
-d '{
"amountCents": 10000,
"destinationAddress": "user@example.com",
"chainId": "solana",
"clientReference": "payout_xyz_42"
}'Response:
{
"id": "8f2a...",
"instanceId": "4e0b...",
"partyId": "2f1b...",
"intentType": "offramp",
"flowVersion": 1,
"adapter": null,
"status": "awaiting_burn_approval",
"statusReason": null,
"clientReference": "payout_xyz_42",
"metadata": null,
"expiresAt": null,
"createdAt": "2026-04-22T12:00:00Z",
"updatedAt": "2026-04-22T12:00:00Z",
"pixInfo": null,
"depositInstructions": null,
"party": {
"displayName": "João da Silva",
"documentMasked": "***.***.***-01",
"solanaWallet": "So11111111111111111111111111111111111111112"
},
"chainId": "solana"
}depositInstructions is always null in the current /v2 response.
The burn-approval gap
The offramp burn-approval contract (what to sign on-chain — the SPL
mint address, the delegate authority public key, and the minor-unit
amount to approve) is not yet part of the public /v2 response.
Coordinate with the Nora team for those values, or see
Burn signing once the documented flow ships with the
construction details embedded.
Similarly, the adapter the intent lands on (adapter: "cuiaba" or
"squads") and adapter-specific mechanics aren't fully modeled in the
public /v2 response today. Treat adapter as an informational field
for now.
Track completion
After burn approval is submitted (via
POST /v2/intents/:id/approve-burn — see
Burn signing), poll the intent until it reaches a
terminal status.
| Status | Terminal? | Notes |
|---|---|---|
created | no | Accepted server-side. |
requires_compliance | no | Held for compliance review. |
awaiting_burn_approval | no | User must sign the SPL Approve — see Burn signing. |
burn_approved | no | Delegate approval confirmed. |
burning | no | Token burn in flight. |
paying_out | no | PIX payout initiated. |
completed | yes | Payout settled. |
failed / expired / canceled / refunded | yes | Terminal error states. |
(Full status enum includes onramp-only values like
awaiting_fiat_payment, minting, and onchain_received; you won't
see those on offramp.)
Resuming an interrupted intent
If your app crashes between POST /v2/intents/offramp and the
approve-burn call, the intent is still in whatever state it was left
in. Reopen your signing UI using the persisted intent ID;
GET /v2/intents/:id returns the current intent shape, and
approve-burn dedups on txSignature — resubmitting the same signed
transaction is safe. See Burn signing for the
recovery pattern. No new intent is created.
Gotchas
destinationAddress, notdestinationWallet. Onramp and offramp diverge on this field name because the offramp destination is fiat, not crypto. UsingdestinationWalleton offramp is a validation error.- No
partyIdin the body. Nora resolves the party from the Solana wallet binding on file. - BRS balance is a prerequisite. The wallet connected to the party must hold enough BRS to cover the intent. If it doesn't, the signing step will refuse.
- Compliance holds don't render differently.
requires_complianceand normal in-flight states share most of the API surface. Key offstatus, not derived UI state.
See also
- Parties setup — the prerequisite
- Burn signing — the next step: sign the SPL Approve and submit the signature
- Onramp intent — the reverse direction
- Idempotency — why the key lives on your user-intent, not each retry
- API Reference: Intents — full endpoint spec
Onramp intent
Move BRL into BRS tokens minted to a Solana wallet. Your backend creates the intent, Nora returns PIX deposit info, your user pays by PIX, and the tokens land.
Burn signing
Sign the SPL Approve that lets Nora burn BRS from the user's wallet, broadcast it on Solana, and submit the signature back via /v2/intents/:id/approve-burn.