Creator Notify v1.0.0

Error codes

Every failure uses one envelope. code is stable and safe to branch on; message is for humans and may be reworded; details is either null or a small object such as {"field": "title"}.

{
  "success": false,
  "error": {
    "code": "SITE_EXPIRED",
    "message": "This registration has expired due to inactivity.",
    "details": null
  },
  "request_id": "req_9x2..."
}

The list

CodeHTTPWhenWhat to do
INVALID_REQUEST400Malformed JSON, missing field, failed validation, wrong Content-Type, bad query parameterRead error.details.field
INVALID_API_KEY401No Authorization header, unknown key, bad registration/sweep tokenCheck the key and that it has not been rotated
SITE_NOT_FOUND404Unknown public identifier, or an Origin that does not match the registrationRe-check the identifier and origin
SITE_EXPIRED403No successful publish within the inactivity windowPOST /v1/sites/reactivate
SITE_SUSPENDED403Key revoked; operator intervention requiredContact the operator
SITE_BLOCKED403The website was submitted for registration more than REGISTRATION_ATTEMPT_LIMIT times (4 by default) and is blocked permanentlyOnly an operator can unblock it — POST /v1/internal/sites/unblock. Use a different website address for a new account
INVALID_SUBSCRIPTION400Malformed endpoint, p256dh or authRe-subscribe from the browser
PUSH_ENDPOINT_NOT_ALLOWED400Endpoint host missing from ALLOWED_PUSH_HOSTSAdd the host, or leave the variable unset to use the default list
NOTIFICATION_PAYLOAD_TOO_LARGE413Body over 32 KB or the serialised payload over MAX_PUSH_PAYLOAD_BYTESShorten body, data, image
RATE_LIMIT_EXCEEDED429Window budget exhausted for the scopeHonour retry-after
PUSH_DELIVERY_FAILED502Reserved for a publish request that cannot be handed to any push service at allCheck the delivery attempts on the notification
INTERNAL_SERVER_ERROR500Unexpected exception; details are logged, never returnedReport the request_id
NOT_FOUND404Unknown route or documentation pageSee GET /v1
METHOD_NOT_ALLOWED405Known path, wrong verb; allow header lists the valid onesAdjust the method
NOTIFICATION_NOT_FOUND404Unknown notification id, or one belonging to another siteCheck the id
DUPLICATE_SITE409Origin already registered. details.attempts counts how many times this address has been submittedUse the existing credentials or rotate the key — do not register again
SITE_LIMIT_REACHED409MAX_SUBSCRIPTIONS_PER_SITE reachedRaise the cap or prune subscriptions
REGISTRATION_DISABLED403REGISTRATION_ENABLED=falseEnable it, or register through the operator
SERVICE_UNAVAILABLE503Required secrets or a valid VAPID configuration are missingSee Installation

HTTP status summary

StatusMeaning here
200Success; a publish finished inline
201Site registered, or a new subscription created
202Notification accepted; delivery continues asynchronously
204CORS preflight
400 / 401 / 403 / 404 / 405Client-side problems as listed above
409Conflict with existing state
413Payload too large
429Rate limited
500 / 502 / 503Server-side problems; retry with backoff

SDK-side error codes

The JavaScript SDK raises CreatorNotifyError with a code. API errors keep their API code; the SDK adds MISSING_WEBSITE_ID, UNSUPPORTED, PERMISSION_DENIED, PERMISSION_ERROR, SERVICE_WORKER_ERROR, NETWORK_ERROR, SUBSCRIPTION_CHANGED and UNKNOWN. See the SDK reference.

Handling errors well

const response = await fetch(`${API}/v1/notifications`, { method: 'POST', headers, body });
const body = await response.json().catch(() => null);

if (!response.ok) {
  const code = body?.error?.code ?? `HTTP_${response.status}`;
  switch (code) {
    case 'SITE_EXPIRED':
      alertOnCall('Registration expired — reactivate it');
      break;
    case 'RATE_LIMIT_EXCEEDED':
      await sleep(Number(response.headers.get('retry-after') ?? 30) * 1000);
      return retry();
    case 'INVALID_REQUEST':
      logValidation(body.error.details);
      break;
    default:
      logWithRequestId(response.headers.get('x-request-id'), code);
  }
  throw new Error(code);
}

Always log x-request-id. It is the only reliable way to correlate a user report with a Worker log line, and it never contains secrets.