nora

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

Loading diagram…

Create the intent

POST /v2/intents/offramp

FieldTypeRequiredNotes
amountCentsintegerYesBRL payout amount in cents. Must be > 0.
destinationAddressstringYesPIX key or payout reference. Not a wallet — this is the fiat destination, named differently from onramp's destinationWallet. 1–255 chars.
chainIdstringYes"solana" or "polygon" accepted on request; responses emit "solana" only today.
clientReferencestringNoYour correlation ID. Up to 255 chars.

Headers:

HeaderValue
X-API-KeyYour API key.
idempotency-keyRecommended. 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.

StatusTerminal?Notes
creatednoAccepted server-side.
requires_compliancenoHeld for compliance review.
awaiting_burn_approvalnoUser must sign the SPL Approve — see Burn signing.
burn_approvednoDelegate approval confirmed.
burningnoToken burn in flight.
paying_outnoPIX payout initiated.
completedyesPayout settled.
failed / expired / canceled / refundedyesTerminal 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, not destinationWallet. Onramp and offramp diverge on this field name because the offramp destination is fiat, not crypto. Using destinationWallet on offramp is a validation error.
  • No partyId in 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_compliance and normal in-flight states share most of the API surface. Key off status, not derived UI state.

See also

On this page