MolnPayDocs
Concepts

Authentication

Keys, kinds, scopes, rolling, the IP allowlist, and the three-level signing gate.

Authorization: Bearer mp6abeada4ef98a41267d194582a620b19a42f8e2c27a6aef892830a8778d99a02

A key is a prefix plus 64 lowercase hex characters (32 random bytes, 256 bits) and nothing else:

PartMeaning
mpa live key
mptesta test key — no chain behind it (the chain is simulated, §10) and every event carries livemode:false
the 64 hexthe secret itself

There are no separators and no embedded fields, so the whole token matches [0-9a-z]+ — nothing in it needs quoting, escaping or encoding, wherever you put it. Put it in the Authorization header and nowhere else. In particular never place it in a query string: URLs end up in access logs, proxy logs, browser history and Referer headers, and a key that lands in any of those is a leaked key regardless of how clean its characters are.

The key's kind is not in the string. Every key is created as one of checkout, payout or wallet, and that is a property of the key, not of the token — authorization is never read off a string you supply. GET /v1/ping reports it.

KindReaches
checkoutinvoices and the hosted checkout
payoutmoney out — payouts and their approvals, plus balances (it reads what it can pay from) and payout history
walletcustomers, deposit addresses, deposits, balances, sweeps, and read-only payout history

GET /v1/ping, GET /v1/chains and GET /v1/balances (the project treasury) accept any kind; a customer's or a wallet's balances need a wallet or payout key.

Calling a route with the wrong kind is 403 wrong_key_kind — deliberately not 401: you are authenticated, and "use your other key" is something you can act on. The point of the split is that the key your storefront carries cannot send funds anywhere, whatever scopes it was granted. The kind is checked before scopes, so a checkout key that was somehow granted payouts:create still gets wrong_key_kind on POST /v1/payouts, never missing_scope; the fix is a different key, not a different grant.

The full key is shown exactly once, when it is created. There is no endpoint that can return it afterwards — not with elevated privileges, not for the account owner. If it is lost, roll it. What we keep is the peppered hash, plus a public display handle (mp6abeada4 — the prefix and the first 8 hex) so you can tell your keys apart in a console or a log without ever writing the key down.

Rolling. A roll issues a new key and sets the old one to expire (24h by default). Both work during the overlap, so you can deploy the new key before the old one dies. Revoking is immediate.

Scopes. Each key carries a subset of: customers:read customers:write wallets:read wallets:create balances:read deposits:read sweeps:read sweeps:create payouts:read payouts:create webhooks:read webhooks:write webhooks:replay. Wildcards * and wallets:* are supported.

IP allowlist. A key may be pinned to CIDRs. Empty means any source.

Signing. Moving funds (payouts:create, sweeps:create) additionally requires signing to be enabled at three levels: globally, for your project, and for that specific key. This means you can issue a deliberately read-only key for reporting systems. 403 signing_disabled means one of the three is off — and GET /v1/ping tells you which, in signing_blocked_by (service / project / key), so you can ask the operator for the right thing instead of guessing.

Verify a key with GET /v1/ping — it echoes your kind, scopes, network, and whether signing is on.