ledger:read, withdrawal:create, treasury:spend, …); a 403 means the
key is valid but lacks the scope the operation requires, not that
something is broken.
Bearer keys
The simple form, and the right default for anything read-only:kp_test_… keys run against testnet; kp_live_… against mainnet. They
are otherwise identical — same endpoints, same shapes — so integrate
against test and swap the key when you’re ready. See
Networks for how the two stay isolated end to end.
Signed requests
For withdrawals, treasury spends, and anything else that moves funds, sign the request instead of sending the secret. The signature covers:
No secret travels on the wire, and a captured request cannot be replayed
(the nonce) or altered (the signature covers the body’s hash) or reused
past its window (the timestamp).
Reference implementation
Reference implementation
A minimal, zero-dependency signer — copy it straight into your backend
rather than depending on a KeyPair SDK for something this small: