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

ScopeUnlocks
*Wildcard — every scope
fees:claimmode=signed on POST /v2/pools/{address}/fee-claims (every kind)
airdrop:adminPOST /v2/airdrops and mode=signed on POST /v2/airdrops/{id}/claims
airdrop:writePOST /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

A 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:

EndpointWhy
POST /v2/airdrops/{id}/claimsClaim must be authorized by the recipient wallet
POST /v2/stakesStake must be authorized by the NFT owner
DELETE /v2/stakes/{asset}Unstake must be authorized by the NFT owner
POST /v2/nftsMint payer is the user
DELETE /v2/nfts/{asset}Burn must be authorized by the asset owner
POST /v2/gold-locksLock must be authorized by the GOLD owner
DELETE /v2/gold-locks/{user}Unlock must be authorized by the lock owner
POST /v2/tokensMint 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:

  1. Reads the API key's scopes text[] column from Supabase.
  2. Returns 403 forbidden if neither the wildcard * nor the required scope is present.
  3. 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 unsigned mode for anything the user is authorizing themselves. No scope required, the user's wallet provides authority.
  • Request fees:claim only if your service routinely claims fees on behalf of pools you administer.
  • Request airdrop:admin / airdrop:write only 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.