nora

Offramp intent

Redeem BRS tokens for a BRL payout via PIX. Your backend creates the intent, your user signs the staged burn transaction 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.

Flow

Loading diagram…

Create the intent

POST /v2/intents/offramp

FieldTypeRequiredNotes
amountCentsintegerYesBRL payout amount in cents. Must be at least 500 (R$5.00 = 5 BRS minimum).
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.

Example:

curl -X POST https://sandbox.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...",
  "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,
  "chainId": "solana"
}

depositInstructions is always null in the current /v2 response.

The burn-approval gap

The offramp signing construction isn't embedded in the public /v2 intent response. In the grant model you don't assemble an approval from raw values — you call prepare-burn, Nora issues an attested on-chain burn grant and stages an unsigned user_burn transaction, and the user signs that staged transaction. See Burn signing for the full construction and field details.

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 staged user_burn transaction — see Burn signing.
burn_approvednoSigned user_burn submitted and accepted.
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

  • Minimum R$5.00 (5 BRS). amountCents below 500 is rejected at creation with a 400 — the floor mirrors the on-chain MintConfig.min_amount.
  • 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.
  • BRS balance is a prerequisite. The wallet 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