Restricted keys and scopes
Updated 27 August 20263 min read
On this page
A restricted key carries only the permissions you grant it. Use one wherever a service needs a slice of the API: a reporting job that only reads balances, a checkout service that only creates payouts, a webhook manager that only edits endpoints. If the key leaks, the damage is limited to what its scopes allow.
Scopes
Scopes are granted per key when you create it. They are additive: a key can do anything covered by at least one of its scopes and nothing else.
| Scope | Allows |
|---|---|
ledgers:read |
Read ledgers, accounts, transactions, entries and balances. |
ledgers:write |
Create and update ledgers and accounts; create, post, void and reverse transactions. |
transfers:read |
Read transfers. |
transfers:write |
Create transfers between accounts and FX conversions. |
payouts:read |
Read payouts, counterparties and bank accounts. |
payouts:write |
Create counterparties and payouts; cancel payouts; verify bank accounts. |
webhooks:manage |
Create, update, delete and roll the secret of webhook endpoints; redeliver events. |
reports:read |
Read statements and reconciliation results; create exports. |
A write scope does not include the matching read scope. A key that creates payouts and also needs to look them up should have both payouts:write and payouts:read.
What a restricted key cannot do
Some operations are reserved for secret keys and the dashboard regardless of scopes:
- Creating, rolling or revoking API keys.
- Changing plan, billing details or team members.
- Submitting or editing business verification.
- Changing the organization's data region.
These calls fail with HTTP 403 and the code permission_denied when made with a restricted key.
Creating a restricted key
In the dashboard, choose Create key, select Restricted and tick the scopes. Through the API, a secret key can create one:
curl https://api.ledgerline.dev/v1/api_keys \
-H "Authorization: Bearer ll_live_sk_replace_me" \
-H "Idempotency-Key: 4b9d3a4f-2a8c-4f7e-a3a1-9a0d6c0b7e21" \
-d type=restricted \
-d name="nightly-reporting" \
-d "scopes[]=ledgers:read" \
-d "scopes[]=reports:read" \
-d expires_at=2027-03-31T00:00:00Z
The response contains the new key once, with the prefix ll_live_rk_.
What happens without the scope
A request that needs a scope the key does not have is rejected before it runs:
{
"error": {
"type": "permission_error",
"code": "scope_missing",
"message": "This API key lacks the required scope: payouts:write.",
"param": "payouts:write",
"request_id": "req_01JA4B2XNE"
}
}
The HTTP status is 403. The param field names the missing scope, so the fix is to create a new key with that scope. Scopes cannot be edited on an existing key. Create a replacement, deploy it and revoke the old one.
IP allowlists
On Growth and Scale plans you can pin a key to a set of IPv4 or IPv6 ranges in CIDR notation. Requests from any other address fail with 403 and the code ip_not_allowed. Combine an allowlist with a restricted key for the strongest setup. More in Security overview.
Expiry
Every key can have an expires_at timestamp. After that moment the key behaves as revoked and returns api_key_expired. The dashboard lists keys by expiry date so you can renew them before they stop working.
Choosing scopes: examples
| Service | Suggested scopes |
|---|---|
| Finance dashboard that shows balances | ledgers:read |
| Payroll service that pays contractors | payouts:write, payouts:read, ledgers:read |
| Reconciliation worker | ledgers:read, reports:read |
| Deployment tooling that registers webhooks | webhooks:manage |
See API keys for how keys are issued, rolled and revoked.