insufficient_scope
The bearer token authenticated successfully, but the key it belongs to does not carry the scope this route requires.
HTTP 403
When this happens
Every route in lib/public-api/routes.ts's ROUTES table declares exactly one required scope. withPublicApi checks the authenticated key's scopes against it after authentication succeeds and after the rate-limit check. So the key is real and not over budget; it was simply never issued the permission this route needs.
This is distinct from unauthorized, where the token itself is invalid. It is also distinct from not_found, which never reveals whether a resource exists. insufficient_scope fires only once the key is known to be real.
The error envelope
{
"error": {
"code": "insufficient_scope",
"message": "This key does not have the required scope for this route: payment_intents:create.",
"detail": { "requiredScope": "payment_intents:create" },
"request_id": "req_1a2b3c4d5e6f7a8b9c0d1e2f",
"docs": "https://canopypay.io/errors/insufficient_scope"
}
}What to do about it
The refusal names the scope it wanted, in both the message and detail.requiredScope. Use a key that carries it.
Every route on /api/v1 requires payment_intents:create: a key that can create intents can also list them, read them, read their origins and issue their source inbox. Every key Canopy issues carries that scope, so in practice this error is one you should not be able to reach with a current key. If you do see it, the key predates the scope, or its scope list was set explicitly to something narrower -- either way the fix is below, and it is a different key, never a retry.
A key issued before a scope existed does not gain it by rotating — rotation copies a key's existing scopes deliberately, so that rotating a credential can never widen what it can do. Issue a new key instead; newly issued keys carry the current default scopes.
If the error persists, confirm you are using the correct account's key rather than one issued under a different account. Retrying with the same key will fail identically; the fix is always a different credential.