Authentication

Authenticate requests with a project-scoped API key, limited by its grants, wallet scope, and IP allowlist.

Send your API key in the Authorization header. The example reads it from SDP_API_KEY:

Shell
curl 'https://api-beta.solana.com/v1/wallets' \
  -H "Authorization: Bearer $SDP_API_KEY"

The Bearer prefix is optional; the header also accepts the bare key.

Each key carries three independent controls: grants, a wallet scope, and an IP allowlist. Together they give the key exactly the access its integration needs.

Key scope

A key belongs to one project and inherits that project's environment: a key from a sandbox project reaches devnet, one from a production project mainnet-beta. The key itself doesn't name its environment; the dashboard shows it next to each key. A key reaches only its own project's resources. There is no cross-project or organization-wide key.

Authorization layers

A request must clear every layer that is configured on its key:

LayerWhat it controls
GrantsThe actions (module.verb) the key may perform. Each route needs exactly one; see Grants by module
Wallet scopeWhich custody wallets SDP may sign with for the key: all in its project, or only the selected wallets in walletIds; see Wallet access
IP allowlistOptional IPv4 or IPv6 addresses and CIDR ranges the key may be used from. An organization-wide list under Settings > API key access applies to every key on top. Neither restricts signed-in dashboard members

Errors

Authentication and authorization failures are always machine-distinguishable: the code says what kind of refusal it is, and a 403 names what is missing in error.details.

ConditionStatusCodeerror.details
No API key on a keyed-only request401UNAUTHORIZED
Malformed or unknown key401INVALID_API_KEY
Key has been revoked, its organization was deleted, or its project was archived401REVOKED_API_KEY
Key is live but its organization is suspended; the key works again once SDP resumes it403FORBIDDENreason: "organization_suspended"
Key past its expiry or rotation deadline401EXPIRED_API_KEY
Request origin not in the key's or the organization's IP allowlist403FORBIDDENreason: "ip_not_allowed"
Route only a signed-in dashboard member may use, such as managing keys, members or the organization403FORBIDDENreason: "human_session_required"
Key lacks the route's action, or the action a signature it asks for needs403INSUFFICIENT_PERMISSIONSaction names the missing action
SDP refused to sign for the request (see below)403FORBIDDENreason names why
Per-key rate limit exceeded429RATE_LIMITED

Signing refusals

SDP decides again each time it signs on a key's behalf, so a change to the key or the organization applies to the next signature, including one later in the same request. Nothing is signed or sent on refusal. error.details.reason is one of:

reasonWhy
wallet_out_of_scopeThe custody wallet is outside the key's wallet scope
wallet_purposeThe custody wallet's purpose may not send payments: only a transfer wallet can be the source of a transfer, transfer batch, or recurring payment
principal_not_liveThe key was revoked, expired, or passed its rotation deadline during the request
organization_inactiveThe organization is suspended or deleted
production_not_enabledThe project is a production project and the organization does not have production access
project_not_foundThe project no longer exists
principal_missingNo credential is attached to the movement
movement_disabledSDP has disabled this kind of money movement for the project

A key without the action a signature needs is refused with INSUFFICIENT_PERMISSIONS and error.details.action, exactly as at the route.

Each key is rate limited by its tier. Rate limits lists the limits.

See Manage API Keys for creating, rotating, and revoking keys.