Scopes & Permissions
Scopes & Permissions
Scopes are string tags attached to an API key that gate server-signing (mode=signed) on sensitive endpoints. Keys default to read-only scopes; request additional scopes from the team if you need them.
unsigned mode never requires a scope — the client provides authority by signing the returned transaction with its own wallet.
Verified scopes
| Scope | Unlocks |
|---|---|
* | Wildcard — every scope |
fees:claim | mode=signed on POST /v2/pools/{address}/fee-claims (every kind) |
airdrop:admin | POST /v2/airdrops and mode=signed on POST /v2/airdrops/{id}/claims |
airdrop:write | POST /v2/merkle-trees |
A request that targets a scoped endpoint without the right scope on its key returns 403 forbidden:
{
"error": {
"code": "forbidden",
"message": "API key missing required scope(s): fees:claim"
},
"meta": { "request_id": "req_…" }
}User-signed operations reject mode=signed entirely
mode=signed entirelyA separate class of endpoints rejects mode=signed regardless of scopes — only the user's wallet has authority to sign these, so the platform wallet can never be the right signer:
| Endpoint | Why |
|---|---|
POST /v2/airdrops/{id}/claims | Claim must be authorized by the recipient wallet |
POST /v2/stakes | Stake must be authorized by the NFT owner |
DELETE /v2/stakes/{asset} | Unstake must be authorized by the NFT owner |
POST /v2/nfts | Mint payer is the user |
DELETE /v2/nfts/{asset} | Burn must be authorized by the asset owner |
POST /v2/gold-locks | Lock must be authorized by the GOLD owner |
DELETE /v2/gold-locks/{user} | Unlock must be authorized by the lock owner |
POST /v2/tokens | Mint authority is the user wallet |
Calling any of these with mode: "signed" returns 400 bad_request.
How scope checks work
Mutating routes call requireScope("<scope>") middleware before the handler runs. The middleware:
- Reads the API key's
scopes text[]column from Supabase. - Returns
403 forbiddenif neither the wildcard*nor the required scope is present. - Otherwise hands off to the route.
If you've been issued a wildcard key, you skip every per-scope check. Wildcard keys should be used only for trusted internal services.
Designing around scopes
For most integrations the right answer is:
- Use
unsignedmode for anything the user is authorizing themselves. No scope required, the user's wallet provides authority. - Request
fees:claimonly if your service routinely claims fees on behalf of pools you administer. - Request
airdrop:admin/airdrop:writeonly for distribution services that initialize airdrops or build trees server-side.
See Transaction Modes for the full per-endpoint matrix of which modes are accepted and which scopes apply.