Skip to content

API error codes

Enterprise feature

The REST API is available on Enterprise plans. Compare plans to find the right fit for your team.

When an API request fails, the response includes an HTTP status code and a structured JSON body with details about the error. This page covers every error you may encounter and how to resolve it.


Error response format

All error responses follow this structure:

{
  "success": false,
  "error": "error_code",
  "message": "A human-readable description of what went wrong."
}
Field Type Description
success boolean Always false for errors
error string Machine-readable error code (snake_case)
message string Human-readable description you can display to users

HTTP status codes

400 — Bad request

The request is malformed or contains invalid data.

Error code Description How to fix
validation_failed One or more required fields are missing or invalid Check the request body against the endpoint documentation
missing_required_field A required field was not included Add the missing field to your request
assignee_required You set a job to Assigned without a technician on it Assign a technician to the job first, then set the status
sla_required You tried to clear a job's SLA deadline. Every job must have one Send a new due_at, or leave the field out of the request to keep the existing deadline. See SLA deadlines
meter_not_found The specified meter does not exist for this asset Configure meters for the asset in the dashboard first
invalid_status On a GET list endpoint, the status query parameter is not a value the resource can hold (assets, technicians) The message lists every accepted value — send one of those. Note this code also appears under 409 with a different meaning; see the note below
invalid_availability_status On the technicians GET list endpoint, the availability_status query parameter is not a value a technician can hold The message lists every accepted value — send one of those. This code is 400 only; it has no 409 sense
invalid_sub On the jobs GET endpoint, the sub query parameter is not hold-reasons, or it was sent together with id Send sub=hold-reasons with no id to list your company's hold reasons
custom_field_invalid A job's custom_fields object was refused: a key that is not one of your fields, a value of the wrong type, or a field required at creation left out The errors object names each field key and the problem. Nothing was written — fix those values and resend. See Custom fields
final_comment_required You set a job to Completed without a final_comment Add a short note on the work done as final_comment. See Completing a job
comment_too_long The job's final_comment is longer than 500 characters Shorten the comment and resend

401 — Unauthorized

Authentication failed or was not provided.

Error code Description How to fix
unauthorized API key is missing, invalid, or revoked Verify your X-API-Key header contains a valid key

Check your key

If you recently rotated your key, make sure all integrations use the new one. Revoked keys return 401 immediately.

403 — Forbidden

The API key does not have permission for this action.

Error code Description How to fix
forbidden The key's scopes do not include the required permission Update the key's scopes in Settings → API Keys
ip_not_allowed Request came from an IP not in the key's whitelist Add the source IP to the key's whitelist or remove the restriction
scope_not_authorized The API key lacks the required scope for this operation Add the required scope to your API key in Settings → API Keys

Suspended or inactive company account

When your company account is suspended or set to inactive, every API request returns 403 forbidden with the message "This company account is suspended". Your keys start working again on the next request once the account is active. Contact support to restore the account.

404 — Not found

The requested resource does not exist.

Error code Description How to fix
not_found The resource ID does not exist or is not accessible Verify the ID is correct and belongs to your company

405 — Method not allowed

The HTTP method is not supported for this endpoint.

Error code Description How to fix
method_not_allowed You used the wrong HTTP method (e.g., POST to a read-only endpoint) Check the endpoint documentation for the correct method

409 — Conflict

The request conflicts with the current state of the resource.

Error code Description How to fix
customer_duplicate_contact Another active customer in your account already uses this email address or phone number Check matched_field to see which one collided. The response also returns the customer_id and customer_name of the existing record — update that customer, add the site as another location on it, or use a different contact detail
customer_soft_deleted_conflict A removed customer still holds this email address or phone number Restore that customer from the dashboard, or use a different contact detail. The response returns its customer_id and customer_name
duplicate_customer A row in a CSV bulk import matched an existing customer Bulk import only — remove the duplicate row or let the import skip it
conflict The resource was modified by another request since you read it (optimistic concurrency / concurrent status change) Re-read the resource and retry — for job updates, resend with the current version as expected_version
invalid_status On a write endpoint, the status you sent is not one this resource uses The message lists every status the resource accepts — send one of those
invalid_transition The status change is not allowed from the status the resource is in right now The message lists the statuses you can move to from here. Move through an allowed step first, or re-read the resource if someone else already changed it
invalid_state The resource is in a terminal state — a completed, declined or cancelled job, or a decommissioned asset — and can no longer be edited Move it out of the terminal state first (reopen the job), then resend. A decommissioned asset cannot be updated through the write endpoints at all
job_forms_incomplete You set a job to Completed while forms it requires are not submitted The missing list names each form and its unanswered items. Submit the forms in the app, then resend. The API cannot skip this check

invalid_status carries two senses, and the HTTP code tells them apart

On a write endpoint the status names a resource state, so the refusal is 409 Conflict. On a GET list endpoint it is only a malformed query-string filter — nothing conflicts with any resource state — so the refusal is 400 Bad Request. Branch on the status code, not on the error string alone.

Never retry a 409

A contact conflict is permanent: each account allows one customer per email address and one per phone number, and a removed customer keeps its contact details until it is restored. The identical request will fail the same way every time, so resolve the conflict rather than retrying.

422 — Unprocessable

The request is well formed, but what it asks for cannot be done with the record as it is.

Error Description Resolution
custom_fields_required You set a job to Completed while custom fields required for completion are empty The missing list names each field (field_key, field_name). Fill them in with a PUT carrying custom_fields, then complete the job again. See Completing a job
hold_reason_required You set a job to On-Hold without a hold_reason_key Send one of your company's hold reasons. See Putting a job on hold
invalid_hold_reason The hold_reason_key is not on your company's list, or the reason is hidden Use a key from the list in Putting a job on hold or one your company added
hold_note_required The reason is other and no hold_reason_note was sent Add a short note and send the request again
hold_note_too_long The hold_reason_note is over 500 characters Shorten the note to 500 characters or fewer

429 — Too many requests

You have exceeded the rate limit.

Error code Description How to fix
rate_limit_exceeded Too many requests in the current time window Wait for the duration specified in the Retry-After header

The response includes a retry_after field (seconds) and a Retry-After HTTP header.

See Rate limits for best practices.

500 — Server error

An unexpected error occurred on the server.

Error code Description How to fix
server_error An internal error occurred Retry the request after a short delay. If the issue persists, contact support.

Troubleshooting checklist

Use this checklist when you encounter an API error:

  • Check the HTTP status code — it tells you the category of the problem.
  • Read the error and message fields — they pinpoint the specific issue.
  • Verify your API key — is it valid, not revoked, and included in the X-API-Key header?
  • Check your scopes — does the key have permission for this operation?
  • Validate your request body — are all required fields present and correctly typed?
  • Review the Retry-After header — if rate-limited, wait before retrying.
  • Contact support — if you see 500 errors consistently, reach out with the request details.

Frequently asked questions

Why do I get 401 even though my key looks correct?

Make sure the key is in the X-API-Key header — not in the URL or request body. Also verify the key has not been revoked.

Can I get more details about a 400 error?

Yes. The message field describes which validation rule failed, and in most cases names the problematic field.

What should I do on a 500 error?

Retry after a short delay (5–10 seconds). If the error persists, contact support with the request method, endpoint, and approximate timestamp.