# POST /api/transaction/mint-request

{% hint style="info" %}
Check out [this page](https://docs.idrx.co/introduction/supported-chain-and-contract-address) to see the list of chains where IDRX is available and [this page](/services/get-other-stablecoins.md) to see the list of supported other stablecoins.
{% endhint %}

{% hint style="info" %}

* The balance will be processed and credited to \`**`` destinationWalletAddress` ``** max 24 hours after your request is submitted.
* Minimum transaction:
  * Minting IDRX is Rp20,000 IDR
  * Minting other stablecoins are $2 USD
* Maximum transaction:&#x20;
  * Minting IDRX is 1,000,000,000 IDR.
  * Minting other stablecoins are 5,555 USD
* For transactions more than the maximum limit, please send your request to <support@idrx.co> to be processed.
* The transaction will be automatically canceled if you have not made a payment within 24 hours.
* IDRX is not responsible for deposit errors from incorrect Virtual Account numbers. Refunds for payments made to Virtual Account numbers under a different name will be processed within 14 business days.
  {% endhint %}

```
POST https://idrx.co/api/transaction/mint-request
```

### Quick start

```bash
curl -X POST 'https://idrx.co/api/transaction/mint-request' \
  -H 'idrx-api-key: <API_KEY>' \
  -H 'idrx-api-sig: <SIGNATURE>' \
  -H 'idrx-api-ts: <TIMESTAMP>' \
  -H 'Content-Type: application/json' \
  -H 'User-Agent: my-app/1.0' \
  -d '{
    "toBeMinted": "20000",
    "destinationWalletAddress": "0x7D1398C397C64368B7079bD5b3EE904aaaf8A495",
    "networkChainId": "8453",
    "returnUrl": "https://your-app.example.com/callback",
    "requestType": "idrx"
  }'
```

Redirect the user to `data.paymentUrl` from the response. After payment, IDRX is delivered to the wallet — usually within minutes.

> For end-to-end integration including status tracking and reconciliation, see [Processing Mint IDRX Requests](/integration/processing-mint-idrx-requests.md#overview).

***

### Pick a flow

|                                   | **Flow A — Hosted checkout**       | **Flow B — Direct payment**         |
| --------------------------------- | ---------------------------------- | ----------------------------------- |
| **Use when**                      | Web, dashboards, top-up flows      | Mobile apps, kiosks, white-label UX |
| **You build**                     | Nothing — we host the payment page | Your own VA / QR screen             |
| **Payment methods**               | All (VA, QRIS, e-wallets, retail)  | One per request: VA or QRIS         |
| **You receive**                   | `paymentUrl`                       | `virtualAccountNo` or `qrContent`   |
| **`returnUrl`**                   | Required                           | Not used                            |
| **`paymentMethod` + `channelId`** | Omit                               | Required                            |

Both flows share auth, parameters, and callbacks.

***

### Authentication

Every request needs four headers:

| Header         | Value                                     |
| -------------- | ----------------------------------------- |
| `idrx-api-key` | Your API key                              |
| `idrx-api-sig` | HMAC-SHA256 signature (see formula below) |
| `idrx-api-ts`  | Unix timestamp in milliseconds            |
| `User-Agent`   | Custom string like `my-app/1.0` (why)     |

#### Signature formula

```
HMAC-SHA256(secret, METHOD + ":" + PATH + ":" + SHA256(body) + ":" + timestamp)
```

For GET requests with no body, use `SHA256("")`.

Full guide: [Generating a Signature](/api/generating-a-signature.md)

API keys are issued via:

```http
POST /api/auth/generate-api-key
```

{% hint style="warning" %}
**Custom `User-Agent` required.** Defaults from `curl`, `Python-urllib`, or generic SDK strings are blocked at the edge with HTTP `403` (Cloudflare error 1010).
{% endhint %}

***

### Parameters

| Parameter                  | Type   | Required           | Description                                                                  |
| -------------------------- | ------ | ------------------ | ---------------------------------------------------------------------------- |
| `toBeMinted`               | string | ✓                  | Amount to mint. Min 20,000 IDR or 2 USD. Max 1,000,000,000 IDR or 5,555 USD. |
| `destinationWalletAddress` | string | ✓                  | Recipient wallet on the target chain.                                        |
| `networkChainId`           | string | ✓                  | Target chain ID. Supported chains →                                          |
| `returnUrl`                | string | Flow A             | HTTPS URL the user returns to after checkout.                                |
| `requestType`              | string | —                  | `"idrx"` (default) or `"usdt"`. Other tokens →                               |
| `paymentMethod`            | string | Flow B             | `"va"` or `"qris"`. Requires `channelId`.                                    |
| `channelId`                | string | If `paymentMethod` | `"MANDIRI"`, `"BRI"` for VA. `"SQ"` for QRIS.                                |
| `expiryPeriod`             | number | —                  | Payment window in minutes. Default `120`.                                    |
| `productDetails`           | string | —                  | Custom note on payment page. Max 255 chars.                                  |
| `customerDetail`           | object | —                  | `{ firstName, lastName, email }`. Each max 50 chars.                         |

***

### Flow A — Hosted checkout

#### Request

```json
{
  "toBeMinted": "20000",
  "destinationWalletAddress": "0x7D1398C397C64368B7079bD5b3EE904aaaf8A495",
  "networkChainId": "8453",
  "returnUrl": "https://your-app.example.com/callback",
  "expiryPeriod": 60,
  "requestType": "idrx"
}
```

#### Response

```json
{
  "statusCode": 200,
  "message": "success",
  "data": {
    "id": "85",
    "merchantOrderId": "20230321091546",
    "merchantCode": "DS15079",
    "reference": "DS1507923DW1N5088J6LW6TO",
    "paymentUrl": "https://app.duitku.com/redirect_checkout?reference=DS1507923DW1N5088J6LW6TO",
    "amount": "19500.00",
    "statusCode": "00",
    "statusMessage": "SUCCESS"
  }
}
```

1. Redirect the user to `paymentUrl`.
2. User picks a payment method and pays.
3. User is redirected to your `returnUrl`.
4. You receive a callback on settlement.

{% hint style="info" %}
**Fee handling depends on the payment method the user picks.** For VA and e-wallet, the fee is added on top of `toBeMinted` (the user pays slightly more). For QRIS, the fee is deducted from the minted IDRX. See [Fee structure by payment method](/api/callback.md#fee-structure-by-payment-method) for the full matrix.
{% endhint %}

{% hint style="warning" %}
Don't rely on the `returnUrl` redirect to confirm payment. Users close browsers. Always confirm via [callback](/api/callback.md) or [Transaction History](/api/transaction-api/get-api-transaction-user-transaction-history.md).
{% endhint %}

***

### Flow B — Direct payment

#### Virtual Account

**Request**

```json
{
  "toBeMinted": "20000",
  "destinationWalletAddress": "0x7D1398C397C64368B7079bD5b3EE904aaaf8A495",
  "networkChainId": "8453",
  "requestType": "idrx",
  "paymentMethod": "va",
  "channelId": "MANDIRI",
  "expiryPeriod": 60
}
```

**Response**

```json
{
  "statusCode": 200,
  "message": "success",
  "data": {
    "id": 1234,
    "merchantOrderId": "20260507130000",
    "reference": "SNAP-20260507130000",
    "virtualAccountNo": "89410340507084345",
    "virtualAccountName": "Customer Name",
    "amount": 20000,
    "expiredDate": "2026-05-08T13:00:00+0700"
  }
}
```

Display `virtualAccountNo` with a copy button. User pays the exact `amount` from their bank app.

***

#### QRIS

**Request**

```json
{
  "toBeMinted": "20000",
  "destinationWalletAddress": "0x7D1398C397C64368B7079bD5b3EE904aaaf8A495",
  "networkChainId": "8453",
  "requestType": "idrx",
  "paymentMethod": "qris",
  "channelId": "SQ",
  "expiryPeriod": 60
}
```

**Response**

```json
{
  "statusCode": 200,
  "message": "success",
  "data": {
    "id": 1234,
    "merchantOrderId": "20260507130000",
    "reference": "SNAP-20260507130000",
    "qrContent": "00020101021126...",
    "referenceNo": "QR-001-XXXXX",
    "redirectUrl": "https://passport.duitku.com/TopUp/v2/TopUpQrisPaymentPage.aspx?reference=...",
    "amount": 20000,
    "expiredDate": "2026-05-08T13:00:00+0700"
  }
}
```

Render `qrContent` as a QR image. Don't decode it — pass it as-is.

```jsx
// React
import { QRCodeSVG } from "qrcode.react";

<QRCodeSVG value={data.qrContent} size={256} />
```

```javascript
// Browser
import QRCode from "qrcode";

await QRCode.toCanvas(
  canvasEl,
  data.qrContent,
  { width: 256 }
);
```

Or redirect to `redirectUrl` for a hosted QR page.

{% hint style="info" %}
`qrContent` is an EMVCo TLV payload (Indonesian QRIS standard), not a URL. Starts with `000201...`. Works with GoPay, OVO, Dana, ShopeePay, mobile banking.
{% endhint %}

***

#### Recover lost payload

If the user closes your app before paying, fetch the cached payload instead of creating a new order:

```bash
curl -X GET 'https://idrx.co/api/duitku-snap/checkout/payment-payload?merchantOrderId=<MERCHANT_ORDER_ID>' \
  -H 'idrx-api-key: <API_KEY>' \
  -H 'idrx-api-sig: <SIGNATURE>' \
  -H 'idrx-api-ts: <TIMESTAMP>' \
  -H 'User-Agent: my-app/1.0'
```

Returns the same VA / QRIS payload plus current `paymentStatus`.

Only the partner that created the order can fetch it.

***

### Confirming payment

A transaction has two status fields you must track together:

| Field            | Tracks                                  |
| ---------------- | --------------------------------------- |
| `paymentStatus`  | The fiat payment (VA / QRIS / checkout) |
| `userMintStatus` | The on-chain token delivery             |

#### Success state

```
paymentStatus: PAID
userMintStatus: MINTED
```

#### Terminal states

* `MINTED`
* `REFUND`
* `REJECTED`
* `paymentStatus: EXPIRED`

Confirm via [callback](/api/callback.md), or poll [Transaction History](/api/transaction-api/get-api-transaction-user-transaction-history.md) by `merchantOrderId` and stop on a terminal state.

> Full state machine, reconciliation pattern, and edge cases: [Processing Mint IDRX Requests](/integration/processing-mint-idrx-requests.md#step-2-track-the-status)

{% hint style="warning" %}
**Callbacks are sent once and not retried.** If your endpoint is down, you must reconcile by polling the Transaction History API. Always re-fetch the transaction before crediting users — treat the callback body as untrusted input.
{% endhint %}

***

### Errors

| Status | Cause                                | Fix                                                                        |
| ------ | ------------------------------------ | -------------------------------------------------------------------------- |
| `400`  | Invalid parameter                    | Check param types and limits                                               |
| `401`  | Bad signature or timestamp drift     | Recompute signature; sync server clock (NTP)                               |
| `403`  | Blocked `User-Agent`                 | Set custom `User-Agent` like `my-app/1.0`                                  |
| `422`  | Unsupported `channelId` for merchant | Contact <support@idrx.co> to enable                                        |
| `429`  | Rate limit                           | Back off and retry                                                         |
| `5xx`  | Server error                         | Retry with backoff. Don't create a new order — recover the payload instead |

***

### See also

* [Callback](/api/callback.md) — webhook payload structure, fee breakdown per payment method, and reconciliation tips.
* [Processing Mint IDRX Requests](/integration/processing-mint-idrx-requests.md) — end-to-end mint workflow with status state machine and edge cases.
* [Transaction History API](/api/transaction-api/get-api-transaction-user-transaction-history.md) — query transaction state by `merchantOrderId`.
* [Generating a Signature](/api/generating-a-signature.md) — HMAC-SHA256 signature reference with code examples.

***

The returned `amount` value will be different from the request in `toBeMinted` parameter. This is the amount after fees and will be the amount that the user are going to pay.

Use the payment page in `paymentUrl` to pay the amount required. Shortly after the payment is done, IDRX tokens will be minted and sent to the specified destination wallet address. In the case of "usdt" `requestType`, USDT tokens will be sent to the wallet address.

To check the status of the transaction, you can use the [Transaction History API](/api/transaction-api/get-api-transaction-user-transaction-history.md).


---

# 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/transaction-api/post-api-transaction-mint-request.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.
