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.
Creating an offramp intent leaves it in
status: awaiting_burn_approval. To complete the offramp, your user's
Solana wallet has to sign an SPL Token Approve instruction that
grants Nora's burn authority delegate rights over the exact amount
being redeemed. You broadcast that transaction from the client and
submit the resulting txSignature back to Nora along with the
token-holder's ATA address.
The /v2 endpoint that accepts the submission is:
POST /v2/intents/:id/approve-burnGap in the current public contract
The public /v2 response does not currently return the SPL Approve
construction details — the BRS mint address, the delegate authority
public key, and the amount to approve in token minor units. Integrators
today must obtain those values out of band (from the Nora team or from
the dashboard) until a future /v2 revision embeds them in the intent
response. Plan for this to be documented here once it ships.
Flow
Steps
-
Offramp intent has been created. You have an
intentIdand the intent's status isawaiting_burn_approval. See Offramp intent. -
Obtain the Approve construction parameters. Until the
/v2response embeds them, coordinate with the Nora team for:- the BRS SPL mint address,
- the delegate authority public key (Nora's burn authority),
- the amount to approve in token minor units (derived from
amountCents×10^decimals).
-
Client-side: construct + sign + broadcast SPL Approve. Build an SPL Token
Approveinstruction that grants the delegate rights over the exact amount on the user's associated token account (ATA) for the BRS mint. Sign with the party's Solana wallet. Broadcast to Solana and collect thetxSignaturethe network returns. -
Submit the signature.
POST /v2/intents/:id/approve-burnwith{ txSignature, tokenHolder }.tokenHolderis the user's ATA for the BRS mint. The intent advances toburn_approvedand the offramp continues.Idempotency note.
txSignatureis the idempotency key for this endpoint. Resubmitting the same signature is accepted and returns the same result; a different signature against the same intent is rejected. There is no separateidempotency-keyheader here — see Idempotency.
Client outline
The library-agnostic shape is:
// 1. Obtain mint, delegate, and amount out-of-band for now; see Gaps above.
// 2. Build an SPL Approve instruction on the user's ATA for the BRS mint.
// 3. Sign with the user's wallet (adapter-specific).
// 4. Broadcast with your Solana RPC client and capture txSignature.
// 5. POST the signature + the ATA address to Nora.Then submit to Nora:
curl -X POST "https://staging.api.nora.finance/v2/intents/$INTENT_ID/approve-burn" \
-H "X-API-Key: $NORA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"txSignature": "5h2...",
"tokenHolder": "FgC...<ATA address>"
}'Response:
{ "status": "burn_approved" }That single field is the entire body — no id, no echo of the intent.
Re-fetch GET /v2/intents/:id to observe subsequent state transitions.
Request schema
POST /v2/intents/:id/approve-burn
Path parameter: id (uuid).
Body:
| Field | Type | Required | Notes |
|---|---|---|---|
txSignature | string | Yes | Base58-encoded signature from the broadcast SPL Approve transaction. Min length 1. |
tokenHolder | string | Yes | The user's associated token account address for the BRS mint. 32–44 chars. |
Errors follow the intents-family envelope
{ code, message, validationErrors?, details? }:
400— body validation, signature rejected, wrong signer / amount.404— intent not found.
Recovery if the user reloads
Between broadcast and submit is the risky gap. Persist
{ intentId, tokenHolder, txSignature } in durable client storage as
soon as sendRawTransaction returns. On resume, re-run
POST /v2/intents/:id/approve-burn with the stored values — the
server dedups on txSignature, so the user does not need to sign again.
Notify-side failure modes
If the transaction lands on Solana but the approve-burn call never
succeeds and the user abandons the flow, the delegate allowance stays
on their ATA. This is a deliberate trade-off:
- The allowance is scoped to the exact amount of that intent.
- Only Nora's burn authority can exercise it.
- Revoking it would require another wallet signature on a failure path.
Your app doesn't need to clean it up. A later offramp creates a fresh Approve for its own amount.
Gotchas
- Amount is in token minor units. The value you pass into
createApproveInstructionmust be the raw on-chain amount (BRL cents ×10^decimalsof the BRS mint). Do not passamountCentsdirectly. - User-rejected signatures should be treated distinctly. Wallet adapters throw with messages containing "rejected", "declined", "cancelled", or "user denied". Surface "try again" copy, not a generic error.
- No
idempotency-keyheader.approve-burndedups ontxSignature. Sending a header is not an error, but it is not used.
See also
- Offramp intent — the prerequisite
- API Reference: Intents — endpoint spec for
approve-burn - Idempotency —
txSignatureas the dedup key