Errors Every error is an RFC 9457 problem document. Branch on its type and detail_key; show detail to people.
The problem document HTTP/1.1 404 Not Found
Content-Type: application/problem+json
{
"type": "https://officehero.io/errors/not-found",
"title": "Not found",
"status": 404,
"detail": "Invoice not found.",
"code": "not_found",
"detail_key": "errors.integrations.invoices.not_found"
} Member Always Use it to typeyes Decide the category (see the table below). Stable. titleyes Nothing — a short label for the type. It is translated to your Accept-Language like detail (Nicht gefunden for de), so never compare it. statusyes The HTTP status, repeated. detailyes Show a human what happened. It is translated to your Accept-Language and its wording can change, so never compare it. detail_keyalmost always Branch on the exact cause, e.g. errors.integrations.invoices.void_failed. Stable. Absent on a 405 (wrong method), on a rate-limit 429, on an unexpected 500 and on the two idempotency 409s, whose key is in detail. codeusually A short token. On a 422 about one field it is that field's validation code (e.g. unknown_field); on other refusals the last segment of detail_key (not_found, endpoint_not_active, rate_limited). field, errors[]validation only Which fields were wrong: each entry has path, code, message. missing_scope, missing_scopeson 403 The scope your key needs; for a webhook subscription, every read scope it needs. unknown_event_typeson 422 unknown_event_type The event types that are not in the catalogue. flag_codeon 403 feature_disabled The feature that is switched off (integrations.api.enabled). retry_after_son rate-limit 429 Seconds to wait (also the Retry-After header).
Error types detail_key values
The detail_key values this API answers with, grouped by resource. Read endpoints also answer
…<resource>.invalid_cursor (422) for an unreadable cursor and
…<resource>.not_found (404) for an unknown id. The Platform group holds keys from
the layers every OfficeHero API shares — an id that is not a UUID, an unknown path, a reference to a record you
do not have, Finance rules. A 405, a rate-limit 429 and an unexpected 500 carry no
detail_key.
Platform detail_keyStatus English detail errors.common.tenant_not_found404 Tenant not found errors.common.validation.failed422 Some of the values you sent are not valid. Correct the fields marked with an error and send the form again. errors.common.validation.invalid_input422 That request wasn't valid — check the values you sent and try again. errors.common.validation.invalid_reference422 That references something that doesn't exist, or that you can't see. errors.finance.validation.currency_not_enabled422 That currency is not enabled for your organisation — enable it in currency settings or pick another. errors.finance.validation.invalid_invoice_status_for_void422 This invoice can no longer be voided in its current status errors.idempotency.body_mismatch409 This Idempotency-Key was already used with a different request body (sent as the raw key in detail). errors.not_found404 Resource not found errors.platform.feature_flag.disabled403 This feature isn't enabled for your plan errors.platform.idempotency.replay_unavailable409 The original request already completed but its response was too large to replay — read the resource instead (sent as the raw key in detail).
bills detail_keyStatus English detail errors.integrations.bills.invalid_cursor422 The pagination cursor is invalid or malformed. errors.integrations.bills.not_found404 Bill not found.
deliveries detail_keyStatus English detail errors.integrations.deliveries.endpoint_not_active409 Deliveries can only be replayed to an active endpoint. Resume the endpoint first. errors.integrations.deliveries.invalid_since422 The since parameter must be a valid ISO-8601 timestamp. errors.integrations.deliveries.not_found404 Delivery not found. errors.integrations.deliveries.pii_not_granted403 This delivery carries personal data and the endpoint is not approved to receive it. OfficeHero must enable the endpoint for personal data first — contact support.
documents detail_keyStatus English detail errors.integrations.documents.invalid_cursor422 The pagination cursor is invalid or malformed. errors.integrations.documents.not_found404 Document not found.
employees detail_keyStatus English detail errors.integrations.employees.invalid_cursor422 The pagination cursor is invalid or malformed. errors.integrations.employees.not_found404 Employee not found.
fuel logs detail_keyStatus English detail errors.integrations.fuel_logs.invalid_cursor422 The pagination cursor is invalid or malformed. errors.integrations.fuel_logs.not_found404 Fuel log not found.
Every endpoint detail_keyStatus English detail errors.integrations.invalid_cursor422 The pagination cursor is invalid or malformed. errors.integrations.missing_scope403 The API key does not have the scope required for this endpoint. errors.integrations.unauthorized401 Authentication required. Provide a valid API key. errors.integrations.unsupported_version400 The requested OH-Version is not supported. errors.integrations.version_sunset410 The requested OH-Version has been sunset. Migrate to a supported version.
invoices detail_keyStatus English detail errors.integrations.invoices.create_failed422 The invoice could not be created. errors.integrations.invoices.invalid_body422 This request could not be read. It must be valid JSON: an object or a list, no more than 63 levels deep and no larger than 2 MiB. errors.integrations.invoices.invalid_cursor422 The pagination cursor is invalid or malformed. errors.integrations.invoices.line_invalid422 One or more invoice lines are invalid. errors.integrations.invoices.no_writable_fields422 No writable fields were provided. errors.integrations.invoices.not_found404 Invoice not found. errors.integrations.invoices.required422 A required field is missing: {field}. errors.integrations.invoices.update_failed422 The invoice could not be updated. errors.integrations.invoices.validation_failed422 The invoice failed validation. errors.integrations.invoices.void_failed422 The invoice could not be voided.
leave requests detail_keyStatus English detail errors.integrations.leave_requests.invalid_cursor422 The pagination cursor is invalid or malformed. errors.integrations.leave_requests.not_found404 Leave request not found.
payments detail_keyStatus English detail errors.integrations.payments.invalid_cursor422 The pagination cursor is invalid or malformed. errors.integrations.payments.not_found404 Payment not found.
purchase orders detail_keyStatus English detail errors.integrations.purchase_orders.invalid_cursor422 The pagination cursor is invalid or malformed. errors.integrations.purchase_orders.not_found404 Purchase order not found.
tickets detail_keyStatus English detail errors.integrations.tickets.invalid_cursor422 The pagination cursor is invalid or malformed. errors.integrations.tickets.not_found404 Ticket not found.
timesheets detail_keyStatus English detail errors.integrations.timesheets.invalid_cursor422 The pagination cursor is invalid or malformed. errors.integrations.timesheets.not_found404 Timesheet not found.
vehicles detail_keyStatus English detail errors.integrations.vehicles.invalid_cursor422 The pagination cursor is invalid or malformed. errors.integrations.vehicles.not_found404 Vehicle not found.
webhooks detail_keyStatus English detail errors.integrations.webhooks.event_types_required422 At least one event type is required. errors.integrations.webhooks.invalid_body422 The webhook could not be saved. Give it a name, the address to call, and at least one event type. errors.integrations.webhooks.invalid_pinned_version422 The pinned OH-Version is not a supported version. errors.integrations.webhooks.not_found404 Webhook endpoint not found. errors.integrations.webhooks.owner_key_inactive409 This endpoint belongs to an API key that was revoked or has expired, so it cannot be resumed. Create a new endpoint with an active key. errors.integrations.webhooks.quota_exceeded429 The webhook endpoint quota for this tenant has been reached. errors.integrations.webhooks.required422 A required field is missing: {field}. errors.integrations.webhooks.unknown_event_type422 One or more event types are not in the event catalog. errors.integrations.webhooks.url_not_publicly_routable422 The webhook URL must be publicly routable (no private, loopback, or link-local addresses).
work orders detail_keyStatus English detail errors.integrations.work_orders.invalid_cursor422 The pagination cursor is invalid or malformed. errors.integrations.work_orders.not_found404 Work order not found.
What to retry Status Retry? 400, 401, 403, 404, 410, 422No — change the request first. 409Not with the same Idempotency-Key and a different body. 429Yes, after Retry-After. 500, 502, 503, 504, network errorsYes, with exponential backoff and jitter; for writes reuse the same Idempotency-Key.
Troubleshooting 401 on every call — the header must be exactly Authorization: Bearer oh_live_…. Check the key has not expired or been revoked. 403 with missing_scope — the key was created without that scope; create a new key. "CORS error" in a browser — the API answers browser requests only from this portal. Call it from your server. 429 — see Rate limits . Anything else — contact support with the x-trace-id response header, the time (UTC), the method and path, and your key's prefix (never the secret). Previous Versioning Next Rate limits