API reference

Account

The person or company behind your merchants: finish sign-up, list your merchants, create another, switch between them (console sessions only).

GET/v1/account

Console sessions only — and the one read that works BEFORE you have a merchant. The signed-in person, their account (individual or business; null until sign-up's last step), every merchant they are in with their role and mode, and which merchant this session acts in.

Every other /v1/* route acts in the active merchant. A session that has not verified with Telegram gets 401 telegram_required from them (telegram says where that stands); one that is in no merchant gets 401 no_merchant until PUT /v1/account has run.

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

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

curl -X GET "https://example.com/v1/account"
{  "object": "account_home",  "user": {    "id": "string",    "name": "string",    "email": "user@example.com"  },  "account": {    "object": "account",    "account_type": "individual",    "legal_name": "string",    "country": "string",    "phone": "string",    "kyc_status": "unstarted",    "kyb_status": "unstarted",    "created_at": "2019-08-24T14:15:22Z"  },  "telegram": {    "required": true,    "verified": true,    "waived": true,    "phone": "string",    "username": "string",    "display_name": "string",    "linked_at": "2019-08-24T14:15:22Z"  },  "merchants": [    {      "object": "merchant_summary",      "billing_account_id": "db2251e5-5308-40a7-9f48-3aed5a9ce736",      "billing_shared": true,      "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",      "display_name": "string",      "logo_url": "string",      "website_url": "string",      "domain": "string",      "network": "testnet",      "status": "active",      "role": "owner",      "active": true,      "created_at": "2019-08-24T14:15:22Z"    }  ],  "active_merchant_id": "4a3e255e-f328-4684-9ba1-87c683d3cd25"}
PUT/v1/account

With no account yet: account_type is required. Creates the account and — if you are in no merchant and have verified with Telegram — your first merchant, named merchant_name (else your business name, else your own name), and makes it active. Before the Telegram step only the account row is written; the merchant follows when POST /v1/account/telegram/complete lands. This is sign-up's last step; the console repeats it at /welcome if it never ran.

With an account: updates legal_name, country and phone. account_type may go individual → business (KYB then starts unstarted); business → individual is refused with 409 downgrade_not_self_service. If you are still in no merchant (the first call made the account but not the merchant), this call makes it.

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

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

curl -X PUT "https://example.com/v1/account" \  -H "Content-Type: application/json" \  -d '{}'
{  "object": "account_home",  "user": {    "id": "string",    "name": "string",    "email": "user@example.com"  },  "account": {    "object": "account",    "account_type": "individual",    "legal_name": "string",    "country": "string",    "phone": "string",    "kyc_status": "unstarted",    "kyb_status": "unstarted",    "created_at": "2019-08-24T14:15:22Z"  },  "telegram": {    "required": true,    "verified": true,    "waived": true,    "phone": "string",    "username": "string",    "display_name": "string",    "linked_at": "2019-08-24T14:15:22Z"  },  "merchants": [    {      "object": "merchant_summary",      "billing_account_id": "db2251e5-5308-40a7-9f48-3aed5a9ce736",      "billing_shared": true,      "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",      "display_name": "string",      "logo_url": "string",      "website_url": "string",      "domain": "string",      "network": "testnet",      "status": "active",      "role": "owner",      "active": true,      "created_at": "2019-08-24T14:15:22Z"    }  ],  "active_merchant_id": "4a3e255e-f328-4684-9ba1-87c683d3cd25"}
POST/v1/account/merchants

Another website, brand or integration under the same account: its own project, API keys, webhooks and domain, with you as its owner. By default it SHARES your plan and prepaid balance (one top-up for every merchant) and, if that plan is already active, it is live from the start; billing: "separate" gives it a plan, balance and funding addresses of its own, starting in test mode. Made active for this session. Needs an account (409 account_required).

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

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

curl -X POST "https://example.com/v1/account/merchants" \  -H "Content-Type: application/json" \  -d '{    "display_name": "Acme Supply"  }'
{  "object": "account_home",  "user": {    "id": "string",    "name": "string",    "email": "user@example.com"  },  "account": {    "object": "account",    "account_type": "individual",    "legal_name": "string",    "country": "string",    "phone": "string",    "kyc_status": "unstarted",    "kyb_status": "unstarted",    "created_at": "2019-08-24T14:15:22Z"  },  "telegram": {    "required": true,    "verified": true,    "waived": true,    "phone": "string",    "username": "string",    "display_name": "string",    "linked_at": "2019-08-24T14:15:22Z"  },  "merchants": [    {      "object": "merchant_summary",      "billing_account_id": "db2251e5-5308-40a7-9f48-3aed5a9ce736",      "billing_shared": true,      "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",      "display_name": "string",      "logo_url": "string",      "website_url": "string",      "domain": "string",      "network": "testnet",      "status": "active",      "role": "owner",      "active": true,      "created_at": "2019-08-24T14:15:22Z"    }  ],  "active_merchant_id": "4a3e255e-f328-4684-9ba1-87c683d3cd25"}
POST/v1/account/telegram/start

Console sessions only. Every account verifies with Telegram — and shares the phone number on its Telegram — before it can set up a merchant, pay or create anything; until then every other route answers 401 telegram_required (GET /v1/account and these two excepted).

Returns the Telegram login URL to send the browser to. Telegram brings the browser back to the console, which calls POST /v1/account/telegram/complete with what it was given. A login attempt lives ten minutes.

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

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

curl -X POST "https://example.com/v1/account/telegram/start"
{  "url": "http://example.com",  "expires_at": "2019-08-24T14:15:22Z"}
POST/v1/account/telegram/complete

Console sessions only. The console posts the code and state Telegram returned (or error and state when the person declined). On success the account is verified and, if sign-up's answers were already given and the person is in no merchant, the first merchant is made and activated in the same call.

One Telegram per account and one account per Telegram: a Telegram or phone number already verifying another account is refused. An anonymous (+888) number is refused. A Telegram identity behind a banned account is refused.

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

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

curl -X POST "https://example.com/v1/account/telegram/complete" \  -H "Content-Type: application/json" \  -d '{    "state": "string"  }'
{  "object": "account_home",  "user": {    "id": "string",    "name": "string",    "email": "user@example.com"  },  "account": {    "object": "account",    "account_type": "individual",    "legal_name": "string",    "country": "string",    "phone": "string",    "kyc_status": "unstarted",    "kyb_status": "unstarted",    "created_at": "2019-08-24T14:15:22Z"  },  "telegram": {    "required": true,    "verified": true,    "waived": true,    "phone": "string",    "username": "string",    "display_name": "string",    "linked_at": "2019-08-24T14:15:22Z"  },  "merchants": [    {      "object": "merchant_summary",      "billing_account_id": "db2251e5-5308-40a7-9f48-3aed5a9ce736",      "billing_shared": true,      "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",      "display_name": "string",      "logo_url": "string",      "website_url": "string",      "domain": "string",      "network": "testnet",      "status": "active",      "role": "owner",      "active": true,      "created_at": "2019-08-24T14:15:22Z"    }  ],  "active_merchant_id": "4a3e255e-f328-4684-9ba1-87c683d3cd25"}
POST/v1/account/merchants/{id}/activate

Makes id (a merchant's public id from GET /v1/account) the merchant this session acts in — every other route follows. You must be a member of it. Better Auth's organization/set-active does the write.

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
Formatuuid

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

curl -X POST "https://example.com/v1/account/merchants/497f6eca-6276-4993-bfeb-53cbbbba6f08/activate"
{  "object": "account_home",  "user": {    "id": "string",    "name": "string",    "email": "user@example.com"  },  "account": {    "object": "account",    "account_type": "individual",    "legal_name": "string",    "country": "string",    "phone": "string",    "kyc_status": "unstarted",    "kyb_status": "unstarted",    "created_at": "2019-08-24T14:15:22Z"  },  "telegram": {    "required": true,    "verified": true,    "waived": true,    "phone": "string",    "username": "string",    "display_name": "string",    "linked_at": "2019-08-24T14:15:22Z"  },  "merchants": [    {      "object": "merchant_summary",      "billing_account_id": "db2251e5-5308-40a7-9f48-3aed5a9ce736",      "billing_shared": true,      "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",      "display_name": "string",      "logo_url": "string",      "website_url": "string",      "domain": "string",      "network": "testnet",      "status": "active",      "role": "owner",      "active": true,      "created_at": "2019-08-24T14:15:22Z"    }  ],  "active_merchant_id": "4a3e255e-f328-4684-9ba1-87c683d3cd25"}
GET/v1/account/overview

Console sessions only. For every merchant you are in: its balance (own wallet + service wallet at spot — what you can use, not what is owed to customers), plan, customers and deposit addresses; the prepaid fee balance per billing account (a shared account appears once); and one activity feed — deposits, paid invoices, withdrawals — across all of them, newest first, each row tagged with its merchant.

A merchant whose wallets could not be read comes back with wallets: null and is counted in totals.unavailable, never as $0. The plan's history window applies per merchant.

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

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

curl -X GET "https://example.com/v1/account/overview"
{  "object": "account_overview",  "merchants": [    {      "object": "merchant_overview",      "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",      "display_name": "string",      "logo_url": "string",      "website_url": "string",      "domain": "string",      "network": "testnet",      "status": "active",      "role": "owner",      "active": true,      "billing_account_id": "db2251e5-5308-40a7-9f48-3aed5a9ce736",      "billing_shared": true,      "plan": {        "key": "string",        "name": "string",        "status": "pending"      },      "wallets": {        "own_usd_cents": "string",        "service_usd_cents": "string",        "total_usd_cents": "string",        "unpriced": 0      },      "customers": 0,      "addresses": 0,      "created_at": "2019-08-24T14:15:22Z"    }  ],  "billing_accounts": [    {      "object": "billing_account_summary",      "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",      "shared": true,      "plan": "string",      "plan_name": "string",      "plan_status": "pending",      "balance_cents": "string",      "merchant_ids": [        "006a4151-5819-498d-ace5-e10247e992c5"      ]    }  ],  "totals": {    "balance_usd_cents": "string",    "prepaid_cents": "string",    "unpriced": 0,    "unavailable": 0,    "test_mode": 0,    "merchants": 0  },  "activity": [    {      "object": "activity",      "kind": "deposit",      "id": "string",      "ref": "string",      "merchant": {        "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",        "display_name": "string"      },      "at": "2019-08-24T14:15:22Z",      "status": "string",      "title": "string",      "subtitle": "string",      "chain": "string",      "asset": {        "symbol": "string",        "decimals": 0,        "logo_url": "string"      },      "amount_decimal": "string",      "value_usd_cents": "string",      "fiat": {        "amount_minor": "string",        "currency": "string"      },      "direction": "in"    }  ]}