type for control flow and on code for specific handling. Both are
stable; message is written for humans and may be reworded.
Always log request_id. It is echoed in the X-Request-ID response header, and
quoting it lets us find your exact request.
Types
A 404 does not distinguish “does not exist” from “belongs to another
workspace”. That is deliberate: telling them apart would let anyone probe for
record ids across workspaces.
Missing scopes
A 403 from a scope check names exactly what is missing, so you do not have to guess:insufficient_scope usually means workspace write policy —
for example, an entity mirrored from an external CRM. Call
GET /v1/capabilities to see which entities you may write.
Retrying
Retryrate_limit_error and api_error. Do not retry 4xx errors unchanged;
they will fail identically.
Error codes
Everyerror.code, grouped by HTTP status. error.documentation_url links straight to the entry for the code you received. Codes are permanent: branch on them.
400 Bad Request
bad_request
The request could not be understood.invalid_cursor
The cursor is corrupt, or was issued for a different query. Pass backlinks.next unchanged, with the same filters and sort.
unknown_expand
A relationship inexpand does not exist on this resource.
expand_limit_exceeded
Too many relationships inexpand; the limit is 4.
unknown_field
A name infields is not on this resource.
unknown_sort_field
This resource cannot be sorted by that key.unknown_entity
No resource has that name.too_many_filter_values
A filter parameter was repeated more times than allowed.unsupported_sort
This collection cannot be sorted that way.invalid_window
The time window is empty or reversed.401 Unauthorized
unauthorized
The token is missing, unknown, revoked or expired.token_expired
The OAuth access token expired. Refresh it and retry.403 Forbidden
insufficient_scope
The token lacks a scope this endpoint needs;param names the first missing one. GET /v1/me lists what the token has.
forbidden
The request is not allowed for this token.local_write_denied
The workspace has switched off this kind of write, usually because an external CRM is its system of record.GET /v1/capabilities shows the policy.
user_grant_denied
The person who authorized this OAuth connection is not allowed to make this change in Sailer.note_author_only
Only the teammate who wrote this note can change or delete it.conversation_access_denied
The person who authorized this OAuth connection cannot act on this conversation in Sailer.channel_access_denied
The person who authorized this OAuth connection cannot use a channel this campaign sends through.campaign_dispatch_denied
The person who authorized this OAuth connection is not allowed to send campaigns.404 Not Found
not_found
No such record in this workspace.contact_not_found
A contact id in the request does not exist in this workspace.pipeline_not_found
The pipeline id does not exist in this workspace.stage_not_found
No stage with that id or name in the pipeline.record_not_found
A contact, organization or deal id in the request body does not exist in this workspace, or is not visible to this token;param names the field, such as record_id, contact_id or organization_id.
405 Method Not Allowed
api_error
The method is not supported on this path.409 Conflict
conflict
The request conflicts with the current state.already_exists
The record already exists.external_id_taken
Another record of the same resource already has thisexternal_id; the message names it.
external_id_conflict
Theexternal_id and the phone of an upsert name different contacts, or the matched contact already has another external id.
phone_mismatch
The contact with thisexternal_id has a different phone. Upsert never changes a contact’s phone.
contact_already_exists
A contact with this phone already exists; the message names it.POST /v1/contacts/upsert creates or updates in one call.
campaign_not_accepting_participants
The campaign has finished and takes no new participants.idempotency_key_reused
ThisIdempotency-Key was already used with a different request body. Use a new key for a new request.
idempotency_key_in_flight
A request with thisIdempotency-Key is still being processed. Retry after it completes.
tag_already_exists
The resource’s tag catalog already has a tag with this name.conversation_closed
The conversation is closed; it cannot be handed off, assigned or resumed.conversation_not_with_ai
A teammate or the queue already has this conversation, so there is nothing to hand off. Useassign to change the teammate.
conversation_with_ai
The AI agent is handling this conversation. Usehandoff to give it to a person first.
conversation_already_with_ai
The AI agent is already handling this conversation.conversation_has_no_contact
The conversation has no contact, or its contact no longer exists, so it cannot be closed here.no_agent_available
The conversation’s queue has no active AI agent with a share of its work to hand it back to.conversation_queue_unavailable
The queue the conversation is in no longer exists, so it cannot be handed off. Move it to another queue in Sailer.campaign_not_running
Only a running campaign can be paused.campaign_not_paused
Only a paused campaign can be resumed.campaign_not_ready
The campaign is missing setup it needs to send; the message names it.campaign_has_no_message
The campaign has no active message to send.campaign_template_variables_unresolved
A template variable in the campaign message cannot be filled.campaign_template_not_approved
A template the campaign sends is not approved by WhatsApp yet.campaign_channel_unavailable
A channel the campaign sends through is missing or not active.already_reversed
This event was already reversed; an event can be reversed once.not_a_record
A reversal cannot itself be reversed.422 Unprocessable Content
validation_failed
Some fields are invalid;detail lists each one. Query, path and header problems answer 400 with the same code.
invalid_request
The request is well-formed but cannot be applied as given.stage_not_in_pipeline
The stage belongs to a different pipeline than the deal.agent_activity_read_only
This activity is the AI agent’s plan to message the contact again; it can only be completed or canceled.assignee_not_found
Theassignee_id is not a teammate who can take this conversation.
won_reason_without_won
won_reason is only accepted with outcome: won.
unknown_event_code
The workspace has no success event type with this code.GET /v1/success-event-types lists them.
value_basis_mismatch
The event type requires avalue and none was sent, or forbids one and one was sent.
429 Too Many Requests
rate_limit_exceeded
Too many requests on this token. WaitRetry-After seconds before retrying.
500 Internal Server Error
internal_error
Something went wrong on our side. Retry with backoff, and quoteerror.request_id to support if it persists.
Field-level codes
Avalidation_failed error lists each offending field in detail, with one of these codes or a standard validation type such as missing.