Something new is coming up soon from Kanishu — stay tuned.

Developers › Errors

API error codes

Every error has the same shape, a stable code to act on, and a request_id to quote to support.

Generated from Suite\ApiKit\ApiError::CODES (common/api-kit). Don't edit by hand: change the list in code and run php common/api-kit/bin/errors-doc.php. This is part of the API standard (api-standard.md) and goes into the developer documentation.

The error body

Every failed call to any Kanishu product API (status 400 or above) returns:

{
  "error": {
    "code": "validation_failed",
    "message": "The email field must be a valid email address.",
    "details": { "fields": { "email": ["The email field must be a valid email address."] } },
    "request_id": "6f1c2a7e-0d7b-4c1e-9a55-2f0c3d1b8e41"
  }
}
  • code: what went wrong, from the list below. Program against this, not the message. Codes keep their names within a version; new ones may be added.
  • message: a sentence for a person, which may change wording.
  • details: extra facts for that code (always an object, often empty). Examples: the fields for validation_failed, the limit and how much is used for limit_reached, the existing record for duplicate.
  • request_id: quote it to support. It is also in the X-Request-Id header of every response, success or failure. Send your own X-Request-Id (up to 64 letters, digits and dashes) to tie our logs to yours; otherwise we generate one.
  • A 5xx never carries internal detail: no exception text, SQL, file names or stack traces.
  • 429 responses carry Retry-After (seconds).
  • Kept for v1: a few older endpoints also put extra fields next to code inside error (for example used and limit_value for limit_reached). Validation failures also keep Laravel's top-level message and errors. The same facts are in details; use details.
  • Security: a bad, expired, revoked or switched-off token all return the same invalid_token, and "not yours" returns not_found. Guessing therefore tells an attacker nothing.

Codes

Authentication

Code Status When What to do
unauthenticated 401 No token was sent, or the Authorization header is not Bearer <token>. Send the token as Authorization: Bearer <token>.
invalid_token 401 The token is unknown, expired, revoked, switched off for inactivity, or its person was disabled. Deliberately one code, so a guessed token can't be told apart from a real one. Check the token in API Access. Request a new one if it ended.
bad_signature 401 A request signature is missing, wrong or too old (high-risk scopes only). Sign every call with the token's signing secret (Kanishu-Signature: t=…,n=…,v1=…), a fresh timestamp and a new nonce. message says what was wrong.
token_in_url 400 A token was put in the URL (?token=, ?access_token=, …). Refused, because URLs end up in logs. Send it as Authorization: Bearer <token>, and replace that token: it may now be in logs.
session_expired 401 A browser session ended (screens only, not tokens). Sign in again.
ip_not_allowed 403 The token has an IP allow-list and the call came from another address. Call from an allowed address, or ask for the list to be changed (a new request).

Authorisation

Code Status When What to do
invalid_scope 403 The token does not have the scope this endpoint needs. Request access with that scope.
forbidden 403 The token's person may not do this (their role or the record's rules). Ask an admin, or use a token of someone who may.
forbidden_organization 403 The call named another organisation. A customer token reaches only its own. Leave out organization_id, or use your own.
not_supported 403 This operation needs a different kind of token (for example a person's own token, not an API client's). Use the kind of token the endpoint describes.

Plan

Code Status When What to do
feature_not_included 402 The organisation's plan does not include this feature. details.feature names it. Upgrade the plan, or don't use this endpoint.
subscription_read_only 402 The subscription has ended and is read-only: reads work, changes are refused until it is renewed. Renew the subscription in Kanishu Hub; until then, only read.
subscription_suspended 402 The subscription has ended and access is suspended; the message says when the data is deleted. Renew the subscription in Kanishu Hub.
limit_reached 402 The write would go over a plan limit. details has the limit and how much is used. Free some up, or upgrade the plan.

Usage

Code Status When What to do
rate_limited 429 Too many calls in a short time (server protection). Wait for the seconds in Retry-After, then retry.
quota_exceeded 429 The organisation's daily quota for this product function is used up. details names the function and the quota. Wait until it resets at midnight (Retry-After), or upgrade the plan.
daily_limit_exceeded 429 ITAM's v1 name for quota_exceeded: the organisation's daily quota for this function is used up. Wait until the quota resets (the Retry-After header), or upgrade the plan.

Request

Code Status When What to do
bad_request 400 The request could not be read (malformed JSON, wrong content type, bad parameters). Fix the request; message says what is wrong.
invalid_request 400 A parameter is not valid for this endpoint. Fix the parameter named in message.
request_failed 400 A general refusal with the reason in message (older endpoints; replaced by a specific code where one fits). Read message.
validation_failed 422 One or more fields are invalid. details.fields lists each field and its messages. Fix the fields listed.
missing_organization_id 422 A product-to-product call did not say which organisation. Send organization_id.
not_eligible 422 The record is not in a state that allows this (for example a ticket not yet resolved). Check the record's state first.
approval_refused 422 An approval could not be recorded; message says why. Read message.
incomplete 422 Part of a multi-part change was missing, so nothing was changed. Send the complete change.
activation_failed 422 Hub could not switch on a product for an organisation (Hub to product only). Hub retries; see its logs.
idempotency_key_reused 409 The same Idempotency-Key was sent with a different request. Use a new key for a new request.
idempotency_in_progress 409 A request with this Idempotency-Key is still being processed. Retry after the seconds in Retry-After; you will get the first request's answer.
invalid_filter 400 A filter[…] the list does not offer. details.allowed lists the ones it does. Use only the listed filters.
invalid_sort 400 A sort field the list does not offer. details.allowed lists the ones it does. Sort only by the listed fields.
invalid_include 400 An include the endpoint does not offer. details.allowed lists the ones it does. Ask only for the listed includes.
invalid_fields 400 A fields name the answer does not have. details.allowed lists the ones it does. Ask only for the listed fields.
not_found 404 No such record or endpoint for this organisation. Also used where saying "exists but not yours" would leak information. Check the id and the URL.
method_not_allowed 405 The endpoint exists but not with this HTTP method. Use the method in the API reference.
conflict 409 The change clashes with the record's current state. Reload the record and try again.
duplicate 409 A matching record already exists. details has its id. Use the existing record.
exists 409 The thing being created already exists. Use the existing one.
version_conflict 412 The record changed since you read it: the If-Match version is old (412, with details.current_version). Some older screens' endpoints answer 409. Read the record again and apply your change to the new version.
payload_too_large 413 The request body or file is too big. Send less, or split it.
unsupported_media_type 415 The body is not JSON (or the file type is not accepted). Send Content-Type: application/json.

Availability

Code Status When What to do
product_maintenance 503 The product is in planned maintenance. Retry later.
product_disabled 503 The product is switched off on the platform. Contact support.
platform_tenant_not_configured 503 A platform-level setup step is missing. Contact support.
unavailable 503 The service is briefly unavailable (for example during a deploy). Retry after a short wait.

Server

Code Status When What to do
server_error 500 Something failed on our side. Never includes internal detail. Retry once; if it persists, contact support quoting request_id.

When a response has no code of its own, the code comes from the status: 400 bad_request, 401 unauthenticated, 402 limit_reached, 403 forbidden, 404 not_found, 405 method_not_allowed, 409 conflict, 412 version_conflict, 413 payload_too_large, 415 unsupported_media_type, 422 validation_failed, 429 rate_limited, 500 server_error, 503 unavailable.