# Callback

Callback allows you to receive a notification when a minting or redemption transaction is completed. To use Callback, you must first generate an [**API Key**](/api/getting-started.md). Once you have your API Key, navigate to the **API Key** menu and select the **Callback URL** tab to set it up.

<figure><img src="/files/m0xAbfV8DFuARhHyM15H" alt=""><figcaption></figcaption></figure>

Enter your Mint and Redeem Callback URLs. You may use a single endpoint for both callbacks if preferred. Once done, click the **Save Callback URL** button to confirm.

<figure><img src="/files/RgY2hkzeyuxfzkrI5ZtA" alt=""><figcaption></figcaption></figure>

You’ll see a notification in the bottom-right corner once the Callback URL is successfully saved.

<figure><img src="/files/m7AMONAFeEbV8YprxPNy" alt=""><figcaption></figcaption></figure>

You can then use the endpoint to receive callbacks in your logs whenever there’s an update to your mint or redeem transactions. Please refer to the example below.

### Delivery rules

These rules apply to both mint and redeem callbacks:

* **One callback per terminal state.** The mint callback fires once when `adminMintStatus` transitions to `MINTED`. If your endpoint is unreachable, the callback is **not** retried automatically — respond with `2xx` quickly and process asynchronously on your side.
* **No signature on the outgoing webhook.** Reconcile by `merchantOrderId` against your own state. Treat the request as untrusted until you've matched it and re-verified via Transaction History API.
* **Body is `application/json`.** Headers are minimal — only the standard `Content-Type`.
* **Two mint flavours:**
  * **IDRX mint** — when `requestType` is empty or `"idrx"`. Fires after IDRX is minted on-chain.
  * **USDT onramp** — when `requestType` is `"usdt"`. Fires after the IDRX→USDT swap and USDT transfer to the destination wallet succeed.

{% hint style="warning" %}
Since callbacks are not retried, missed deliveries must be reconciled by polling the Transaction History API using `merchantOrderId`.
{% endhint %}

***

## Mint callback

### Payload structure

Every mint callback body has these top-level fields:

| Field                        | Type           | Description                                                |
| ---------------------------- | -------------- | ---------------------------------------------------------- |
| `id`                         | number         | Internal MintRequest ID                                    |
| `paymentAmount`              | number         | Amount the user paid in IDR (before fees)                  |
| `toBeMinted`                 | string         | IDRX amount minted (= `paymentAmount` minus deducted fees) |
| `merchantOrderId`            | string         | Your order ID                                              |
| `destinationWalletAddress`   | string         | Wallet that received the IDRX or USDT                      |
| `chainId`                    | number         | EVM chain ID                                               |
| `paymentStatus`              | enum           | `WAITING_FOR_PAYMENT` / `PAID` / `EXPIRED`                 |
| `userMintStatus`             | enum           | `PROCESSING` / `MINTED` / `NOT_AVAILABLE`                  |
| `adminMintStatus`            | enum           | `REQUESTED` / `PROCESSING` / `MINTED` / `REJECTED`         |
| `txHash`                     | string         | On-chain mint transaction hash                             |
| `reference`                  | string         | Payment gateway reference                                  |
| `requestType`                | string         | `null` / `"idrx"` / `"usdt"`                               |
| `customerVaName`             | string         | Source account name                                        |
| `paymentProviderId`          | number         | Always `1` (POP)                                           |
| `returnUrl`                  | string \| null | Echoed back from original request                          |
| `expiryTimestamp`            | string         | Unix epoch milliseconds                                    |
| `isApproved`                 | boolean        | Multisig approval flag                                     |
| `reportStatus`               | enum           | `NONE` / `PENDING` / `RESOLVED`                            |
| `processByUserId`            | number \| null | Admin processor                                            |
| `qredoTxId`                  | string \| null | Safe multisig transaction hash                             |
| `signedTx`                   | string \| null | Solana signed transaction blob                             |
| `deleted`                    | boolean        | Soft-delete flag                                           |
| `createdAt`                  | ISO 8601       | Creation timestamp                                         |
| `updatedAt`                  | ISO 8601       | Last updated timestamp                                     |
| `TransactionHistory`         | object         | Payment gateway notification                               |
| `MintRequestTransactionFees` | array          | Fee breakdown                                              |
| `usdtRequest`                | object \| null | USDT swap details                                          |

***

### `TransactionHistory` sub-object

```jsonc
{
  "id": 21468,
  "merchantCode": "D11808",
  "amount": "55000",
  "merchantOrderId": "20260508154603",
  "productDetail": "Deposit USD",
  "additionalParam": "toBeMinted: 55000 destinationWalletAddress: 0x...",
  "resultCode": "00",
  "signature": "4f9cabb4aac2b04fbc02a3b039a906fc",
  "paymentCode": "BR",
  "merchantUserId": 5002,
  "reference": "D11808261VSEBJBL43V2ZIW",
  "spUserHash": null,
  "issuerCode": null,
  "settlementDate": "2026-05-09",
  "publisherOrderId": "BR26BOLMOR7DDYK18WA",
  "sourceAccount": "",
  "deleted": false,
  "createdAt": "2026-05-08T15:47:58.487Z",
  "updatedAt": "2026-05-08T15:47:58.487Z"
}
```

***

### `MintRequestTransactionFees` array

```jsonc
{
  "name": "QRIS Fee (0.7%)",
  "amount": "609"
}
```

{% hint style="warning" %}
An empty array (`[]`) means the payment channel charges no IDRX-layer fee.\
This is expected behaviour, not missing data.
{% endhint %}

***

### `usdtRequest` sub-object (USDT onramp only)

```jsonc
{
  "id": 13397,
  "mintRequestId": 33685,
  "merchantOrderId": "20260508154603",
  "destinationWalletAddress": "0xadbe359255d723a5ad3706395b446aed5cf285e6",
  "chainId": 8453,
  "amountIdrx": 55000,
  "usdtRequested": "3.174996",
  "amountUsdt": "3.174996",
  "swapTxHash": "0x8a4b781e8c8694bc68906bb85206da174fe3550776e72cbe806f19931732030d",
  "txHash": "0x55b16ff8100f12e9c611f13a7848d16917b477a5d838172c26e3b0d6da1e8617",
  "status": "SUCCESS",
  "deleted": false,
  "createdAt": "2026-05-08T15:46:03.272Z",
  "updatedAt": "2026-05-08T15:49:04.769Z"
}
```

***

## Fee structure by payment method

Two patterns exist depending on the channel:

* **VA & e-wallet**
  * Fee is added on top at checkout.
  * `paymentAmount === toBeMinted`
  * `MintRequestTransactionFees = []`
* **QRIS**
  * Fee is deducted from minted IDRX.
  * `toBeMinted = paymentAmount - 0.7%`
  * Fee row appears in `MintRequestTransactionFees`

| Channel      | `paymentCode` | IDRX fee  | Charged how  | Effect           |
| ------------ | ------------- | --------- | ------------ | ---------------- |
| BNI VA       | `IQ`          | 3,000 IDR | Added on top | Fees array empty |
| BRI VA       | `BR`          | 3,000 IDR | Added on top | Fees array empty |
| OVO          | `OV`          | 1.67%     | Added on top | Fees array empty |
| DANA         | `DA`          | 1.67%     | Added on top | Fees array empty |
| NusaPay QRIS | `SQ`          | 0.7%      | Deducted     | Fee row present  |
| NOBU QRIS    | `NQ`          | 0.7%      | Deducted     | Fee row present  |

***

### Worked example — 100,000 IDR top-up

| Channel             | User pays | IDRX delivered | `paymentAmount` | `toBeMinted` | Fees                                               |
| ------------------- | --------- | -------------- | --------------- | ------------ | -------------------------------------------------- |
| BRI VA (`BR`)       | 103,000   | 100,000        | `100000`        | `"100000"`   | `[]`                                               |
| OVO (`OV`)          | 101,670   | 100,000        | `100000`        | `"100000"`   | `[]`                                               |
| NusaPay QRIS (`SQ`) | 100,000   | 99,300         | `100000`        | `"99300"`    | `[{ "name": "QRIS Fee (0.7%)", "amount": "700" }]` |

Universal reconciliation formula:

```ts
const idrxFee =
  body.paymentAmount -
  parseInt(body.toBeMinted, 10);
```

***

## Mint examples

### A — IDRX mint via QRIS (`SQ`)

```json
{
  "id": 33494,
  "paymentAmount": 86967,
  "toBeMinted": "86358",
  "merchantOrderId": "20260506070155",
  "destinationWalletAddress": "0xA3aE7A9562E6DDdc693be0c34013637730E2bA00",
  "chainId": 8453,
  "paymentStatus": "PAID",
  "userMintStatus": "MINTED",
  "adminMintStatus": "MINTED",
  "txHash": "0x9a3c5b...e2f01a",
  "reference": "D11808260X8QYZJZQ19K5",
  "requestType": "idrx"
}
```

***

### B — IDRX mint via BRI VA (`BR`)

```json
{
  "id": 33687,
  "paymentAmount": 100000,
  "toBeMinted": "100000",
  "merchantOrderId": "20260509093000",
  "destinationWalletAddress": "0xA3aE7A9562E6DDdc693be0c34013637730E2bA00",
  "chainId": 8453,
  "paymentStatus": "PAID",
  "userMintStatus": "MINTED",
  "adminMintStatus": "MINTED",
  "txHash": "0x4b1f8d...c91e72",
  "reference": "D11808261VKLMNO78PQRS",
  "requestType": "idrx",
  "MintRequestTransactionFees": []
}
```

{% hint style="info" %}
Empty `MintRequestTransactionFees` on VA payments is expected behaviour.
{% endhint %}

***

### USDT onramp variant

When `requestType === "usdt"`:

* Callback fires after the IDRX → USDT swap completes
* Payload shape is identical
* `usdtRequest` becomes populated

```json
{
  "id": 13397,
  "mintRequestId": 33685,
  "merchantOrderId": "20260508154603",
  "destinationWalletAddress": "0xadbe359255d723a5ad3706395b446aed5cf285e6",
  "chainId": 8453,
  "amountIdrx": 55000,
  "usdtRequested": "3.174996",
  "amountUsdt": "3.174996",
  "swapTxHash": "0x8a4b781e8c8694bc68906bb85206da174fe3550776e72cbe806f19931732030d",
  "txHash": "0x55b16ff8100f12e9c611f13a7848d16917b477a5d838172c26e3b0d6da1e8617",
  "status": "SUCCESS"
}
```

***

## Reconciliation tips

```ts
// Universal fee calculation
const idrxFee =
  body.paymentAmount -
  parseInt(body.toBeMinted, 10);

// IDRX delivered
const idrxMinted =
  body.usdtRequest
    ? null
    : body.toBeMinted;

// USDT delivered
const usdtDelivered =
  body.usdtRequest?.amountUsdt;

// Effective exchange rate
const rate =
  body.usdtRequest?.amountUsdt
    ? body.paymentAmount /
      parseFloat(body.usdtRequest.amountUsdt)
    : null;
```

For high-value transactions, verify on-chain delivery using `txHash`.

***

## Status handling

Always check `adminMintStatus`.

| Status        | Meaning             | Action         |
| ------------- | ------------------- | -------------- |
| `MINTED`      | Tokens delivered    | Mark success   |
| `REJECTED`    | Expired or rejected | Mark cancelled |
| anything else | Unexpected          | Log and alert  |

***

## Redeem callback

### IDRX

```json
{
  id: 1234,
  chainId: 56,
  userId: 1234,
  requester: 'NUR MUHAMMAD LUTHFI',
  txHash: '0xabcdefghijk',
  fromAddress: '0xlmnopqrstuvwxyz',
  amount: '15000',
  bankName: 'BANK JAGO',
  bankCode: '542',
  bankAccountNumber: '123456789',
  bankAccountName: 'NUR MUHAMMAD LUTHFI',
  bankAccountNumberHash: null,
  custRefNumber: '123456789',
  disburseId: 123456789,
  burnStatus: 'SUCCESS',
  createdAt: '2025-07-29T10:48:50.417Z',
  updatedAt: '2025-07-29T10:49:54.691Z',
  deleted: false,
  reportStatus: 'NONE',
  notes: ' | ',
  RedeemRequestTransactionFees: [
    {
      id: 1234,
      name: 'Payment Gateway Fee',
      amount: '5000',
      redeemRequestId: 3456,
      deleted: false
    }
  ]
}
```

***

### USDT / USDC / USDT0

```json
{
  id: 4593,
  chainId: 1135,
  userId: 2255,
  requester: 'NUR MUHAMMAD LUTHFI',
  txHash: '0x8359c8901667923ee0d214fd5100e0b99daea63d5acda2de276cff584461f16c',
  fromAddress: '0x55360Fe4179936e88c321f6e3bd23Ef34E916110',
  amount: '36219',
  bankName: 'GOPAY',
  bankCode: '1011',
  bankAccountNumber: '08123456789',
  bankAccountName: 'GOPAY Nxx Mxxxxxxx Lxxxxx',
  bankAccountNumberHash: null,
  custRefNumber: '000045642625',
  disburseId: 30098713,
  burnStatus: 'SUCCESS',
  createdAt: '2025-08-01T06:50:53.109Z',
  updatedAt: '2025-08-01T06:51:52.473Z',
  deleted: false,
  reportStatus: 'NONE',
  notes: null,
  RedeemRequestTransactionFees: [
    {
      id: 3217,
      name: 'Payment Gateway Fee',
      amount: '5000',
      redeemRequestId: 5943,
      deleted: false
    }
  ],
  depositRedeemRequest: {
    id: 208,
    chainId: 1135,
    userId: 2255,
    address: '0x1095bBe769fDab716A823d0f7149CAD713d20A13',
    toAddress: '0x55360Fe4179936e88c321f6e3bd23Ef34E916110',
    transferTxHash: '0x3e36838c61f3c9f2009d71b1b53dafc1d8eb280bf490e58ab08dee9767dcc842',
    tokenFrom: 'usdt',
    amountFrom: '2.511252',
    tokenTo: 'idrx',
    amountTo: '41219',
    swapTxHash: '0x15187b051f8f6b9da02470cf548f395199429442c445a52ebee44698fad9de93',
    burnTxHash: '0x8359c8901667923ee0d214fd5100e0b99daea63d5acda2de276cff584461f16c',
    status: 'SUCCESS',
    createdAt: '2025-08-01T06:49:06.406Z',
    updatedAt: '2025-08-01T06:50:53.118Z',
    deleted: false
  }
}
```

***

## See also

* [POST /api/transaction/mint-request](/api/transaction-api/post-api-transaction-mint-request.md) — how mint orders are created.
* [Processing Mint IDRX Requests](/integration/processing-mint-idrx-requests.md) — end-to-end mint flow with reconciliation pattern.
* [Transaction History API](/api/transaction-api/get-api-transaction-user-transaction-history.md) — pull state by `merchantOrderId` if a webhook is missed.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.idrx.co/api/callback.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
