Error Handling
All errors are returned as JSON with a consistent structure. Use the HTTP status code for routing
logic and the message field for human-readable details.
Error Response Shape
{
"error": "Unauthorized",
"message": "Invalid or missing token"
}
Some 400-series errors include a details field with per-field validation messages.
{
"error": "Validation failed",
"message": "Request body is invalid",
"details": {
"text": "text is required",
"secret": "secret must be a non-empty string"
}
}
HTTP Status Codes
| Code | Name | When it occurs |
|---|---|---|
| 200 | OK | Request succeeded. Response body contains the result. |
| 201 | Created | Resource created (e.g. new API token, new signing channel). |
| 400 | Bad Request | Missing or invalid fields in the request body. Check the details field for per-field messages. |
| 401 | Unauthorized | Missing, expired, or invalid credential. Re-authenticate and retry.
|
| 402 | Payment Required | Credit balance is zero or insufficient for the requested operation. Top up credits or upgrade your plan. |
| 403 | Forbidden | Authenticated but not allowed. Causes:
|
| 404 | Not Found | The requested resource (channel, token, etc.) does not exist or does not belong to your account. |
| 409 | Conflict | A resource with the same unique identifier already exists (e.g. email already registered, channel name already taken). |
| 422 | Unprocessable Entity | Semantically invalid input. Common causes:
|
| 429 | Too Many Requests | Rate limit exceeded on an auth endpoint (login, register). Check the Retry-After response header for the wait time in seconds. |
| 500 | Internal Server Error | Unexpected server-side failure. These are logged automatically. If the issue persists, contact support. |
Handling 401 — Token Refresh
JWT access tokens expire after 2 hours. When you receive a 401, call the refresh endpoint to get a new one. The refresh token is stored as a httpOnly cookie and sent automatically by the browser (or --cookie-jar in curl).
POST /api/auth/refresh
Cookie: refreshToken=<cookie-sent-automatically>
# Response
{
"accessToken": "eyJ..."
}
If the refresh token is also expired (30-day TTL), re-authenticate with POST /api/auth/login.
API tokens do not expire — a 401 on a watermark endpoint means the token has been revoked.
Handling 402 — Insufficient Credits
Every watermark encode or decode consumes credits. A 402 means your balance reached zero before the operation could complete. The operation is not charged — no credits are deducted for failed requests.
{
"error": "Payment Required",
"message": "Insufficient credits. Current balance: 0"
}
Check your balance at GET /api/v2/credits/balance. Top up via your dashboard or upgrade your subscription plan.
Handling 403 — Forbidden
A 403 with "error": "AccountSuspended" means an open payment dispute has frozen your account. All API operations are blocked until the dispute is resolved. Contact [email protected] to resolve.
{
"error": "AccountSuspended",
"message": "Account is suspended due to an open dispute"
}
Handling 422 — Encode/Decode Failure
The watermark engine returns a 422 when encoding is not possible (text too short) or decoding fails (no watermark found or data corrupted).
| Message | Meaning |
|---|---|
| Text too short to embed watermark | The input text does not have enough characters to carry the secret. Use a longer text or a shorter secret. |
| No watermark found in text | Decode found no zero-width characters. The text was either never watermarked or the watermark was stripped. |
| Watermark data corrupted | Zero-width characters are present but the encoded data fails integrity checks. Text may have been partially edited. |
Client-Side Best Practices
- Always check the HTTP status code first; do not parse the body unless the status indicates success or a known error shape.
- On 401: attempt one silent refresh, then redirect to login. Avoid refresh loops.
- On 429: read the
Retry-Afterheader and wait that many seconds before retrying. Do not hammer the endpoint. - On 500: retry once with exponential backoff. If errors persist, check the status page or contact support.
- Do not retry 400, 402, 403, or 422 without first fixing the underlying issue — these are deterministic failures.