https://sandbox-api.nuvante.ioGuides
Issuer-side API
Stablecoin issuers sit on the other side of a transaction's burn or mint leg. This page describes the contract between Nuvanté and you as an issuer.
Who this is for
Most of these docs are written for clearing members, who start redemptions and swaps using assets that other people issue. This section is for stablecoin issuers: participants with participantKind: "stablecoin_issuer" and the manageIssuedAssets capability.
If you're a clearing member, you can skip this section. If you're an issuer, most of the clearing-member pages still apply to you (authentication, error handling, response envelopes), and the endpoints below are just for issuers.
How it fits together
Clearing members never call your API directly. Nuvanté sits in the middle. We call you to instruct a burn or mint, and we use your declared capabilities to decide how. The full field list is in Issuer capability declaration.
This section covers the two things you own:
- declaring your capability profile
- the instruction and webhook contract your API needs to meet, if
feedbackChannelincludes anything besidesPOLLABLE_RESOURCE
Declaring your capability profile
PUT https://sandbox-api.nuvante.io/api/v1/issuers/{issuerId}/capabilitiesThis needs manageIssuedAssets. It uses the same schema as Issuer capability declaration. We repeat it here because this is the endpoint that writes the profile.
{
"commitPoint": "ON_INSTRUCTION",
"preBurnTransferRequired": true,
"custodyModel": "PER_MEMBER_ISSUER_WALLET",
"mintDeliveryTarget": "REGISTERED_HANDLE",
"feedbackChannel": ["SIGNED_WEBHOOK", "POLLABLE_RESOURCE"],
"fiatLeg": "NUVANTE_SETTLES_RTGS",
"ledger": "stellar",
"finalityClass": "DETERMINISTIC",
"amountPrecision": 2,
"approvalSlaSeconds": 45,
"apiBaseUrl": "https://issuer-api.partner-bank.test",
"webhookUrl": "https://issuer-api.partner-bank.test/nuvante/webhooks"
}Changing commitPoint or preBurnTransferRequired changes which mode (1, 2 or 3) Nuvanté works out for your transactions. See Compare the modes.
On this endpoint, custodyModel describes your issuer wallet structure (PER_MEMBER_ISSUER_WALLET or POOLED_ISSUER_WALLET). The participant-level custodyModel (self-custody, mpc or caas) is a separate field.
Profile updates apply to transactions started after the update. Transactions already in flight carry on with the snapshot they started with, as described for swaps in Initiate a stable-to-stable swap.
The response includes a profileVersion number. Nuvanté records which profileVersion each transaction used. Clearing members can't see it on the transaction resource, but you can look it up per transaction in the admin console when you reconcile.
Credential exchange
On top of the API keys used everywhere else, issuer-gateway integrations need:
- An mTLS client certificate, which you submit as a CSR during onboarding. Nuvanté's issuer-gateway service presents its own client certificate when it calls your
apiBaseUrl. Verify it against the CA bundle we give you at onboarding. - OAuth2 client credentials for our calls to you, if your
apiBaseUrlneeds bearer auth as well as mTLS. We share these outside the sandbox portal during onboarding. - A webhook signing secret, if
feedbackChannelincludesSIGNED_WEBHOOK. It's generated in the same way as the secrets described in Event notifications.
All of this happens during onboarding. A tenant admin with manageIssuedAssets submits the CSR and OAuth client credential details through the portal as part of the issuer onboarding flow. The rest of this page describes what your API has to do once credentials are exchanged, so you can build and test against it before that flow starts.
The instruction API you must implement
If your commitPoint is ON_INSTRUCTION (Modes 2 and 3), Nuvanté calls your apiBaseUrl to instruct burns and mints. Your API needs these routes:
POST {apiBaseUrl}/burn-instructions
POST {apiBaseUrl}/mint-instructions
GET {apiBaseUrl}/instructions/{id}Here's a request to POST /burn-instructions:
{
"idempotencyKey": "5c9e...a1f0",
"reference": "NUV-123e4567-e89b-42d3-a456-426614174000",
"sourceWalletId": "wal_nuv_mem_014",
"amount": "250000.00",
"assetCode": "GBPC"
}A request to POST /mint-instructions looks the same as a burn, with one extra field, targetWalletId, telling you where to send the minted asset:
{
"idempotencyKey": "7b2a...c3e1",
"reference": "NUV-123e4567-e89b-42d3-a456-426614174000",
"targetWalletId": "wal_nuv_mem_017",
"amount": "250000.00",
"assetCode": "GBPC"
}The synchronous response from POST /mint-instructions:
{ "instructionId": "your-internal-mint-id", "status": "RECEIVED" }A couple of things to know about these fields:
idempotencyKeyis a UUIDv5 built from(commandId, leg). If we replay a request with the same key and body, you must return the original result and avoid creating a second instruction.referenceis Nuvanté's transaction reference. It's at most 35 characters, matching the width of an ISO 20022 EndToEndId. Store it in your own records so you can match an instruction back to a Nuvanté transaction without a second lookup.
Your synchronous response:
{ "instructionId": "your-internal-id", "status": "RECEIVED" }status has to be one of the values in the table below. If your process has a separate authorisation step (Mode 2), stop at RECEIVED or PENDING_APPROVAL. Hold off executing, and wait for Nuvanté to call the execute endpoint described further down.
RECEIVEDPENDING_APPROVALAPPROVEDEXECUTINGCOMPLETEDREJECTEDFAILEDThis table mirrors the normalised events from the issuer's side. Please take care to get REJECTED and FAILED right. REJECTED triggers a safe, automatic unwind. FAILED parks the transaction, because Nuvanté has no way to check that your side rolled back. Please only use FAILED for that genuinely uncertain case, since every FAILED sends the transaction to a person to sort out.
If your profile is two-phase (Mode 2), you also need to implement:
POST {apiBaseUrl}/instructions/{id}/executeWe only call this after confirming the RTGS earmark, and once it's called there's no going back. Your API should treat this call as the signal to actually move the asset. The earlier burn-instruction call only sets things up.
Sending status by webhook
If feedbackChannel includes SIGNED_WEBHOOK, POST status changes to the webhookUrl you declared. This works like Event notifications in reverse: here you're sending to Nuvanté, where a clearing member would be receiving.
{
"instructionId": "your-internal-id",
"status": "COMPLETED",
"onchain": { "txHash": "0x...", "ledgerSequence": 48213904 },
"finalizedAt": "2026-08-04T09:04:33.000Z"
}Sign it the same way Nuvanté signs webhooks to clearing members. Use an HMAC over {timestamp}.{body} with the shared secret from onboarding, and send it in a Nuvante-Signature header in the format described in Verifying signatures. We verify your signature when it arrives. If a delivery is unsigned or the signature is wrong, we drop it, log it and don't retry. Test your signing against the sandbox harness before you go live.
We treat each webhook from you as a prompt to check, and we don't act on its contents directly. After every delivery, Nuvanté calls GET {apiBaseUrl}/instructions/{id} before doing anything. So please make your GET handler at least as reliable as your webhook delivery. If one of them is down, the other is how we'll find out what happened.
Mode 3 refund endpoint
Every Mode 3 issuer implements a refund endpoint as well as the burn and mint instructions. Nuvanté calls it when an instruction expires after the tokens have already been transferred.
POST https://{issuer-api}/treasury/refunds
{
"transactionId": "e7a1c3f5-9b2d-4e6f-8a0c-2b4d6f8a1c3e",
"reason": "instruction_expired",
"refundToWalletId": "wallet_source_original"
}200 OK
{
"transactionId": "e7a1c3f5-9b2d-4e6f-8a0c-2b4d6f8a1c3e",
"status": "refunded",
"refundTxHash": "5f2c8a9e...",
"refundedAt": "2026-09-01T10:04:12.000Z"
}Participants and issuers can follow recovery progress through GET /api/v1/transactions/{id}/recovery-status on the Nuvanté API.
Conformance testing
Before you go live, your instruction API has to pass the conformance suite from the issuer capability framework. It covers:
- the happy path for each instruction type you support
- an explicit rejection, checking that the earmark is cancelled and the asset is returned
- silence beyond your declared
approvalSlaSeconds, checking that the transaction parks - replaying a request with the same idempotency key
- verifying callback signatures
Sandbox has a harness for this:
POST https://sandbox-api.nuvante.io/api/v1/issuers/{issuerId}/conformance/run
GET https://sandbox-api.nuvante.io/api/v1/issuers/{issuerId}/conformance/resultsIt sends synthetic instructions to your declared apiBaseUrl in sandbox and records a pass or fail for each scenario. Your profile can only be promoted from sandbox to production once every mandatory scenario passes. Sandbox trial lists the scenarios as they run against clearing-member flows. The issuer-side scenarios have the same shape, run from the other direction.
