API reference

Invoices

Hosted-checkout invoices: a price, a deadline, and an address per chain (merchant accounts).

GET/v1/invoices
AuthorizationBearer <token>

Authorization: Bearer mp<64 hex> for live, mptest<64 hex> for test.

The token is the prefix plus 64 lowercase hex characters (32 CSPRNG bytes) and nothing else — no separators, no embedded fields — so nothing in it needs quoting, escaping or encoding. Send it in the Authorization header and nowhere else. Never put it in a query string: URLs reach access logs, proxy logs, browser history and Referer headers, and a key that lands in any of those is a leaked key. A key's KIND (checkout, payout or wallet) is a property of the key, not of the string: it is chosen at creation, returned by GET /v1/ping, and decides which routes the key may reach.

The full key is shown exactly once, at creation. No endpoint can return it afterwards. Moving funds additionally requires signing to be enabled globally, for your project, and for that key — so a read-only key is a supported configuration.

In: header

Query Parameters

limit?integer
Range1 <= value <= 100
Default25
cursor?string

Opaque cursor from the previous page's next_cursor.

status?string

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

curl -X GET "https://example.com/v1/invoices"
{  "object": "list",  "has_more": true,  "next_cursor": "string",  "data": [    {      "object": "invoice",      "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",      "reference_id": "string",      "title": "string",      "price_amount": "string",      "payer_amount": "string",      "payer_fee_cents": "string",      "price_currency": "string",      "status": "string",      "received_amount": "string",      "fee_bps": 0,      "fee_fixed_cents": "string",      "fee_cap_cents": "string",      "expires_at": "2019-08-24T14:15:22Z",      "paid_at": "2019-08-24T14:15:22Z",      "created_at": "2019-08-24T14:15:22Z"    }  ]}
POST/v1/invoices

Mints one payment address per chain you accept (identical on every EVM chain) and locks a rate per asset for the life of the invoice. The payer sends the quoted atomic amount to the address; the settler credits it once final.

price_amount is integer MINOR units of price_currency (cents), as a string. Only USD is priced today.

The fee is frozen onto the invoice at creation (fee_bps, fee_fixed_cents, fee_cap_cents) from your plan and is charged to your operational balance when the payment credits — never taken out of the coins.

A reference_id makes the create idempotent: a retry returns the FIRST invoice, addresses and rates included.

Live invoices require an active, funded plan (see GET /v1/billing); test projects are never gated.

AuthorizationBearer <token>

Authorization: Bearer mp<64 hex> for live, mptest<64 hex> for test.

The token is the prefix plus 64 lowercase hex characters (32 CSPRNG bytes) and nothing else — no separators, no embedded fields — so nothing in it needs quoting, escaping or encoding. Send it in the Authorization header and nowhere else. Never put it in a query string: URLs reach access logs, proxy logs, browser history and Referer headers, and a key that lands in any of those is a leaked key. A key's KIND (checkout, payout or wallet) is a property of the key, not of the string: it is chosen at creation, returned by GET /v1/ping, and decides which routes the key may reach.

The full key is shown exactly once, at creation. No endpoint can return it afterwards. Moving funds additionally requires signing to be enabled globally, for your project, and for that key — so a read-only key is a supported configuration.

In: header

Header Parameters

Idempotency-Key?string

Recommended. Replays the stored response for a repeated key.

Lengthlength <= 255

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

curl -X POST "https://example.com/v1/invoices" \  -H "Content-Type: application/json" \  -d '{    "price_amount": "string"  }'
{  "object": "invoice",  "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",  "reference_id": "string",  "title": "string",  "description": "string",  "price_amount": "string",  "payer_amount": "string",  "payer_fee_cents": "string",  "tolerance_bps": 0,  "overpayment_policy": "keep",  "fee_to_payer_bps": 0,  "markup_bps": 0,  "price_currency": "string",  "status": "open",  "fee_bps": 0,  "fee_fixed_cents": "string",  "fee_cap_cents": "string",  "metadata": {},  "customer": {    "name": "string",    "email": "string",    "reference_id": "string"  },  "return_url": "string",  "cancel_url": "string",  "expires_at": "2019-08-24T14:15:22Z",  "created_at": "2019-08-24T14:15:22Z",  "received_amount": "string",  "excess_amount": "string",  "checkout_url": "string",  "checkout_mode": "hosted",  "client_token": "string",  "paid_in": {    "chain": "ethereum",    "asset": "string",    "decimals": 0,    "received_atomic": "1500000000000000000",    "refunded_atomic": "1500000000000000000",    "refundable_atomic": "1500000000000000000"  },  "addresses": [    {      "chain": "ethereum",      "address": "string",      "memo": "string"    }  ],  "quotes": [    {      "chain": "ethereum",      "asset": "string",      "contract": "string",      "amount_atomic": "1500000000000000000",      "amount": "string",      "decimals": 0,      "rate": "string",      "rate_source": "string"    }  ]}
GET/v1/invoices/{id}
AuthorizationBearer <token>

Authorization: Bearer mp<64 hex> for live, mptest<64 hex> for test.

The token is the prefix plus 64 lowercase hex characters (32 CSPRNG bytes) and nothing else — no separators, no embedded fields — so nothing in it needs quoting, escaping or encoding. Send it in the Authorization header and nowhere else. Never put it in a query string: URLs reach access logs, proxy logs, browser history and Referer headers, and a key that lands in any of those is a leaked key. A key's KIND (checkout, payout or wallet) is a property of the key, not of the string: it is chosen at creation, returned by GET /v1/ping, and decides which routes the key may reach.

The full key is shown exactly once, at creation. No endpoint can return it afterwards. Moving funds additionally requires signing to be enabled globally, for your project, and for that key — so a read-only key is a supported configuration.

In: header

Path Parameters

id*string

The invoice's public id.

Formatuuid

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

curl -X GET "https://example.com/v1/invoices/497f6eca-6276-4993-bfeb-53cbbbba6f08"
{  "object": "invoice",  "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",  "reference_id": "string",  "title": "string",  "description": "string",  "price_amount": "string",  "payer_amount": "string",  "payer_fee_cents": "string",  "tolerance_bps": 0,  "overpayment_policy": "keep",  "fee_to_payer_bps": 0,  "markup_bps": 0,  "price_currency": "string",  "status": "open",  "fee_bps": 0,  "fee_fixed_cents": "string",  "fee_cap_cents": "string",  "metadata": {},  "customer": {    "name": "string",    "email": "string",    "reference_id": "string"  },  "return_url": "string",  "cancel_url": "string",  "expires_at": "2019-08-24T14:15:22Z",  "created_at": "2019-08-24T14:15:22Z",  "received_amount": "string",  "excess_amount": "string",  "checkout_url": "string",  "checkout_mode": "hosted",  "client_token": "string",  "paid_in": {    "chain": "ethereum",    "asset": "string",    "decimals": 0,    "received_atomic": "1500000000000000000",    "refunded_atomic": "1500000000000000000",    "refundable_atomic": "1500000000000000000"  },  "addresses": [    {      "chain": "ethereum",      "address": "string",      "memo": "string"    }  ],  "quotes": [    {      "chain": "ethereum",      "asset": "string",      "contract": "string",      "amount_atomic": "1500000000000000000",      "amount": "string",      "decimals": 0,      "rate": "string",      "rate_source": "string"    }  ]}
POST/v1/invoices/{id}/cancel

Closes the window early, for an order that was abandoned or replaced. Needs invoices:write rather than a payout scope, because cancelling moves no money.

šŸ”“ IT DOES NOT CLOSE THE PAYMENT ADDRESS. The address is an ordinary keyless customer wallet and stays watched forever, so a transfer arriving afterwards is still detected and still credited — surfaced as late, never voided.

An invoice that has already received money cannot be cancelled: reversing a credited payment is a refund, which needs a destination only the payer can give.

AuthorizationBearer <token>

Authorization: Bearer mp<64 hex> for live, mptest<64 hex> for test.

The token is the prefix plus 64 lowercase hex characters (32 CSPRNG bytes) and nothing else — no separators, no embedded fields — so nothing in it needs quoting, escaping or encoding. Send it in the Authorization header and nowhere else. Never put it in a query string: URLs reach access logs, proxy logs, browser history and Referer headers, and a key that lands in any of those is a leaked key. A key's KIND (checkout, payout or wallet) is a property of the key, not of the string: it is chosen at creation, returned by GET /v1/ping, and decides which routes the key may reach.

The full key is shown exactly once, at creation. No endpoint can return it afterwards. Moving funds additionally requires signing to be enabled globally, for your project, and for that key — so a read-only key is a supported configuration.

In: header

Path Parameters

id*string

The invoice's public id.

Formatuuid

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

curl -X POST "https://example.com/v1/invoices/497f6eca-6276-4993-bfeb-53cbbbba6f08/cancel"
{  "object": "invoice",  "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",  "reference_id": "string",  "title": "string",  "description": "string",  "price_amount": "string",  "payer_amount": "string",  "payer_fee_cents": "string",  "tolerance_bps": 0,  "overpayment_policy": "keep",  "fee_to_payer_bps": 0,  "markup_bps": 0,  "price_currency": "string",  "status": "open",  "fee_bps": 0,  "fee_fixed_cents": "string",  "fee_cap_cents": "string",  "metadata": {},  "customer": {    "name": "string",    "email": "string",    "reference_id": "string"  },  "return_url": "string",  "cancel_url": "string",  "expires_at": "2019-08-24T14:15:22Z",  "created_at": "2019-08-24T14:15:22Z",  "received_amount": "string",  "excess_amount": "string",  "checkout_url": "string",  "checkout_mode": "hosted",  "client_token": "string",  "paid_in": {    "chain": "ethereum",    "asset": "string",    "decimals": 0,    "received_atomic": "1500000000000000000",    "refunded_atomic": "1500000000000000000",    "refundable_atomic": "1500000000000000000"  },  "addresses": [    {      "chain": "ethereum",      "address": "string",      "memo": "string"    }  ],  "quotes": [    {      "chain": "ethereum",      "asset": "string",      "contract": "string",      "amount_atomic": "1500000000000000000",      "amount": "string",      "decimals": 0,      "rate": "string",      "rate_source": "string"    }  ]}
POST/v1/invoices/{id}/refund

Sends amount of the coin the invoice was paid in to to_address, from the funds that invoice brought in. Omit amount to return everything still refundable (paid_in.refundable_atomic on the invoice). Partial refunds may be repeated up to that bound; when the whole receipt has been returned the invoice becomes refunded.

šŸ”“ A payout key, signing on, Idempotency-Key required — this is money out to an address of your choosing, and the credential in a storefront must never be able to do that. Priced as a payout: your plan's payout fee from the prepaid balance, the network fee as on any payout; above the approval threshold, or past the daily automatic line, the carrying payout waits for MolnPay's review (never refused for the daily total). The payout allowlist does NOT apply — the destination is the payer's, not yours.

201 is a promise, not a fact: the refund is sent while the carrying payout is queued or in flight. invoice.refunded fires when it confirms on chain; invoice.refund_failed if it does not, and the amount returns to your balance.

AuthorizationBearer <token>

Authorization: Bearer mp<64 hex> for live, mptest<64 hex> for test.

The token is the prefix plus 64 lowercase hex characters (32 CSPRNG bytes) and nothing else — no separators, no embedded fields — so nothing in it needs quoting, escaping or encoding. Send it in the Authorization header and nowhere else. Never put it in a query string: URLs reach access logs, proxy logs, browser history and Referer headers, and a key that lands in any of those is a leaked key. A key's KIND (checkout, payout or wallet) is a property of the key, not of the string: it is chosen at creation, returned by GET /v1/ping, and decides which routes the key may reach.

The full key is shown exactly once, at creation. No endpoint can return it afterwards. Moving funds additionally requires signing to be enabled globally, for your project, and for that key — so a read-only key is a supported configuration.

In: header

Path Parameters

id*string

The invoice's public id.

Formatuuid

Header Parameters

Idempotency-Key*string

REQUIRED. A retry without one can send funds twice, so the request is rejected outright.

Lengthlength <= 255

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

curl -X POST "https://example.com/v1/invoices/497f6eca-6276-4993-bfeb-53cbbbba6f08/refund" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "to_address": "string"  }'
{  "object": "refund",  "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",  "invoice_id": "f4c4edb8-11e0-4b33-bcc1-482dc59ebb32",  "kind": "overpayment",  "status": "awaiting_address",  "chain": "ethereum",  "asset": "string",  "contract": "string",  "decimals": 0,  "amount_atomic": "1500000000000000000",  "amount": "string",  "fee_atomic": "1500000000000000000",  "net_atomic": "1500000000000000000",  "to_address": "string",  "reason": "string",  "payout_id": "89e4daf5-d103-4869-b9da-3d54ecc98dfe",  "tx_hash": "string",  "failure_code": "string",  "skip_reason": "string",  "network_fee_paid_by": "payer",  "created_at": "2019-08-24T14:15:22Z",  "sent_at": "2019-08-24T14:15:22Z",  "completed_at": "2019-08-24T14:15:22Z"}
GET/v1/invoices/{id}/checkout

The same payer-safe document GET /v1/checkout/{token} returns, from the same builder — fetched with your API key instead of the payer's token, so your customer's browser never has to reach us.

šŸ”“ BUILD YOUR PAGE FROM THIS, NEVER FROM GET /v1/invoices/{id}. That response's quotes[] is the FULL price, always, because that is the figure the rate was locked at. Render it on a part-paid invoice and you tell a payer who has already sent $31 of a $50 order to send $50 more; they overpay by $31, and that needs a refund we may not be able to make. The options[] here are the REMAINDER, re-priced at the same locked rate with integer arithmetic that rounds up.

šŸ”“ It stays payer-safe, so you may relay it to a browser verbatim: no fee in any form, no metadata, no reference_id, no internal ids. Piping it straight through does not leak your own rate card to your own customer.

Costs your key's plan rate limit, not the anonymous 120/min the token route runs on — that bucket is keyed on IP, so proxying every payer through one backend would share one bucket between all of them.

Available from the Growth plan up; test keys are never gated.

AuthorizationBearer <token>

Authorization: Bearer mp<64 hex> for live, mptest<64 hex> for test.

The token is the prefix plus 64 lowercase hex characters (32 CSPRNG bytes) and nothing else — no separators, no embedded fields — so nothing in it needs quoting, escaping or encoding. Send it in the Authorization header and nowhere else. Never put it in a query string: URLs reach access logs, proxy logs, browser history and Referer headers, and a key that lands in any of those is a leaked key. A key's KIND (checkout, payout or wallet) is a property of the key, not of the string: it is chosen at creation, returned by GET /v1/ping, and decides which routes the key may reach.

The full key is shown exactly once, at creation. No endpoint can return it afterwards. Moving funds additionally requires signing to be enabled globally, for your project, and for that key — so a read-only key is a supported configuration.

In: header

Path Parameters

id*string

The invoice's public id.

Formatuuid

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

curl -X GET "https://example.com/v1/invoices/497f6eca-6276-4993-bfeb-53cbbbba6f08/checkout"
{  "object": "checkout",  "status": "open",  "merchant": {    "name": "string",    "logo_url": "string"  },  "title": "string",  "description": "string",  "currency": "string",  "price": "string",  "payer_amount": "string",  "received": "string",  "remaining": "string",  "excess": "string",  "expires_at": "2019-08-24T14:15:22Z",  "window_started_at": "2019-08-24T14:15:22Z",  "extended": true,  "overpayment_policy": "keep",  "selected": {    "chain": "ethereum",    "asset": "string"  },  "options": [    {      "chain": "ethereum",      "asset": "string",      "name": "string",      "decimals": 0,      "address": "string",      "memo": "string",      "amount": "string",      "amount_atomic": "1500000000000000000",      "rate": "string",      "stable": true,      "refresh_at": "2019-08-24T14:15:22Z",      "uri": "string"    }  ],  "payments": [    {      "chain": "ethereum",      "asset": "string",      "amount": "string",      "tx_hash": "string",      "explorer_url": "string",      "confirmations": 0,      "required_confirmations": 0,      "status": "pending",      "at": "2019-08-24T14:15:22Z",      "receipt_no": "string"    }  ],  "refund": {    "status": "awaiting_address",    "skip_reason": "string",    "chain": "ethereum",    "asset": "string",    "gross": "string",    "fee": "string",    "net": "string",    "sender_class": "personal",    "suggested_address": "string",    "to_address": "string",    "tx_hash": "string",    "explorer_url": "string"  },  "return_url": "string"}
POST/v1/invoices/{id}/checkout/select

The authenticated twin of POST /v1/checkout/{token}/select, for a page you render yourself.

šŸ”“ IT LOCKS NOTHING AND STARTS NO CLOCK. The invoice's window begins at creation, and one address serves every EVM chain, so money arriving on any offered chain settles regardless of what was sent here. It is a display preference and a hint that tells the re-quoter which asset somebody is actually reading.

AuthorizationBearer <token>

Authorization: Bearer mp<64 hex> for live, mptest<64 hex> for test.

The token is the prefix plus 64 lowercase hex characters (32 CSPRNG bytes) and nothing else — no separators, no embedded fields — so nothing in it needs quoting, escaping or encoding. Send it in the Authorization header and nowhere else. Never put it in a query string: URLs reach access logs, proxy logs, browser history and Referer headers, and a key that lands in any of those is a leaked key. A key's KIND (checkout, payout or wallet) is a property of the key, not of the string: it is chosen at creation, returned by GET /v1/ping, and decides which routes the key may reach.

The full key is shown exactly once, at creation. No endpoint can return it afterwards. Moving funds additionally requires signing to be enabled globally, for your project, and for that key — so a read-only key is a supported configuration.

In: header

Path Parameters

id*string

The invoice's public id.

Formatuuid

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

curl -X POST "https://example.com/v1/invoices/497f6eca-6276-4993-bfeb-53cbbbba6f08/checkout/select" \  -H "Content-Type: application/json" \  -d '{    "chain": "ethereum",    "asset": "string"  }'
{  "object": "checkout",  "status": "open",  "merchant": {    "name": "string",    "logo_url": "string"  },  "title": "string",  "description": "string",  "currency": "string",  "price": "string",  "payer_amount": "string",  "received": "string",  "remaining": "string",  "excess": "string",  "expires_at": "2019-08-24T14:15:22Z",  "window_started_at": "2019-08-24T14:15:22Z",  "extended": true,  "overpayment_policy": "keep",  "selected": {    "chain": "ethereum",    "asset": "string"  },  "options": [    {      "chain": "ethereum",      "asset": "string",      "name": "string",      "decimals": 0,      "address": "string",      "memo": "string",      "amount": "string",      "amount_atomic": "1500000000000000000",      "rate": "string",      "stable": true,      "refresh_at": "2019-08-24T14:15:22Z",      "uri": "string"    }  ],  "payments": [    {      "chain": "ethereum",      "asset": "string",      "amount": "string",      "tx_hash": "string",      "explorer_url": "string",      "confirmations": 0,      "required_confirmations": 0,      "status": "pending",      "at": "2019-08-24T14:15:22Z",      "receipt_no": "string"    }  ],  "refund": {    "status": "awaiting_address",    "skip_reason": "string",    "chain": "ethereum",    "asset": "string",    "gross": "string",    "fee": "string",    "net": "string",    "sender_class": "personal",    "suggested_address": "string",    "to_address": "string",    "tx_hash": "string",    "explorer_url": "string"  },  "return_url": "string"}
POST/v1/invoices/{id}/checkout/refund

The authenticated twin of POST /v1/checkout/{token}/refund.

šŸ”“ IF YOU RENDER YOUR OWN PAGE, YOU MUST BUILD THIS FORM. An embedded checkout that shows the amount and the address but never asks the question leaves an overpayment sitting in awaiting_address for ever — the payer is never prompted, and the money is stranded by an omission that looks like nothing at all from your side. Watch for status: "overpaid" with a refund block.

šŸ”“ YOUR API KEY IS NOT YOUR PAYER'S CONSENT. Every gate the payer-facing route applies applies here unchanged: the address is validated through the destination chain's own adapter rather than a regex, an address this service custodies is refused outright, and nothing is accepted until we have classified who paid and priced what the send costs (refund_not_ready until then). It is still the payer's money and still a transfer nobody can take back.

Wait for BOTH refund.sender_class and refund.net to be non-null before you show the form. Idempotent on a repeat of the SAME address; a different one is refused once the refund is pending.

Ten a minute, matching the payer-facing route.

AuthorizationBearer <token>

Authorization: Bearer mp<64 hex> for live, mptest<64 hex> for test.

The token is the prefix plus 64 lowercase hex characters (32 CSPRNG bytes) and nothing else — no separators, no embedded fields — so nothing in it needs quoting, escaping or encoding. Send it in the Authorization header and nowhere else. Never put it in a query string: URLs reach access logs, proxy logs, browser history and Referer headers, and a key that lands in any of those is a leaked key. A key's KIND (checkout, payout or wallet) is a property of the key, not of the string: it is chosen at creation, returned by GET /v1/ping, and decides which routes the key may reach.

The full key is shown exactly once, at creation. No endpoint can return it afterwards. Moving funds additionally requires signing to be enabled globally, for your project, and for that key — so a read-only key is a supported configuration.

In: header

Path Parameters

id*string

The invoice's public id.

Formatuuid

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

curl -X POST "https://example.com/v1/invoices/497f6eca-6276-4993-bfeb-53cbbbba6f08/checkout/refund" \  -H "Content-Type: application/json" \  -d '{    "address": "string"  }'
{  "object": "checkout",  "status": "open",  "merchant": {    "name": "string",    "logo_url": "string"  },  "title": "string",  "description": "string",  "currency": "string",  "price": "string",  "payer_amount": "string",  "received": "string",  "remaining": "string",  "excess": "string",  "expires_at": "2019-08-24T14:15:22Z",  "window_started_at": "2019-08-24T14:15:22Z",  "extended": true,  "overpayment_policy": "keep",  "selected": {    "chain": "ethereum",    "asset": "string"  },  "options": [    {      "chain": "ethereum",      "asset": "string",      "name": "string",      "decimals": 0,      "address": "string",      "memo": "string",      "amount": "string",      "amount_atomic": "1500000000000000000",      "rate": "string",      "stable": true,      "refresh_at": "2019-08-24T14:15:22Z",      "uri": "string"    }  ],  "payments": [    {      "chain": "ethereum",      "asset": "string",      "amount": "string",      "tx_hash": "string",      "explorer_url": "string",      "confirmations": 0,      "required_confirmations": 0,      "status": "pending",      "at": "2019-08-24T14:15:22Z",      "receipt_no": "string"    }  ],  "refund": {    "status": "awaiting_address",    "skip_reason": "string",    "chain": "ethereum",    "asset": "string",    "gross": "string",    "fee": "string",    "net": "string",    "sender_class": "personal",    "suggested_address": "string",    "to_address": "string",    "tx_hash": "string",    "explorer_url": "string"  },  "return_url": "string"}