Errors

How the ibakepro API reports errors, and how to handle them.

Almost every error uses one envelope. The exceptions are listed below.


Error response format

Error response

{
  "error": {
    "type": "validation_error",
    "code": "invalid_value",
    "message": "first_name is required",
    "field": "first_name"
  }
}
  • Name
    error.type
    Type
    string
    Description

    A broad category for the error, such as validation_error or authentication_error. Determines the HTTP status.

  • Name
    error.code
    Type
    string
    Description

    A specific machine-readable code. Branch on this, not on the message.

  • Name
    error.message
    Type
    string
    Description

    A human-readable description. Wording is not stable; do not parse it.

  • Name
    error.field
    Type
    string
    Description

    The request field that caused the error. Present on most validation errors, absent when the failure is not attributable to one field.

  • Name
    error.requiredScope
    Type
    string
    Description

    The scope your key is missing. Present only on 403 insufficient_scope.

Responses that do not use the envelope

Three responses are flat objects with a string error. Check the HTTP status before reaching for error.type.

429 Too Many Requests

{
  "error": "Too many requests",
  "message": "Please slow down and try again later",
  "retryAfter": 60
}

403 Plan required

{
  "error": "plan_required",
  "feature": "rest-api",
  "label": "Public REST API",
  "required": "business",
  "current": "starter"
}

403 Account inactive

{
  "error": "account_inactive",
  "status": "suspended"
}

plan_required means the account is not on the Business plan. account_inactive means the account status is neither active nor grace. Both apply to every /api/v2 endpoint and neither is retryable.


HTTP Status Codes

  • Name
    200 OK
    Description

    The request succeeded. Every GET returns 200.

  • Name
    201 Created
    Description

    A new resource was created.

  • Name
    204 No Content
    Description

    Returned to a CORS OPTIONS preflight on a publishable-key endpoint.

  • Name
    400 Bad Request
    Description

    The request was malformed, or a query parameter or body field was invalid.

  • Name
    401 Unauthorized
    Description

    The API key is missing, malformed, unknown, revoked, expired, or minted for a different region than the host you called.

  • Name
    403 Forbidden
    Description

    The key lacks the required scope, a publishable key was used on a secret-only endpoint, the account is not on the Business plan, the account is inactive, or a plan order limit was reached.

  • Name
    404 Not Found
    Description

    The requested resource does not exist under your account.

  • Name
    409 Conflict
    Description

    A conflicting resource already exists, or a request with the same Idempotency-Key is still in flight.

  • Name
    410 Gone
    Description

    The request was sent to /api/v1, which has been removed. Use /api/v2.

  • Name
    422 Unprocessable Entity
    Description

    An Idempotency-Key was reused with a different body or on a different route.

  • Name
    429 Too Many Requests
    Description

    A rate limit was exceeded. See Rate limiting.

  • Name
    500 Internal Server Error
    Description

    Something went wrong on our end.

  • Name
    503 Service Unavailable
    Description

    A dependency is temporarily unavailable. Retryable.


Error types

Authentication errors

  • Name
    authentication_error
    Description

    401, code invalid_api_key. Covers every rejection reason: no Authorization header, a key that does not match the key format, a key for the wrong region, an unknown or revoked key, an expired key, and a key string whose declared type does not match the stored key. The message is deliberately generic and never says which of these it was.

401 Unauthorized

{
  "error": {
    "type": "authentication_error",
    "code": "invalid_api_key",
    "message": "Invalid API key"
  }
}

Permission errors

  • Name
    permission_error
    Description

    403, code insufficient_scope. Your key does not hold the scope the endpoint requires. The response carries requiredScope naming it.

  • Name
    invalid_request_error
    Description

    403, code publishable_key_not_allowed. A publishable key was used on a secret-only endpoint. See which endpoints accept a publishable key.

403 Forbidden

{
  "error": {
    "type": "permission_error",
    "code": "insufficient_scope",
    "message": "This action requires the 'orders:write' scope",
    "requiredScope": "orders:write"
  }
}

Validation errors

All return 400 with type: "validation_error". The code identifies the problem.

  • Name
    invalid_value
    Description

    The default for a missing or invalid field. field names it.

  • Name
    invalid_json
    Description

    The request body was not parseable JSON.

  • Name
    invalid_cursor
    Description

    The cursor query parameter did not decode, or decoded to a record that no longer exists. Restart pagination from the first page.

  • Name
    conflicting_filters
    Description

    Two query filters cannot be combined. Returned by GET /expenses when updated_after or order_by=updated_at is combined with date_from or date_to, and when more than one equality filter is supplied.

  • Name
    unsupported_filter_sort_combination
    Description

    The requested sort cannot be served with the requested filter. Returned by GET /orders when created_after or created_before is combined with any sort other than the default createdAt.

  • Name
    customer_not_found
    Description

    POST /orders was given a customer.id that does not exist under your account. field is customer.id.

  • Name
    invalid_idempotency_key
    Description

    The Idempotency-Key header was empty, over 255 characters, or contained characters outside letters, digits, _, -, : and ..

Domain validation failures on create endpoints pass through their own code, so treat this list as the codes you can rely on, not as exhaustive.

400 Bad Request

{
  "error": {
    "type": "validation_error",
    "code": "invalid_cursor",
    "message": "Invalid cursor",
    "field": "cursor"
  }
}

Resource errors

  • Name
    not_found
    Description

    404, code not_found. The resource does not exist under your account.

  • Name
    conflict
    Description

    409, code conflict. A conflicting resource already exists, for example a customer with the same email on POST /customers.

404 Not Found

{
  "error": {
    "type": "not_found",
    "code": "not_found",
    "message": "Order 'HR7Pcz4Arg6kvZKseSil' not found"
  }
}

Idempotency errors

Returned on a misused Idempotency-Key. The type is idempotency_error; the code distinguishes the case. See Idempotency.

  • Name
    idempotency_key_reused
    Description

    422. The key was previously used with a different body, or on a different endpoint. Use a fresh key for a different operation.

  • Name
    request_in_flight
    Description

    409. A request with the same key is still being processed. Retry shortly. A request that has already failed releases its key immediately, so this only appears while an attempt is genuinely running.

422 Unprocessable Entity

{
  "error": {
    "type": "idempotency_error",
    "code": "idempotency_key_reused",
    "message": "This Idempotency-Key was previously used with a different request. Use a new key for a different operation."
  }
}

Plan limit

  • Name
    limit_reached
    Description

    403, with type: "rate_limit_error". POST /orders was rejected because the account has reached its plan order limit. The body carries limit, current and max. This is not a rate limit and retrying will not clear it until the limit window rolls over or the account is upgraded.

403 Forbidden

{
  "error": {
    "type": "rate_limit_error",
    "code": "limit_reached",
    "message": "Order limit reached",
    "limit": "orders",
    "current": 500,
    "max": 500
  }
}

Server and availability errors

  • Name
    api_error
    Description

    500, code internal_error. An unexpected failure on our side. A failed request releases its Idempotency-Key immediately, so the same key can be retried.

  • Name
    service_unavailable
    Description

    503, code search_not_configured. POST /products/search-token could not mint a token because search is not currently available for the account. Retryable.

  • Name
    deprecated
    Description

    410, code api_version_deprecated. The request went to /api/v1. Move to /api/v2.

500 Internal Server Error

{
  "error": {
    "type": "api_error",
    "code": "internal_error",
    "message": "An unexpected error occurred"
  }
}

Rate limiting

Exceeding a limit returns 429. This body does not use the error object envelope: error is a plain string, alongside message and retryAfter in seconds.

Prefer the Retry-After header over the body field. The X-RateLimit-* headers on the response tell you which bucket you hit. See Rate limiting.

429 Too Many Requests

{
  "error": "Too many requests",
  "message": "Please slow down and try again later",
  "retryAfter": 60
}

Handling errors

JavaScript error handling

async function createOrder(orderData) {
  const response = await fetch('https://au.api.ibakepro.com/api/v2/orders', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.IBAKEPRO_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(orderData),
  })

  if (response.ok) {
    return response.json()
  }

  const result = await response.json()

  // A few responses use a flat shape: { error: string, ... }
  if (typeof result.error === 'string') {
    if (response.status === 429) {
      const retryAfter = response.headers.get('Retry-After') ?? result.retryAfter
      throw new Error(`Rate limited, retry after ${retryAfter}s`)
    }
    // plan_required or account_inactive
    throw new Error(result.error)
  }

  // Everything else uses { error: { type, code, message, field } }
  switch (result.error.code) {
    case 'insufficient_scope':
      throw new Error(`Key is missing scope ${result.error.requiredScope}`)
    case 'request_in_flight':
      throw new Error('Retry shortly with the same Idempotency-Key')
    case 'idempotency_key_reused':
      throw new Error('Use a fresh Idempotency-Key for a different request')
    case 'customer_not_found':
      throw new Error(result.error.message)
    default:
      throw new Error(`${result.error.type}: ${result.error.message}`)
  }
}