Onramp intent

Move BRL into BRS tokens minted to a whitelisted wallet on Solana, Ethereum, Base or Polygon. Your backend creates the intent, Nora returns PIX deposit info, your user pays by PIX, and the tokens land.

An onramp intent is a single BRL → BRS mint. Your backend posts the amount, the destination wallet, and the chain; Nora returns PIX deposit info you surface to your user. Once the PIX lands, Nora mints BRS to the wallet and the intent reaches completed.

Flow

Loading diagram…

Create the intent

POST /v2/intents/onramp

chainId accepts "solana", "ethereum", or "base". Base is live in both environments (sandbox = Base Sepolia, production = Base mainnet). It also accepts "polygon", live in both environments (sandbox = Polygon Amoy, production = Polygon mainnet).

FieldTypeRequiredNotes
amountCentsintegerYesBRL amount in cents, not decimals. 100.00 BRL → 10000. Must be at least 500 (R$5.00 = 5 BRS minimum).
destinationWalletstringYesWallet that receives the minted BRS: a Solana address, or an Ethereum address (0x…) registered through the Wallets API. 1–255 chars.
chainIdstringYes"solana", "ethereum", "base", or "polygon". The intent is minted on that chain; destinationWallet must be a whitelisted wallet on it.
clientReferencestringNoYour own correlation ID, up to 255 chars. Echoed on the intent. Unique per environment, forever, across onramp and offramp; when omitted, the idempotency-key is stored and echoed as the reference.

Headers:

HeaderValue
X-API-KeyYour API key. See Authentication.
idempotency-keyRecommended. A UUID you pick (format-validated). Reuse on retries, regenerate on new user intents. See Idempotency.

Example:

ORDER_KEY=$(uuidgen)   # one per user order, reused on retries
curl -X POST https://sandbox.api.nora.finance/v2/intents/onramp \
  -H "X-API-Key: $NORA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "idempotency-key: $ORDER_KEY" \
  -d '{
    "amountCents": 10000,
    "destinationWallet": "So11111111111111111111111111111111111111112",
    "chainId": "solana",
    "clientReference": "ord_123"
  }'

Response:

{
  "id": "8f2a...",
  "instanceId": "4e0b...",
  "intentType": "onramp",
  "flowVersion": 1,
  "status": "awaiting_fiat_payment",
  "statusReason": null,
  "failureCode": null,
  "providerFailureCode": null,
  "originalFailureCode": null,
  "originalProviderFailureCode": null,
  "clientReference": "ord_123",
  "metadata": { "chainId": "solana", "amountCents": 10000, "…": "…" },
  "expiresAt": "2026-04-22T12:30:00Z",
  "createdAt": "2026-04-22T12:00:00Z",
  "updatedAt": "2026-04-22T12:00:00Z",
  "pixPayment": {
    "brcode": "00020101021126580014BR.GOV.BCB.PIX...",
    "txid": "abc123",
    "amountCents": 10000,
    "expiresAt": "2026-04-22T12:30:00Z"
  },
  "depositInstructions": null,
  "party": { "displayName": "Your Company", "documentMasked": "64***65", "solanaWallet": null },
  "chainId": "solana"
}

Notable response fields:

  • intentType — "onramp" here.
  • flowVersion — integer; pin on at least the version your code was written against.
  • status — see the lifecycle table below for the full enum.

Render the PIX instructions

Surface pixPayment to the user: render brcode as a scannable QR code and a copy-and-paste field. amountCents and expiresAt provide the payment amount and deadline. PIX deposits expire — if the user hasn't paid by expiresAt, the intent transitions to expired and you need a new intent to retry: a fresh idempotency-key and, if you send one, a fresh clientReference (references are unique per environment, forever).

In the sandbox nobody pays: the deposit is simulated and the intent moves to fiat_received on its own shortly after creation, then mints.

Track completion

Poll GET /v2/intents/:id or subscribe to webhooks. The full intent status enum:

StatusTerminal?Notes
creatednoAccepted server-side, not yet routed.
requires_compliancenoHeld for compliance review.
awaiting_fiat_paymentnoWaiting for the user's PIX.
fiat_receivednoPIX confirmed by the rail.
mintingnoBRS being minted on-chain.
awaiting_onchain_transferno
awaiting_burn_approvalno(offramp) — not seen on onramp.
burn_approvedno(offramp).
onchain_receivedno
burningno(offramp).
scheduledno(offramp).
paying_outno(offramp).
completedyesTerminal success.
expiredyesPIX window closed before payment.
canceledyes
failednoNot terminal, but no automatic recovery: a refund is started by Nora's operators after review, never by an API call, and it may not start. Check failureCode; it can be null (for example when the destination was denied after the PIX was charged, see metadata.destinationAuthDenialCode), and then statusReason or metadata carries the cause. Contact Nora with the intent id.
refundingnoNora is processing a refund.
refundedyesRefund completed.

Gotchas

  • amountCents, not amount. Integer cents, not decimal BRL.
  • 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.
  • Idempotency keys scope to "this user intent," not "this retry." Regenerate on a new form submission, reuse across network-level retries. See Idempotency.
  • Response chainId echoes the request ("solana", "ethereum", "base", or "polygon").
  • clientReference is unique per environment, forever. Reusing one answers 409 Intent with clientReference 'ord_123' already exists, even when that intent expired. A retry with the same idempotency-key and body is replayed instead.
  • On Ethereum, Nora reads your whitelister and the BRS token on-chain before charging. A destinationWallet with no authorization fails with 400 and message: "evm_destination_wallet_not_authorized"; one whose daily limit is below amountCents gets evm_destination_wallet_rate_limit_exceeded; an amount outside the per-mint bounds gets evm_mint_amount_below_minimum or evm_mint_amount_above_maximum. Register the wallet through the Wallets API first. A completed Ethereum mint reports statusReason: "EVM mint confirmed on-chain." and the transaction hash at metadata.mintTxSignature; metadata keys are informational, not part of the versioned contract.

See also

On this page