Skip to content

Redeem: burn MYRT

Redeem burns MYRT from a customer's wallet and pays the net MYR to the customer's registered bank account.

POST/v1/redeemsredeem:create
bash
curl -X POST https://sandbox-api.myrt.money/v1/redeems \
  -H "Authorization: Bearer $MYRT_API_KEY" \
  -H "Idempotency-Key: 7c9e6679-7425-40de-944b-e07fc1f90ae7" \
  -H "Content-Type: application/json" \
  -d '{
    "referenceId": "acme:redeem:2026-09-10:00092",
    "amountMyrt": "500.00",
    "walletAddress": "0xAbC0000000000000000000000000000000000001",
    "customerId": "cus_9f2a3b4c",
    "bankReference": "acme-payout-00092"
  }'
ts
import { randomUUID } from "node:crypto";

const res = await fetch("https://sandbox-api.myrt.money/v1/redeems", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.MYRT_API_KEY}`,
    "Idempotency-Key": randomUUID(),
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    referenceId: "acme:redeem:2026-09-10:00092",
    amountMyrt: "500.00",
    walletAddress: "0xAbC0000000000000000000000000000000000001",
    customerId: "cus_9f2a3b4c",
    bankReference: "acme-payout-00092",
  }),
});
const order = await res.json();
if (!order.ok) throw new Error(`${order.error.code}: ${order.error.message}`);
python
import os, uuid, requests

res = requests.post(
    "https://sandbox-api.myrt.money/v1/redeems",
    headers={
        "Authorization": f"Bearer {os.environ['MYRT_API_KEY']}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "referenceId": "acme:redeem:2026-09-10:00092",
        "amountMyrt": "500.00",
        "walletAddress": "0xAbC0000000000000000000000000000000000001",
        "customerId": "cus_9f2a3b4c",
        "bankReference": "acme-payout-00092",
    },
    timeout=30,
)
order = res.json()
if not order["ok"]:
    raise RuntimeError(f"{order['error']['code']}: {order['error']['message']}")

Request fields

FieldTypeRequiredNotes
referenceIdstringyesYour unique handle for this order. See reference IDs
amountMyrtstringyesPositive decimal, at most 6 decimal places
walletAddressstringyesChecksummed EVM address the MYRT is burned from
customerIdstringyesOwner of the wallet, linked to your integration
bankReferencestringnoYour payout reference, echoed in reconciliation
chainIdnumbernoDefaults to the active chain. See network configuration

Redeem needs both scopes

Creating a redeem order submits it for settlement in the same call. The key must hold both redeem:create and redeem:execute. A key with only redeem:create receives 403 SCOPE_DENIED. See scopes.

Redeem is a single call. There is no separate confirm step. Re-posting a referenceId that already exists on a redeem with different details returns 409 IDEMPOTENCY_CONFLICT.

What is checked before the order is accepted

The checks run in this order. The first one that fails is the error you receive.

  1. Balance. The wallet's on-chain MYRT balance is read. If it is short, the call returns 400 INSUFFICIENT_BALANCE with meta.walletAddress, meta.chainId, meta.requestedMyrt and meta.walletBalanceMyrt.
  2. Policy. AMOUNT_NEGATIVE_OR_ZERO when the amount is zero or negative. AMOUNT_ABOVE_REDEEM_CEILING when a single redemption is above RM 1,000,000. FOREIGNER_LIMIT_EXCEEDED when a non-resident customer redeems above RM 100,000 in one transaction. POLICY_REJECTED when no more specific reason applies; read message. All four are 400.
  3. Monthly withdrawal limit. Redemptions count against the customer's monthly withdrawal limit. If this order would exceed it, the call returns 400 MONTHLY_WITHDRAWAL_LIMIT_EXCEEDED with meta.monthlyLimit, meta.monthlyCommitted and meta.requestedAmount, all numbers in MYR.

A customer's monthly withdrawal cap and remaining headroom are readable through the accounts endpoints.

Response

201 Created

json
{
  "ok": true,
  "referenceId": "acme:redeem:2026-09-10:00092",
  "status": "pending",
  "phase": "ORDER_CREATED",
  "direction": "REDEEM",
  "chainId": 1,
  "network": "Ethereum",
  "tokenAddress": "0x...",
  "walletAddress": "0xAbC0000000000000000000000000000000000001",
  "amounts": {
    "myr": "500.00",
    "myrt": "500.00",
    "feeMyr": "0.00",
    "amountRaw": "500000000",
    "decimals": 6
  },
  "blockchain": { "txHash": null, "blockNumber": null },
  "timestamps": { "createdAt": "2026-09-10T04:12:33.918Z", "updatedAt": null }
}

For a redeem, myrt is the amount burned and myr is the net MYR paid out after feeMyr. walletAddress is the wallet the MYRT is burned from. Fees are quoted server-side and returned on the order. Do not compute them client-side.

status and phase are explained in core concepts. Drive your business logic from status. Amounts are decimal strings; see amounts.

Reading status

GET/v1/redeems/{referenceId}redeem:read

Returns the same object with updated status, phase, and blockchain.txHash. An order reaches completed only after the chain's confirmation count, so do not treat a transaction hash as final on its own.

bash
curl https://sandbox-api.myrt.money/v1/redeems/acme:redeem:2026-09-10:00092 \
  -H "Authorization: Bearer $MYRT_API_KEY"

completed means the burn is confirmed on chain and the MYR payout to the customer's registered bank account has been initiated. Bank settlement time is outside the API.

This route returns only redeem orders. A reference that belongs to a mint or a transfer, or to an order another integration created, returns 404 NOT_FOUND, the same as a missing one.

Polling works. Webhooks are preferred for settlement events.

Errors you should handle

CodeHTTPWhen
INVALID_AMOUNT400amountMyrt is not a positive decimal with at most 6 decimal places
INVALID_WALLET_ADDRESS400walletAddress is not a valid EVM address
INVALID_REFERENCE_ID400referenceId fails the format rule
INSUFFICIENT_BALANCE400The wallet holds less MYRT than amountMyrt. meta carries the requested and actual balances
AMOUNT_NEGATIVE_OR_ZERO400amountMyrt is zero or negative
AMOUNT_ABOVE_REDEEM_CEILING400A single redemption above RM 1,000,000
FOREIGNER_LIMIT_EXCEEDED400Above RM 100,000 per transaction for a non-resident customer
POLICY_REJECTED400Generic policy rejection. Read message
MONTHLY_WITHDRAWAL_LIMIT_EXCEEDED400The customer's monthly withdrawal limit would be exceeded. meta carries the limit, the amount committed and the amount requested
CUSTOMER_NOT_VERIFIED403The customer has not cleared verification
SCOPE_DENIED403The key lacks redeem:create or redeem:execute
NOT_FOUND404customerId is not linked to your integration
IDEMPOTENCY_CONFLICT409Same Idempotency-Key with a different body, or referenceId reused with different details
SETTLEMENT_FAILED502Settlement submission failed. Retry with the same Idempotency-Key

The full list is on the errors page. The request and response shapes are on the redeems reference.

MYRT is a 1:1 Ringgit-backed stablecoin on Ethereum.