Access
What a business needs before it can call this API, and the one thing you still have to ask us for.
Three separate things decide whether a call succeeds: whether the business can hold a key, whether the key authenticates from where you are calling, and - for money only - whether the business is open for spending. They are separate on purpose, so no single mistake and no single stolen credential is enough on its own.
Keys are self-serve
Generate a keypair, upload the public half under your business's API keys, and you have a key id. There is no application, no enablement request and nothing to wait for, in either sandbox or live.
This changed on 2026-09-18. Both surfaces used to be granted per business by us, which made "can I try it" a conversation rather than a signup. If you read older notes saying you need us to switch API keys or sandbox on for you, they are out of date.
We never hold your private key
Which means we cannot reissue it. Rotation is register-a-second-key, then revoke the first. See Signing.
The IP allowlist
A key that can send money authenticates only from the addresses listed against it. It must
have at least one, and there is no wildcard. A key with only read and receive, such as
a store's key, may have an empty list, which means any address.
ip_not_allowed means the signature was fine and the address was not listed. Egress
addresses are easy to get wrong when your code runs behind a NAT or on a platform that
rotates outbound IPs, so list every address your traffic can leave from.
What a key may do
Three capabilities, chosen when you register the key and changeable afterwards from the same screen - you do not re-register a key to widen or narrow one.
| Capability | Covers |
|---|---|
read | Every GET. On every key, and not removable. |
receive | POST /addresses, POST /lightning/invoices, POST /checkout/orders and POST /checkout/orders/{id}/cancel. |
money | Everything that sends money: payouts, transfers and Lightning payments. |
Only read is implied. A key registered without asking for anything else can read your
balances, transactions and beneficiaries and nothing else - including no minting.
Creating an address or an invoice is a write at the provider rather than a read of our
own records, so it is granted rather than assumed.
Nothing is granted by leaving it out, either: a registration that does not mention capabilities produces a read-only key, never a broader one.
A call refused for want of a capability answers 403 with details.code of
capability_required, naming the one it wanted.
Moving money still needs enabling
The money capability is only half of what the endpoints that
send money require: they need two independent grants.
- The key must carry the
moneycapability. A key without it cannot spend, whoever holds it. - The business must be opened for money over the API, which is ours to switch on - talk to us.
Either one alone does nothing. A leaked key cannot pay anybody unless the business was also opened, and an opened business arms no key that was not separately granted.
Until both are in place, those endpoints answer 403 with details.code of
capability_required (the key) or api_money_not_enabled (the business). Two codes
rather than one because the fixes are different: the first is a permission you change
on the key you already hold, the second is a conversation.
Once both are in place, those endpoints demand one more thing that the rest of the API
does not: an Idempotency-Key header on every call. See Sandbox for
where to rehearse it, and Sending money for the exact
semantics - the short version is that a retry with the same key replays the first order
instead of paying a second time.
Why this one stayed gated
Holding a credential and spending from a balance are different risks, and only the second is irreversible. Opening keys to everyone was worth doing precisely because a key on its own cannot move value - that property is what makes it safe, so it is not one we hand away in the same motion.
Either surface can be closed again
A business can be restricted, and both surfaces can be closed platform-wide, without touching your key. The check runs on every request rather than at registration, so access that is withdrawn stops working on the next call - and access that comes back needs no new key.
Two codes, because the fixes differ:
details.code | What happened |
|---|---|
api_access_revoked | This business is no longer open for API access at all. |
sandbox_unavailable | The key is a sandbox key and sandbox is closed for this business. A live key on the same business is unaffected. |
Treat them the way you would treat any other refusal on this API: switch on
details.code, and default an unknown code to failure. See Errors.