Skip to content

API reference

The MYRT partner API is plain HTTP with JSON bodies. There is no SDK. This page lists every v1 endpoint and the conventions they share; each endpoint's shapes are on its own reference page. The flows and the reasoning behind them are in the guides, starting with getting started.

Base URLs

EnvironmentBase URLKey prefix
Sandboxhttps://sandbox-api.myrt.money/v1myrt_sandbox_
Livehttps://api.myrt.money/v1myrt_live_

The version is in the path, and every response carries X-MYRT-API-Version: v1. See versioning for what can change within v1.

A key works only in the environment it was issued for. A sandbox key on live, or the reverse, returns 401 UNAUTHORIZED. Sandbox runs on a testnet with test fiat rails, so no real money moves. Read the chain and token address from /v1/config and /v1/tokens on the host you are talking to; never hardcode them. See network configuration.

The API is server-to-server. No CORS headers are sent, so browser calls are not supported.

Conventions

Requests and responses are JSON. Every POST sends a JSON object with Content-Type: application/json. A body that is not a JSON object returns 400 INVALID_JSON.

Envelope

Every response body has ok: true or ok: false. A success carries the result fields at the top level:

json
{ "ok": true, "serverTime": "2026-09-10T04:12:33.918Z", "timezone": "UTC" }

A failure carries an error object:

json
{
  "ok": false,
  "error": {
    "code": "INSUFFICIENT_BALANCE",
    "message": "Wallet has insufficient MYRT balance for redemption.",
    "meta": { "requestedMyrt": "500.00", "walletBalanceMyrt": "212.40" }
  }
}

error.code is stable and is what your integration branches on. error.message is for humans and may be reworded at any time. error.meta is present only where documented. The full catalogue is on the errors page.

Amounts

Amounts are decimal strings, never numbers. decimals is always 6. amountRaw is the exact on-chain integer, myrt scaled by 10^6, and is the value to compare with a Transfer event. Strings are not zero-padded: 1000 and 1000.00 are the same value, so parse with a decimal library and never compare amount strings. See amounts.

Timestamps

Timestamps are ISO 8601 in UTC, for example 2026-09-10T04:12:33.918Z. GET /v1/system/time returns the server clock so you can detect skew.

Not found

404 NOT_FOUND means no such order or party, or not yours. An order that another integration created, an order that is the wrong direction for the route (a redeem read through /v1/mints), and a customerId that is not linked to your integration all return the same 404 NOT_FOUND as one that does not exist. The response never says which.

Headers

Request headers

HeaderWhenValue
Authorizationevery authenticated callBearer <key>
Idempotency-Keyevery POST1 to 255 characters; a UUID per logical operation
Content-Typeevery POSTapplication/json

GET /v1/health and GET /v1/system/time take no key. Every other endpoint requires a key with the scope shown in the endpoint table; see scopes. A POST without an Idempotency-Key returns 400 IDEMPOTENCY_KEY_REQUIRED; see idempotency.

Response headers

HeaderMeaning
X-MYRT-API-VersionAlways v1
X-MYRT-Idempotent-Replaytrue when a POST was answered from the stored original response
X-RateLimit-LimitRequests allowed per minute in this bucket
X-RateLimit-RemainingRequests left in the current window
X-RateLimit-ResetUnix time in seconds when the window resets
Retry-AfterOnly on 429; seconds to wait

Rate limit buckets are described under rate limits and best practices.

Making a request

A public call

GET /v1/health takes no key.

bash
curl https://sandbox-api.myrt.money/v1/health
ts
const res = await fetch("https://sandbox-api.myrt.money/v1/health");
const health = await res.json();
python
import requests

res = requests.get("https://sandbox-api.myrt.money/v1/health", timeout=30)
health = res.json()

200 OK

json
{ "ok": true, "service": "myrt-api", "apiVersion": "v1", "status": "healthy", "timestamp": "2026-09-10T04:12:33.918Z" }

An authenticated call

GET /v1/tokens requires a key with config:read. The key comes from the MYRT_API_KEY environment variable in every sample.

bash
curl https://sandbox-api.myrt.money/v1/tokens \
  -H "Authorization: Bearer $MYRT_API_KEY"
ts
const res = await fetch("https://sandbox-api.myrt.money/v1/tokens", {
  headers: { Authorization: `Bearer ${process.env.MYRT_API_KEY}` },
});
const tokens = await res.json();
if (!tokens.ok) throw new Error(`${tokens.error.code}: ${tokens.error.message}`);
python
import os, requests

res = requests.get(
    "https://sandbox-api.myrt.money/v1/tokens",
    headers={"Authorization": f"Bearer {os.environ['MYRT_API_KEY']}"},
    timeout=30,
)
tokens = res.json()
if not tokens["ok"]:
    raise RuntimeError(f"{tokens['error']['code']}: {tokens['error']['message']}")

200 OK

json
{
  "ok": true,
  "tokens": [
    { "symbol": "MYRT", "name": "MYR Stablecoin", "chainId": 1, "network": "Ethereum", "tokenAddress": "0x...", "decimals": 6, "explorerUrl": "https://etherscan.io/address/0x..." }
  ]
}

A missing, malformed, unknown, revoked, expired or wrong-environment key returns 401 UNAUTHORIZED with the same message, Invalid or expired API key.

Endpoints

Every v1 endpoint, with its scope, rate limit bucket and availability; a Planned endpoint is part of the v1 contract and documented ahead of release, but not yet served.

Planned marks an endpoint that is part of the v1 contract and documented ahead of release. It is not yet served in production. Build against it only once the badge is gone.

System

MethodPathScopeDescription
GET/v1/healthnoneService liveness
GET/v1/system/timenoneServer time, for clock-skew checks
GET/v1/configconfig:readChains, decimals, endpoint map
GET/v1/tokensconfig:readMYRT token address per chain

Mints

MethodPathScopeDescription
POST/v1/mintsmint:createCreate a mint order
GET/v1/mints/{referenceId}mint:readRead a mint order
POST/v1/mints/{referenceId}/cancelPlannedmint:createCancel a mint order before settlement

Redeems

MethodPathScopeDescription
POST/v1/redeemsredeem:createCreate a redeem order
GET/v1/redeems/{referenceId}redeem:readRead a redeem order

Transfers

MethodPathScopeDescription
POST/v1/transferstransfer:createCreate a transfer
GET/v1/transfers/{referenceId}transfer:readRead a transfer

Transactions

MethodPathScopeDescription
GET/v1/transactionsPlannedtransaction:readList your orders, paginated
GET/v1/transactions/{referenceId}transaction:readRead any order you created

Balances

MethodPathScopeDescription
GET/v1/balances/{walletAddress}balance:readOn-chain MYRT balance

Accounts

MethodPathScopeDescription
GET/v1/customers/{customerId}Plannedaccount:readCustomer verification status
GET/v1/corporates/{corporateId}Plannedaccount:readCorporate verification status

HTTP status codes

StatusMeaning
200The request succeeded. Reads and cancels return 200 with the object
201A mint, redeem or transfer order was created
400The request failed validation. error.code names the rule, for example INVALID_AMOUNT or IDEMPOTENCY_KEY_REQUIRED
401UNAUTHORIZED: missing, malformed, unknown, revoked, expired or wrong-environment key
403The key or the party is not permitted: IP_NOT_ALLOWED, SCOPE_DENIED, CUSTOMER_NOT_VERIFIED or RECIPIENT_NOT_PERMITTED
404NOT_FOUND: no such order or party, or not yours
409IDEMPOTENCY_CONFLICT (key reused with a different body, or referenceId reused with different details) or ORDER_NOT_CANCELLABLE
429RATE_LIMITED: the bucket is exhausted. Honour Retry-After
500SERVER_ERROR: unexpected error. Retry with backoff, then quote meta.correlationId to support
502SETTLEMENT_FAILED: settlement submission failed. Retry with the same Idempotency-Key

Retry 429, 502, 503 and 504 with backoff, reusing the Idempotency-Key on a POST. Never retry any other 4xx. Each code's meaning and the action to take are on the errors page.

OpenAPI

The machine-readable contract is served at /openapi/myrt-api-v1.json. It is an OpenAPI 3.1 document and is suitable for generating a client. Its servers entries carry the /v1 prefix, so the paths inside it omit it. The endpoint table above, the reference pages and this document are checked against each other, so they cannot drift apart.

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