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.
  • JWT expired → call /api/auth/refresh
  • API token revoked → create a new token
  • No Authorization header present
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:
  • Account suspended due to an open dispute
  • Email not verified
  • Token lacks the required permission scope
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:
  • Input text is too short to embed a watermark
  • Watermark could not be decoded from the provided text
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-After header 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.