Skip to content

Errors

The BenchKey API uses a single, consistent error envelope across every endpoint. When a request fails, the HTTP status is non-2xx and the body is always:

{
"error": {
"type": "invalid_request_error",
"code": "missing_field",
"message": "Field 'customer_id' is required.",
"param": "customer_id",
"request_id": "req_01J8X4abc123"
}
}
FieldTypeDescription
typestringMachine-stable error category (see Error types)
codestringSpecific machine-stable code within the type (see Error codes)
messagestringHuman-readable description, safe to display to developers
paramstring | nullThe request field that caused the error, if applicable
detailsobject | nullExtra structured data (e.g. required_scope) when helpful
request_idstringEchoes X-Request-Id — quote this when contacting support

The type field groups errors by category. Each type maps to a fixed HTTP status:

TypeHTTP statusMeaning
invalid_request_error400 / 413Malformed input, missing or invalid parameters, or body too large
authentication_error401API key is missing, malformed, or revoked
payment_required_error402Billing action required — plan doesn’t include API access, or tenant’s subscription is inactive
permission_error403Key exists but lacks the required scope or permission
not_found_error404Resource does not exist, or belongs to a different tenant
method_not_allowed_error405The HTTP method is not supported on this endpoint
request_timeout_error408The operation timed out — transient, safe to retry
conflict_error409Idempotency replay mismatch, duplicate resource, or in-progress conflict
gone_error410The resource existed but has been permanently removed
unsupported_media_type_error415Write request body sent without Content-Type: application/json
unprocessable_error422Input is structurally valid but violates a business rule
rate_limit_error429Per-key rate limit exceeded
api_error500Unexpected server-side failure
service_unavailable_error503A dependency is temporarily unavailable, or the tenant is suspended

The code field is specific within a type. Use it for programmatic error handling — it is stable across API versions.

CodeDescription
missing_fieldA required field was not supplied. param names the field.
invalid_fieldA field was supplied but its value is invalid (wrong type, out-of-range, bad enum). param names the field.
invalid_queryA query parameter is invalid (wrong type, array where scalar expected, or out-of-range). param names the parameter.
invalid_idempotency_keyThe Idempotency-Key header is present but does not meet length or character requirements.
invalid_valueA supplied value is semantically out of range for the target field (e.g. an integer exceeds the column’s 32-bit range, or a string exceeds its maximum length). Correct the value and retry. param names the field where known.
payload_too_largeThe request body exceeds the maximum allowed size (HTTP 413).
CodeDescription
unauthorizedThe Authorization header is absent, malformed, or contains a revoked key.
CodeDescription
insufficient_scopeThe key is valid but lacks the required scope. details.required_scope names the missing scope.
forbiddenThe key is valid but is not permitted to perform this action for another reason.
CodeDescription
upgrade_requiredThe key’s tenant plan does not include public API access. details contains required_plan, current_plan, and upgrade_url.
payment_requiredA billing action is required to proceed (e.g. past-due balance or entitlement gate).
billing_subscription_requiredThe tenant’s billing subscription is inactive. API access is unavailable until billing is restored. details contains billing_required, reason, subscription_status, and current_period_end.
CodeDescription
not_foundThe requested resource does not exist or does not belong to this tenant.
CodeDescription
method_not_allowedThe HTTP method used is not supported on this endpoint. The response includes an Allow header listing the accepted methods.
CodeDescription
request_timeoutThe underlying operation timed out before completing. This is a transient error — retry with an Idempotency-Key to avoid duplicates. The response may include a Retry-After header.
CodeDescription
conflictA duplicate or conflicting resource was detected.
idempotency_key_reuseAn Idempotency-Key was reused with a different request body or URL. See Idempotency.
idempotency_in_progressA request with this Idempotency-Key is still being processed. Wait and retry.
CodeDescription
goneThe resource existed but has been permanently removed and will not be restored. Stop retrying.
CodeDescription
unsupported_media_typeThe request body was not sent with Content-Type: application/json.
CodeDescription
unprocessableThe request is structurally valid but fails a business rule (e.g. closing a ticket that is already closed). The message field explains the specific rule.
CodeDescription
rate_limitedThe per-key request limit was exceeded. The response includes a Retry-After header with the number of seconds to wait. See Rate limits.
CodeDescription
internal_errorAn unexpected server-side error occurred. Quote the request_id when contacting support.
CodeDescription
service_unavailableA dependency (database, third-party service) is temporarily unavailable. Retry with exponential backoff.
tenant_suspendedThe tenant account has been suspended. API access is unavailable until the account is reinstated.
Terminal window
response=$(curl -s -w "\n%{http_code}" https://app.benchkey.com/api/v1/customers/cust_missing \
-H "Authorization: Bearer $BENCHKEY_API_KEY" \
-H "BenchKey-Version: 2026-06-13")
body=$(echo "$response" | head -n -1)
status=$(echo "$response" | tail -n 1)
echo "Status: $status"
echo "Body: $body"
const res = await fetch(
"https://app.benchkey.com/api/v1/customers/cust_missing",
{
headers: {
Authorization: `Bearer ${process.env.BENCHKEY_API_KEY}`,
"BenchKey-Version": "2026-06-13",
},
}
);
if (!res.ok) {
const { error } = await res.json();
// error.type, error.code, error.message, error.param, error.request_id
if (error.code === "not_found") {
console.error("Customer not found:", error.message);
} else if (error.code === "rate_limited") {
const retryAfter = res.headers.get("Retry-After");
console.error(`Rate limited. Retry in ${retryAfter}s`);
} else {
console.error(`[${error.code}] ${error.message} (request_id: ${error.request_id})`);
}
}

Example error response — missing required field

Section titled “Example error response — missing required field”
HTTP/1.1 400 Bad Request
Content-Type: application/json
X-Request-Id: req_01J8X4abc123
{
"error": {
"type": "invalid_request_error",
"code": "missing_field",
"message": "Field 'customer_id' is required.",
"param": "customer_id",
"request_id": "req_01J8X4abc123"
}
}

Example error response — insufficient scope

Section titled “Example error response — insufficient scope”
HTTP/1.1 403 Forbidden
Content-Type: application/json
X-Request-Id: req_01J8X4def456
{
"error": {
"type": "permission_error",
"code": "insufficient_scope",
"message": "This API key is missing the required scope 'tickets.write'.",
"details": {
"required_scope": "tickets.write"
},
"request_id": "req_01J8X4def456"
}
}

At minimum, log error.code, error.message, and error.request_id for every non-2xx response. The request_id is the fastest path to support resolution when something goes wrong.