# Authentication (/concepts/authentication)



<!-- generated from docs/wallet-api/QUICKSTART.md §1 — edit THAT file -->

```
Authorization: Bearer mp6abeada4ef98a41267d194582a620b19a42f8e2c27a6aef892830a8778d99a02
```

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

| Part       | Meaning                                                                                                    |
| ---------- | ---------------------------------------------------------------------------------------------------------- |
| `mp`       | a **live** key                                                                                             |
| `mptest`   | a **test** key — no chain behind it (the chain is simulated, §10) and every event carries `livemode:false` |
| the 64 hex | the 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. &#x2A;*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.

| Kind       | Reaches                                                                                                       |
| ---------- | ------------------------------------------------------------------------------------------------------------- |
| `checkout` | invoices and the hosted checkout                                                                              |
| `payout`   | **money out** — payouts and their approvals, plus balances (it reads what it can pay from) and payout history |
| `wallet`   | customers, 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.
