API reference
Errors
The one error shape every Kanorio API returns, how codes map to HTTP statuses, and how to handle retries.
The Management API, the Content Delivery API and the Customer API return errors in the same shape. A code always has the same HTTP status on every API, so one error handler works for all of them.
{ "error": { "code": "SLUG_TAKEN", "message": "Another post already uses this slug.", "details": { "slug": "how-we-feed-our-starter" } } }
| Field | Type | Notes |
|---|---|---|
error.code | string | Stable, machine-readable. Branch on this. |
error.message | string | One English sentence for logs and developers. It can change at any time, so never match on it. |
error.details | object, optional | Structured context for the code, such as retryAfter, availableAt, issues or requiredScope. |
Successful responses never contain error, and no API wraps success in an ok flag.
HTTP status by code
Every code maps to one status. Codes added later follow the same rules, so you can handle a code you have not seen yet by its status.
| Status | Meaning | Examples |
|---|---|---|
400 | The request is malformed or fails validation | VALIDATION, INVALID_CURSOR, codes starting with INVALID_ |
401 | No credential, or the credential is invalid, expired or revoked | UNAUTHORIZED, UNAUTHENTICATED |
403 | The credential is valid but not allowed: permission, plan, role or mode | FORBIDDEN, OWNER_REQUIRED, LIMIT_REACHED, LOCKED |
404 | The resource does not exist on this site | NOT_FOUND, codes ending in _NOT_FOUND |
405 | The method is not supported on this path | METHOD_NOT_ALLOWED |
409 | Conflicts with the current state: stale writes, taken slugs, duplicates | CONFLICT, SLUG_TAKEN, IDEMPOTENCY_CONFLICT |
413 | The upload is too large | FILE_TOO_LARGE |
422 | Valid input, but a business rule does not allow it | SITE_NOT_PUBLISHED, ASSET_IN_USE |
429 | Too many requests, or a busy backend; retry later | RATE_LIMITED, SEARCH_BUSY, ANALYTICS_BUSY |
500 | Unexpected server error | INTERNAL |
502, 503 | An upstream service failed or is temporarily unavailable | UPLOAD_FAILED, SERVICE_UNAVAILABLE, SHARED_SENDER_PAUSED |
Each operation in the API reference lists the codes it can return and what they mean for that operation. The OpenAPI documents carry the same list as x-kanorio-errors.
Validation errors
VALIDATION lists every failing field in details.issues. path uses dots for nesting.
{ "error": { "code": "VALIDATION", "message": "Input does not match the capability schema.", "details": { "issues": [ { "path": "title", "message": "Required", "code": "invalid_type" } ] } } }
Form submissions on the Content Delivery API report invalid answers the same way, with the question id as the path.
Retries
- Retry
429,502and503with backoff. When the response has aRetry-Afterheader ordetails.retryAfter, wait at least that many seconds. - Do not retry other
4xxresponses unchanged. Fix the request first. 409 CONFLICTon a write means someone changed the resource after you read it. Read it again, merge, and send the write with the new timestamp.- Make create requests safe to retry with an idempotency key.
Permission errors
403 responses explain what is missing in details, for example the requiredScope the token lacks or reason: "principal_not_allowed" when a service account calls an account-level operation. Show the user what to change instead of retrying.
Request IDs
Content Delivery API and Customer API responses carry an X-Request-Id header. Include it when you contact support about a specific request.
Error codes
The tables below list every code each API can return today, generated from the OpenAPI documents.
Management API codes
| Code | HTTP status |
|---|---|
EMPTY_SLUG | 400 |
INVALID_BODY | 400 |
INVALID_CURSOR | 400 |
INVALID_EDITED_PATH | 400 |
INVALID_FORMAT | 400 |
INVALID_ORDER | 400 |
INVALID_SUBDOMAIN | 400 |
UNSUPPORTED_EDITED_PATH | 400 |
VALIDATION | 400 |
UNAUTHENTICATED | 401 |
UNAUTHORIZED | 401 |
CUSTOM_DOMAIN_CHANGE_FORBIDDEN | 403 |
CUSTOM_DOMAIN_LIMIT_REACHED | 403 |
FORBIDDEN | 403 |
LIMIT_REACHED | 403 |
MODERATION_LOCKED | 403 |
OWNER_REQUIRED | 403 |
PLAN_CANNOT_PUBLISH | 403 |
PRO_REQUIRED | 403 |
REDIRECT_LIMIT | 403 |
SUBDOMAIN_CHANGE_FORBIDDEN | 403 |
TOKEN_WEBSITE_SCOPED | 403 |
HEADER_NOT_FOUND | 404 |
HOME_PAGE_NOT_FOUND | 404 |
NOT_FOUND | 404 |
OWNER_NOT_FOUND | 404 |
PAGE_NOT_FOUND | 404 |
PARENT_NOT_FOUND | 404 |
PRODUCT_NOT_FOUND | 404 |
WEBSITE_NOT_FOUND | 404 |
METHOD_NOT_ALLOWED | 405 |
CONFLICT | 409 |
CUSTOM_DOMAIN_TAKEN | 409 |
HAS_CHILDREN | 409 |
HOSTED_ONLY | 409 |
HOSTNAME_CONFLICT | 409 |
IDEMPOTENCY_CONFLICT | 409 |
IDEMPOTENCY_IN_PROGRESS | 409 |
KEY_TAKEN | 409 |
PRIMARY_HEADER_PROTECTED | 409 |
READD_COOLDOWN | 409 |
REDIRECT_CONFLICT | 409 |
SLUG_TAKEN | 409 |
SUBDOMAIN_TAKEN | 409 |
TAKEN | 409 |
BLOCKED | 422 |
BREADCRUMB_PARENT_INVALID | 422 |
CONTENT_VALIDATION_FAILED | 422 |
CORE_SECTION_VIOLATION | 422 |
CUSTOM_DOMAIN_BLOCKED | 422 |
CUSTOM_DOMAIN_HIGH_RISK_TLD | 422 |
CUSTOM_DOMAIN_TOO_LONG | 422 |
DEPTH_EXCEEDED | 422 |
DNS_NOT_PROPAGATED | 422 |
DOMAIN_CHANGE_NOT_ALLOWED | 422 |
EMPTY_CONTENT | 422 |
FOREIGN_PAGE | 422 |
HIGH_RISK_TLD | 422 |
HOME_HAS_HIERARCHY | 422 |
HOME_NOT_CREATABLE | 422 |
HOME_NOT_DELETABLE | 422 |
HOME_NOT_SORTABLE | 422 |
HOME_SLUG_IMMUTABLE | 422 |
LINK_IN_BIO_DEDICATED_ENTRY | 422 |
LINK_IN_BIO_NOT_DUPLICABLE | 422 |
META_NOT_LIVE | 422 |
NO_PUBLIC_URL | 422 |
OWNERSHIP_REQUIRED | 422 |
PAGE_LIMIT_REACHED | 422 |
PARENT_NOT_ALLOWED | 422 |
RESERVED_SEGMENT | 422 |
RESERVED_SLUG | 422 |
RESERVED_SUBDOMAIN | 422 |
SANITIZE_FAILED | 422 |
SEARCH_CONSOLE_AUTH_EXPIRED | 422 |
SEARCH_CONSOLE_NOT_CONNECTED | 422 |
SEARCH_CONSOLE_PERMISSION_DENIED | 422 |
SEARCH_CONSOLE_PROPERTY_NOT_FOUND | 422 |
SEGMENT_TOO_LONG | 422 |
SITEMAP_NOT_REACHABLE | 422 |
SUBDOMAIN_MISSING | 422 |
SYSTEM_PAGE_LOCKED | 422 |
TEMPLATE_PRODUCT_LOCKED | 422 |
ANALYTICS_BUSY | 429 |
DAILY_LIMIT_REACHED | 429 |
RATE_LIMITED | 429 |
SEARCH_BUSY | 429 |
SEARCH_CONSOLE_QUOTA_EXCEEDED | 429 |
STOCK_BUSY | 429 |
INTERNAL | 500 |
SEARCH_CONSOLE_ERROR | 502 |
BUSY | 503 |
SEARCH_CONSOLE_UNAVAILABLE | 503 |
SERVICE_UNAVAILABLE | 503 |
WRITE_FROZEN | 503 |
Content Delivery API codes
| Code | HTTP status |
|---|---|
BAD_HOST | 400 |
FILE_TYPE_NOT_ALLOWED | 400 |
INVALID_ANSWERS | 400 |
INVALID_BODY | 400 |
INVALID_CURSOR | 400 |
INVALID_KIND | 400 |
INVALID_LIMIT | 400 |
INVALID_PATH | 400 |
INVALID_QUERY | 400 |
QUERY_REQUIRED | 400 |
QUERY_TOO_LONG | 400 |
QUERY_TOO_SHORT | 400 |
TOO_MANY_PARAMS | 400 |
UNKNOWN_BOT | 400 |
UNSUPPORTED_LOCALE | 400 |
VALIDATION | 400 |
VERIFICATION_EXPIRED | 400 |
NOT_PREVIEW | 403 |
COLLECTION_NOT_FOUND | 404 |
FORM_NOT_FOUND | 404 |
HEADER_NOT_FOUND | 404 |
ITEM_NOT_FOUND | 404 |
PAGE_NOT_FOUND | 404 |
PRODUCT_NOT_FOUND | 404 |
SITE_NOT_FOUND | 404 |
UNIT_NOT_FOUND | 404 |
DUPLICATE_SUBMISSION | 409 |
FILE_TOO_LARGE | 413 |
RATE_LIMITED | 429 |
SEARCH_BUSY | 429 |
QUERY_TIMEOUT | 503 |
UPLOAD_UNAVAILABLE | 503 |
UPSTREAM_UNAVAILABLE | 503 |
Customer API codes
| Code | HTTP status |
|---|---|
BAD_HOST | 400 |
INVALID_CODE | 400 |
INVALID_CURSOR | 400 |
INVALID_EMAIL | 400 |
INVALID_LIMIT | 400 |
VALIDATION | 400 |
UNAUTHENTICATED | 401 |
BOT_PROTECTION_FAILED | 403 |
DOWNLOAD_NOT_ALLOWED | 403 |
EXPIRED | 403 |
FORBIDDEN | 403 |
LIMIT_REACHED | 403 |
LOCKED | 403 |
NOT_RELEASED | 403 |
NO_ATTEMPTS_LEFT | 403 |
REVIEWS_DISABLED | 403 |
CERTIFICATE_DISABLED | 404 |
DELIVERABLE_NOT_FOUND | 404 |
NOT_FOUND | 404 |
NO_FILE | 404 |
NO_RECEIPT | 404 |
ORDER_NOT_FOUND | 404 |
SITE_NOT_FOUND | 404 |
UNIT_NOT_FOUND | 404 |
WEBSITE_NOT_FOUND | 404 |
METHOD_NOT_ALLOWED | 405 |
ALREADY_REVIEWED | 409 |
FILE_NOT_PROTECTED | 409 |
NOT_A_QUIZ | 409 |
QUIZ_NOT_READY | 409 |
ACCOUNT_BLOCKED | 422 |
ALREADY_REGISTERED | 422 |
CHALLENGE_EXPIRED | 422 |
EMAIL_NOT_VERIFIED | 422 |
METHOD_DISABLED | 422 |
VERIFY_FAILED | 422 |
RATE_LIMITED | 429 |
SHARED_SENDER_PAUSED | 503 |
SIGN_FAILED | 503 |
UPSTREAM_UNAVAILABLE | 503 |