API Errors
Common Rising Tide Protocol API error codes and troubleshooting guidance.
Every response uses one envelope. On failure, data is null and error carries a machine-readable code.
Response envelope
Success:
{ "data": { }, "error": null }Error:
{
"data": null,
"error": {
"code": "SCREAMING_SNAKE_CODE",
"message": "Human-readable message",
"details": {}
}
}The SDK parses error.code / error.message / error.details into a thrown RisingTideError. See SDK Error Handling.
Authentication and authorization
| Code | Status | Meaning |
|---|---|---|
UNAUTHORIZED | 401 | Missing or invalid API key, or missing user token where one is required |
FORBIDDEN | 403 | Read-only API key attempting a write without user auth, or the key lacks access to the target app |
ENTITLEMENT_REQUIRED | 403 | The authenticated user lacks the entitlement the route requires |
APP_ACCESS_REQUIRED | 403 | The viewer holds no active grant for this app, on an acquisition or participation route. details carries appId and appName; render a locked state rather than a generic error |
Validation (400)
| Code | Meaning |
|---|---|
INVALID_INPUT | Request body failed schema validation |
INVALID_PRODUCT_TYPE | Unknown or inactive product type slug |
INVALID_PRODUCT_TYPE_FOR_DOMAIN | Product type does not match the product domain |
INVARIANT_SUM_NOT_100 | Contribution percentages do not sum to 100% |
INVARIANT_NO_CREATOR | No CREATOR contribution present |
MAX_CONTRIBUTIONS_EXCEEDED | More than 10 contributions specified |
PERCENTAGE_OUT_OF_RANGE | A contribution percentage is outside the valid range |
BLACKLISTED_GROUP | Referenced FundingGroup is blacklisted |
CURRENCY_NOT_ACCEPTED | The submitted currency is outside the app or collection's accepted set - see Payment Currency Policy |
INVALID_ADDRESSING | A WAITLIST_ONLY invite was sent with send; use sendToWaitlist |
INVALID_CONTACT | A waitlist contact that does not parse as an email address or an X handle |
Text that cannot reach the chain
Anything a person types that is written into a Clarity string-ascii field is refused when it carries a character outside ASCII - a curly apostrophe, an em dash, an accented letter, an emoji. The encoder would otherwise truncate each character to its low byte and mint the result, so the refusal is the honest half of a rule whose other half is the app folding look-alikes at the compose surface (foldToAscii in the SDK).
Two shapes carry it. The user-create and product-create schemas return the envelope above with INVALID_INPUT and a message naming the field (Display name contains quotes, parentheses, semicolons, backslashes, or non-ASCII characters). The four Privy signing routes that take free text - make-reservation (holderName), publish-feedback-edition and publish-open-letter-edition (feedbackNote), set-product-info (updates.name, updates.description, updates.thumbnail) - return their own { success: false, error } body with a message that names the field and lists the offending characters, so the app can show the person exactly which ones to change.
Conflict (409)
| Code | Meaning |
|---|---|
INVALID_STATUS_TRANSITION | Requested status change is not allowed from the current state |
PRODUCT_DEPLOYED | A sticker placement change on a product that has already deployed |
TOO_MANY_DEVICES | A person already holds ten push devices in that app |
NOT_PENDING | A waitlist request that was already decided |
TAKEN | A waitlist pick somebody else brought in first; nothing was spent |
Invites
The public preview endpoint never reveals why a code is invalid - it returns a generic { valid: false }. Operations by the invite's creator or holder, and claims, surface specific codes:
| Code | Meaning |
|---|---|
SELF_CLAIM | Cannot claim your own invite (403) |
EXPIRED | Invite TTL passed without a claim (410) |
QUOTA_EXCEEDED | The creator is over their tier's invite quota (403 for tier, 429 for daily cap) |
ROLE_HIERARCHY | The creator tried to attach a role above their own tier |
INVALID_STATUS | Invite is not in a state that allows this transition (409, or 410 at claim) |
Not found (404)
| Code | Meaning |
|---|---|
NOT_FOUND | No entity with this ID or slug (smart products, funding groups, invites, and more share this code; the message names the entity) |
SOURCE_NOT_FOUND | A referenced StyleSet, Sticker, or FundingGroup contribution source does not exist |
STYLE_SET_NOT_FOUND | No style set for this render request |
Server errors (500)
| Code | Meaning |
|---|---|
INTERNAL_ERROR | Unexpected server error |
Service unavailable (503)
| Code | Meaning |
|---|---|
COUNT_UNREACHABLE | A tournament settled by a count could not read its live count; retry |
Client-side codes
The SDK raises two codes that no response carried: NETWORK_ERROR (the request never reached the API) and HTTP_ERROR (a non-2xx response with no structured error). See SDK Error Handling.
Rate limiting
Rate-limited responses return HTTP 429 with code RATE_LIMITED and a Retry-After header. The invite preview and claim endpoints carry stricter per-IP and per-user limits than the rest of the API.