API Keys
App-scoped API keys for authenticating against the Rising Tide Protocol API - what a key carries, what it authorizes, and what rotation does.
API keys identify your app to the protocol API. They are app identifiers, not user secrets - the API treats them as the "who is calling" channel and pairs them with a per-user session token for any operation that touches user state.
What a key carries
A key has exactly two properties, both set by the protocol team when it is issued:
| Property | Meaning |
|---|---|
| read-only or read/write | A read-only key identifies the app but never authorizes a write on its own. A write request under a read-only key must carry a verified user session token, or it is a 401. A read/write key lets the app's server-side code call write routes that do not act as a person |
| app binding (optional) | The appId the key belongs to. A bound key may only generate contracts for its own app, and routes that answer differently per calling app - app-scoped style sets, for instance - resolve the app from it. A key with no binding is protocol-wide |
There is no admin key and no master key. Admin-only endpoints authorize on the person, not the key: the caller's session token must resolve to a user whose role holds the endpoint's entitlement (VIEW_ADMIN_PANEL, MANAGE_ACCOUNTS, ...). A key named Master Key is a reserved name the API refuses to create or rotate, and nothing else.
Key format
Keys are opaque UUIDs - no prefix, no embedded environment marker. The plaintext key is shown once, at creation: the API stores only a peppered hash of it, so it cannot be shown again. Capture it then, or rotate to get a new one.
Using an API key
Send it in the X-API-Key header on every request:
GET /api/smart-products
X-API-Key: 550e8400-e29b-41d4-a716-446655440000The Authorization header is reserved for the user session token (see below). For API-key-only requests with no user session, the API also accepts a UUID-shaped key as Authorization: Bearer <key>, but X-API-Key is the canonical channel and the only one that works once a user session is attached.
Or configure it on the SDK client and it forwards it as X-API-Key on every request:
import { RisingTideSDK } from "@rising-tide/sdk";
const rtClient = new RisingTideSDK({
baseUrl: process.env.NEXT_PUBLIC_RT_API_URL!,
apiKey: process.env.NEXT_PUBLIC_API_KEY,
});A valid key is cached server-side for ten minutes, so a newly issued key works at once. Rotation and deletion both clear that cache entry, so an old value stops answering the moment the row changes.
Two-layer auth
API key alone is enough to read public smart-product data. User-acting writes (mint, transfer, manage invites) require the user's session token too. The SDK threads the session token via setAuthToken() or via ProtocolUserProvider. See Authentication for the request shape.
Key rotation
Rotation is an operator action in rt-admin. It mints a fresh UUID, replaces the stored hash in one update, and records the old and new eight-character prefixes in an audit row. The old key stops authenticating at once - there is no grace window in which both keys work. Plan a rotation as a deploy: have the new value ready to put into your environment, then rotate, then redeploy. Contact the protocol team to schedule one.
App scoping
A key bound to an app id may act only for that app where a route names one. POST /api/contracts/generate checks the key against the body's sourceAppId and answers 403 on a mismatch; the app-scoped style catalog lists a style set published for one app to that app's key and to no other. A key with no binding is protocol-wide and passes both. The same binding decides which app a person is acting in, so it is what the app access check reads (see Authentication), and a push subscription may only name the key's own app. The binding does not otherwise restrict which routes a key may reach, and it does not scope contract templates - every app generates from the same template catalog.
Environment variables
NEXT_PUBLIC_API_KEY=550e8400-e29b-41d4-a716-446655440000
NEXT_PUBLIC_APP_ID=8Read these by literal name (process.env.NEXT_PUBLIC_API_KEY), never through a dynamic key. Next.js only inlines literal-access reads into the client bundle - a dynamic-key read resolves to undefined on the client, and the failure looks like a missing variable rather than a lookup bug.
Never commit API keys to version control.