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
| Environment | Base URL | Key prefix |
|---|---|---|
| Sandbox | https://sandbox-api.myrt.money/v1 | myrt_sandbox_ |
| Live | https://api.myrt.money/v1 | myrt_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:
{ "ok": true, "serverTime": "2026-09-10T04:12:33.918Z", "timezone": "UTC" }A failure carries an error object:
{
"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
| Header | When | Value |
|---|---|---|
Authorization | every authenticated call | Bearer <key> |
Idempotency-Key | every POST | 1 to 255 characters; a UUID per logical operation |
Content-Type | every POST | application/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
| Header | Meaning |
|---|---|
X-MYRT-API-Version | Always v1 |
X-MYRT-Idempotent-Replay | true when a POST was answered from the stored original response |
X-RateLimit-Limit | Requests allowed per minute in this bucket |
X-RateLimit-Remaining | Requests left in the current window |
X-RateLimit-Reset | Unix time in seconds when the window resets |
Retry-After | Only 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.
curl https://sandbox-api.myrt.money/v1/healthconst res = await fetch("https://sandbox-api.myrt.money/v1/health");
const health = await res.json();import requests
res = requests.get("https://sandbox-api.myrt.money/v1/health", timeout=30)
health = res.json()200 OK
{ "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.
curl https://sandbox-api.myrt.money/v1/tokens \
-H "Authorization: Bearer $MYRT_API_KEY"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}`);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
{
"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
| Method | Path | Scope | Description |
|---|---|---|---|
| GET | /v1/health | none | Service liveness |
| GET | /v1/system/time | none | Server time, for clock-skew checks |
| GET | /v1/config | config:read | Chains, decimals, endpoint map |
| GET | /v1/tokens | config:read | MYRT token address per chain |
Mints
| Method | Path | Scope | Description |
|---|---|---|---|
| POST | /v1/mints | mint:create | Create a mint order |
| GET | /v1/mints/{referenceId} | mint:read | Read a mint order |
| POST | /v1/mints/{referenceId}/cancelPlanned | mint:create | Cancel a mint order before settlement |
Redeems
| Method | Path | Scope | Description |
|---|---|---|---|
| POST | /v1/redeems | redeem:create | Create a redeem order |
| GET | /v1/redeems/{referenceId} | redeem:read | Read a redeem order |
Transfers
| Method | Path | Scope | Description |
|---|---|---|---|
| POST | /v1/transfers | transfer:create | Create a transfer |
| GET | /v1/transfers/{referenceId} | transfer:read | Read a transfer |
Transactions
| Method | Path | Scope | Description |
|---|---|---|---|
| GET | /v1/transactionsPlanned | transaction:read | List your orders, paginated |
| GET | /v1/transactions/{referenceId} | transaction:read | Read any order you created |
Balances
| Method | Path | Scope | Description |
|---|---|---|---|
| GET | /v1/balances/{walletAddress} | balance:read | On-chain MYRT balance |
Accounts
| Method | Path | Scope | Description |
|---|---|---|---|
| GET | /v1/customers/{customerId}Planned | account:read | Customer verification status |
| GET | /v1/corporates/{corporateId}Planned | account:read | Corporate verification status |
HTTP status codes
| Status | Meaning |
|---|---|
200 | The request succeeded. Reads and cancels return 200 with the object |
201 | A mint, redeem or transfer order was created |
400 | The request failed validation. error.code names the rule, for example INVALID_AMOUNT or IDEMPOTENCY_KEY_REQUIRED |
401 | UNAUTHORIZED: missing, malformed, unknown, revoked, expired or wrong-environment key |
403 | The key or the party is not permitted: IP_NOT_ALLOWED, SCOPE_DENIED, CUSTOMER_NOT_VERIFIED or RECIPIENT_NOT_PERMITTED |
404 | NOT_FOUND: no such order or party, or not yours |
409 | IDEMPOTENCY_CONFLICT (key reused with a different body, or referenceId reused with different details) or ORDER_NOT_CANCELLABLE |
429 | RATE_LIMITED: the bucket is exhausted. Honour Retry-After |
500 | SERVER_ERROR: unexpected error. Retry with backoff, then quote meta.correlationId to support |
502 | SETTLEMENT_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.
