Rising Tide Developers
API

API

The Rising Tide REST API - base URLs, authentication, the response envelope, pagination, and the full endpoint reference.

The SDK is a typed client over this API, and everything it does is an HTTP call you can make yourself. If you are writing TypeScript, use the SDK - it carries the types, the BigInt handling and the transaction builders. This section is for everything else: another language, a server-to-server integration, or a case where you want to see exactly what is on the wire.

Base URLs

EnvironmentBase URL
Testnethttps://testnet-api.risingtide.app
Mainnethttps://api.risingtide.app (planned)

Authentication

Two headers, two independent questions. The full model is in Authentication.

X-API-Key: 3f8a1c02-9d47-4b6e-8f11-2c5a7e90d3b4
Authorization: Bearer <user session token>
  • X-API-Key identifies your app. Send it on every request. Keys are opaque UUIDs with no prefix and no environment marker.
  • Authorization: Bearer identifies the person acting, and is required only on routes that act as a user. That token is a Privy access token or a signed Stacks message.

The Auth column on every table in this section tells you which applies, per method:

Column readsWhat the request needs
publicNothing. No key and no session
app keyThe API key alone. Public read
app key, user optionalThe key. A session token, when sent, changes the answer
app key + app accessThe key. For an app behind an access gate, also a signed-in user holding an active grant
app key + userA valid user session too
app key + user, entitlementThe user must also hold the entitlement for that action
app key + user, app accessThe user must hold an active access grant for your app
app key + user, entitlement, app accessBoth: the entitlement and an active access grant for your app
user, user, entitlementA valid user session (and the entitlement) and nothing else. These routes verify the session token and do not check the key

A missing or invalid key is 401 on every route that checks one. A valid identity without the right entitlement is 403 with code ENTITLEMENT_REQUIRED, and one without an access grant is 403 with code APP_ACCESS_REQUIRED - different failures, worth telling apart in your UI. Both carry the missing piece in error.details.

A read-only key identifies the app but never authorizes a write on its own: a write under one must also carry a verified session token, or it is a 401.

Response envelope

Every response, signing routes aside, is the same shape. data and error are both always present, and exactly one is non-null.

{ "data": { "id": 42, "title": "morning rain" }, "error": null }
{ "data": null, "error": { "code": "NOT_FOUND", "message": "SmartProduct 42 not found" } }

error.details is present on validation failures. Codes and their meanings are in API Errors.

One family differs. The server-signing routes under /api/privy/ answer { "success": true, "txId": "0x..." } on success and { "success": false, "error": "..." } on failure, and a failed session check on them is a 401 with a bare { "error": "..." }. See Contracts and Signing. The SDK's command surface normalizes all of it.

Pagination

Paged list endpoints take page and limit, and nest their results under data:

{
  "data": {
    "items": [],
    "pagination": { "page": 1, "limit": 20, "total": 137, "totalPages": 7 }
  },
  "error": null
}

A few lists are shaped for what they hold instead: the notification feed pages with an opaque cursor and returns nextCursor, and the small catalogs return the whole array.

Three conventions that will bite you

BigInts are strings. Anything the chain owns as a 128-bit integer - instanceId, instanceNumber, feePaid - is serialized as a decimal string, not a JSON number. Parsing one into a float will silently lose precision. Dates are ISO 8601 strings.

Unknown query parameters are rejected, not ignored. A listing route declares the params it reads and 400s on anything else. A misspelled filter fails loudly instead of quietly returning every row in the table, which is the failure that actually costs you.

include is an allowlist, and it is narrower on lists than on detail. A listing route will not join per-row relations that would bloat the payload, so ?include=instances works on one product and not on a page of them.

Rate limits

Every route sits behind a per-IP ceiling, and the surfaces that cost something carry tighter limits of their own: signing, wallet creation, the faucets, contract generation, and the invite and waitlist routes that a stranger can reach. The numbers are operator dials, so read the response rather than hardcoding them. A limited request is 429 with code RATE_LIMITED and the back-off in headers:

Retry-After: 42
RateLimit-Limit: 10
RateLimit-Remaining: 0
RateLimit-Reset: 42

All four are exposed to cross-origin browser clients. The SDK surfaces a 429 as a typed RateLimitError carrying retryAfterSeconds and does not wait and retry for you, because the window can run to a minute: honoring the back-off is your call.

Health and maintenance

GET /api/health needs no key and is the one liveness check built for callers: it answers { timestamp, status, commit, services } with status of healthy, degraded or unhealthy, and is a 200 only when healthy, 503 otherwise. commit is the git commit the deployment was built from, or null where the platform does not report one. The SDK exposes it as health(). The detailed check is operator-only.

When an operator puts the API into maintenance, every other route answers 503 with code MAINTENANCE until it is lifted. Treat that as a scheduled pause, not a fault: render your own maintenance state and retry later.

Versioning

The API is unversioned today. Breaking changes are announced in the Changelog and coordinated with SDK releases.

Reference

Every endpoint on the integration surface, grouped by what it acts on:

Operator routes are not documented here. /api/admin/* authorizes on the signed-in person: the session token must resolve to a user whose role holds the route's operator entitlement, which no app-scoped key can supply. Scheduled workers authorize on a server-side secret. Neither is part of the integration surface.