Concepts
Errors
The error envelope, request ids, and every error code the API answers.
Every error, from every operation, has the same JSON shape. Branch on error in code, alert on code, show message to people, and quote request_id to support.
{
"error": "lead_not_found",
"message": "No lead with this id exists in this workspace.",
"code": "LEAD_NOT_FOUND_404",
"request_id": "req_4fK9wQz2LmT7xB1sR8vN3cYd",
"doc_url": "https://developers.dmhero.ai/errors#LEAD_NOT_FOUND_404"
}| Field | Meaning |
|---|---|
error | A snake_case discriminator. Stable across releases: the field to branch on. |
message | One English sentence a person can read. It may be reworded; never parse it. |
code | A stable identifier, <DOMAIN>_<KIND>_<STATUS>, never reused for another meaning. Good for alerting and logs. |
request_id | Our id of the request, also in the DMHero-Request-Id header of every response. |
doc_url | A link to this code's row on this page. |
fields | Validation errors only: field path to reason, for example { "ig_handle": "required" }. |
Some errors add fields of their own, named in their row below: claimed_at on an unknown idempotent outcome, quote on a discovery refusal.
Handling errors well
- 4xx means the request cannot succeed as sent. Fix it before retrying, except 409
API_IDEMPOTENCY_IN_PROGRESS_409and 429, which are about timing. - 429 is your own limit (the key's or the organization's). 503 is our capacity. Both carry
Retry-After: wait that long, add jitter, then retry. See Rate limits. - 404 answers the same for a resource that does not exist and for one that belongs to another workspace, so an id never confirms that someone else has it.
- 5xx on a write: retry with the same
Idempotency-Key, which either replays the result or runs once. See Idempotency. - Log
request_idwith every error. It is the fastest way for support to find what happened.
Every code
Generated from the API itself: the codes the request pipeline answers for any operation, and every code an operation documents in its contract. Each row is the target of its doc_url.
| Code | Status | error and meaning | Returned by |
|---|---|---|---|
API_CURSOR_INVALID_400 | 400 | cursor_invalid: The cursor was altered, belongs to another list or sort order, or was sent with different filters than the request that produced it. Start again from the first page. | Any operation |
API_IDEMPOTENCY_KEY_REQUIRED_400 | 400 | idempotency_key_required: This operation needs an Idempotency-Key header, because a blind retry could act twice. Send a fresh UUID per logical action. | conversations.drafts.approve, leads.bulk.confirm, leads.exports.create, leads.imports.create |
API_IDEMPOTENCY_KEY_RESERVED_400 | 400 | idempotency_key_reserved: Idempotency keys starting with mcp: are reserved for the MCP server. Use another key. | Any operation |
API_VERSION_UNSUPPORTED_400 | 400 | version_unsupported: The DMHero-Version header names a version this API does not serve. Send a supported date or leave the header out. | Any operation |
BULK_AUTO_CONFIRM_NOT_ALLOWED_400 | 400 | bulk_auto_confirm_not_allowed: auto_confirm on a destructive action. | leads.bulk.preview |
BULK_TOO_MANY_400 | 400 | bulk_too_many: The target holds more than 30 000 leads. | leads.bulk.preview |
CAMPAIGN_STATUS_VIA_CONTROL_400 | 400 | campaign_status_via_control: The body sets status; use start, pause, resume or archive. | campaigns.update |
COMPARISON_QUERY_INVALID_400 | 400 | comparison_query_invalid: The combination is not comparable (for example templates at sequence scope, or step scope without step_order); reason names the rule. | analytics.ab_comparison |
CONFIRM_REQUIRED_400 | 400 | confirm_required: confirm=true is missing: deleting a field strips its value from every lead. | campaigns.archive, campaigns.delete, custom_fields.delete |
CUSTOM_FIELD_UNKNOWN_400 | 400 | custom_field_unknown: A cf.<key> condition names a key this workspace has not defined. | campaign_leads.upsert_enroll, enrollments.create, leads.bulk.preview, leads.exports.create, leads.list, leads.update, leads.upsert |
DISCOVERY_CREDITS_400 | 400 | invalid_credit_amount: The token ledger refused the amount to reserve; nothing was reserved. | discovery.runs.create |
DISCOVERY_RECIPE_400 | 400 | recipe_invalid: The recipe document does not validate; error names the first problem. | discovery.quote, discovery.recipes.create, discovery.runs.create |
DISCOVERY_RECIPE_NAME_400 | 400 | recipe_name_invalid: The recipe name is empty or longer than 200 characters. | discovery.recipes.create, discovery.runs.create |
ENROLL_BATCH_TOO_LARGE_400 | 400 | enroll_batch_too_large: More than 100 lead ids. | enrollments.create |
ENROLL_CONFIRM_REQUIRED_400 | 400 | enroll_confirm_required: A filter enrollment on an active campaign without a valid confirm_token from a dry run with the same parameters. | enrollments.create |
EXPORT_TOO_MANY_ROWS_400 | 400 | export_too_many_rows: The filter selects more than 30 000 leads. Narrow it. | leads.exports.create |
IMPORT_CSV_COLUMN_UNSUPPORTED_400 | 400 | import_csv_column_unsupported: A CSV header names no lead field; the column is named in the body. | leads.imports.create |
IMPORT_CSV_INVALID_400 | 400 | import_csv_invalid: The CSV cannot be read whole, or has no Instagram handle column. | leads.imports.create |
IMPORT_EMPTY_400 | 400 | import_empty: The import holds no rows, or both or neither of leads and a CSV body. | leads.imports.create |
IMPORT_TOO_MANY_ROWS_400 | 400 | import_too_many_rows: The import holds more rows than one job takes (30 000; 5 000 for a new organization). | leads.imports.create |
LEADS_BATCH_TOO_LARGE_400 | 400 | batch_too_large: An upsert batch carried more than 100 leads. Split it, or use an import job for large volumes. | leads.upsert |
MCP_PARSE_ERROR_400 | 400 | parse_error: The MCP request body is not valid JSON-RPC. The client receives the JSON-RPC parse error the MCP transport specifies; this code names it in the key's request log. | The MCP endpoint |
TAG_INVALID_400 | 400 | tag_invalid: A tag is empty or longer than the tag limit. | leads.bulk.preview, leads.imports.create, leads.tags.add, leads.tags.remove |
TEMPLATE_SPINTAX_INVALID_400 | 400 | template_spintax_invalid: A {{option|option}} group in the content does not parse (unclosed, nested or with an empty option). | templates.create, templates.spintax.propose, templates.update |
VALIDATION_FAILED_400 | 400 | validation_failed: A parameter or body field is missing or invalid. fields maps each field path to the reason. Fix the request; retrying it unchanged fails again. Every operation, the five original Clay endpoints included, refuses a string or object key holding U+0000 (the NUL character) or an unpaired UTF-16 surrogate with the reason text_invalid, because it cannot be stored as sent; nothing is written. | reps.excluded.add, reps.setter_mode.set, setter.knowledge.create, setter.knowledge.update, setter.persona.get, setter.persona.set, setter.test, setter.variants.create, setter.variants.start_test, setter.variants.update, templates.create, templates.spintax.propose, templates.update |
VALIDATION_INVALID_JSON_400 | 400 | invalid_json: The body is not a JSON object (or not valid JSON at all). Send Content-Type: application/json with an object body. | Any operation |
WEBHOOK_REP_FILTER_REQUIRED_400 | 400 | webhook_rep_filter_required: A rep-restricted key must filter the endpoint to a non-empty subset of its reps (ig_account_ids). | webhooks.endpoints.create, webhooks.endpoints.update |
WEBHOOK_URL_INVALID_400 | 400 | webhook_url_invalid: The url is not an https URL on the public internet (reason says why), or it points at DM Hero itself (own_host). | webhooks.endpoints.create, webhooks.endpoints.update |
API_KEY_INVALID_401 | 401 | invalid_api_key: The key is missing, malformed, unknown, revoked or expired, or the request came from an address outside the key's IP allowlist. Every case answers the same body on purpose; the key's request log in DM Hero shows the real reason to its owner. | meta.openapi |
DISCOVERY_CEILING_402 | 402 | period_limit_reached: The organization reached its spend ceiling for the period; the body carries the quote. | discovery.runs.create |
DISCOVERY_CREDITS_402 | 402 | insufficient_credits: The organization holds fewer tokens than the run needs; the body carries the quote. | discovery.runs.create |
SUBSCRIPTION_REQUIRED_402 | 402 | subscription_required: The organization has no active subscription. The API works again as soon as billing is in order. | Any operation |
API_ACCESS_NOT_INCLUDED_403 | 403 | api_access_not_included: This operation is not included in your organization's API access. Adding scopes does not help; the message says what the operation needs. Contact the DM Hero team to extend your access: see Extended access. | Any operation |
API_IP_BLOCKED_403 | 403 | ip_blocked: Too many failed authentications came from this address, so it is blocked for a few minutes. Requests with a valid key are never blocked. | Any operation |
API_KEY_SURFACE_403 | 403 | api_key_surface: A Clay compatible (integrations) key called an operation outside the five original endpoints. Create a developer key. | meta.openapi |
API_ORG_DISABLED_403 | 403 | api_org_disabled: API access was switched off for this organization by DM Hero staff. Contact support. | meta.openapi |
API_REP_RESTRICTED_403 | 403 | rep_restricted: A key restricted to reps asked for workspace-level numbers, which include other reps' traffic. Pass one of the key's reps as rep_id. | analytics.ab_comparison, analytics.funnel, analytics.overview |
API_SCOPE_MISSING_403 | 403 | scope_missing: The key lacks a scope this operation needs, or its creator's current role may no longer grant it. Create a key with the scope; see Authentication for who may grant what. | Any operation |
API_SETTER_NOT_IN_PLAN_403 | 403 | setter_not_in_plan: The organization's plan does not include the AI Setter. Its operations are refused, its fields on other objects read null, and its webhook events and MCP tools are not offered. Change the plan in the app. | conversations.drafts.approve, conversations.drafts.dismiss, conversations.drafts.list, conversations.setter.hand, conversations.setter.resume, setter.variants.archive, setter.variants.create, setter.variants.get, setter.variants.list, setter.variants.promote, setter.variants.publish, setter.variants.start_test, setter.variants.update, setter.variants.versions.list |
DISCOVERY_NOT_ENABLED_403 | 403 | discovery_not_enabled: Lead discovery is not enabled for this organization. | Discovery operations |
GOOGLE_CALENDAR_NOT_ENABLED_403 | 403 | google_calendar_not_enabled: Booking in Google Calendar is not enabled for this organization, and the call would move the variant onto goal_tool google_calendar or change the booking rules of a variant on it. | setter.booking.preview, setter.test, setter.variants.create, setter.variants.update |
INTEGRATIONS_NOT_ENABLED_403 | 403 | integrations_not_enabled: A Clay compatible (integrations) key called one of the five original endpoints, and integrations are not enabled for its organization. A developer key is never refused this way: create one in the Developer section of the settings. | The five original endpoints, to a Clay compatible (integrations) key |
JOB_CONFIRM_TOKEN_INVALID_403 | 403 | job_confirm_token_invalid: The confirm_token does not match the one the bulk preview returned. | leads.bulk.confirm |
MCP_ORIGIN_FORBIDDEN_403 | 403 | origin_forbidden: The MCP endpoint refuses requests that carry a browser Origin. Connect from a server-side MCP client. | The MCP endpoint |
ONBOARDING_REQUIRED_403 | 403 | onboarding_required: The organization behind the key has not finished setting up its DM Hero account. Finish setting up your DM Hero account in the app, then retry. | Any operation |
ORGANIZATION_SUSPENDED_403 | 403 | organization_suspended: The organization is suspended. | Any operation |
WEBHOOK_TIER_NOT_INCLUDED_403 | 403 | webhook_tier_not_included: An event type the request adds, or a setting it switches on, is not included in the organization's API access; the message says what it needs. Removing event types, or changing the url or the auth header of an endpoint that still lists one, is always allowed. Contact the DM Hero team to extend the access. | webhooks.endpoints.create, webhooks.endpoints.update |
API_NOT_FOUND_404 | 404 | not_found: No endpoint exists at this path. Check the method and the path against the API reference. | Any operation |
CAMPAIGN_NOT_FOUND_404 | 404 | campaign_not_found: No campaign with this id exists in the key's workspace. | campaign_leads.upsert_enroll, campaigns.archive, campaigns.clone, campaigns.delete, campaigns.get, campaigns.pause, campaigns.resume, campaigns.start, campaigns.stats, campaigns.steps.replace, campaigns.update, enrollments.create, enrollments.list, enrollments.remove, leads.bulk.preview, leads.exports.create, leads.list |
CONVERSATION_NOT_FOUND_404 | 404 | conversation_not_found: No conversation with this id is visible to this key: missing, in another workspace, or on a rep the key is not restricted to. | conversations.archive, conversations.drafts.approve, conversations.drafts.dismiss, conversations.drafts.list, conversations.get, conversations.mark_read, conversations.setter.hand, conversations.setter.resume, conversations.star, conversations.unarchive, conversations.unstar |
CUSTOM_FIELD_NOT_FOUND_404 | 404 | custom_field_not_found: No custom field with this key exists in this workspace. | custom_fields.delete, custom_fields.update |
DISCOVERY_CANDIDATES_NOT_FOUND_404 | 404 | candidates_not_found: Some of the candidate ids are not results of this run; nothing was moved. | discovery.candidates.promote |
DISCOVERY_CREDITS_404 | 404 | unknown_scope: The token ledger has no wallet for the organization. | discovery.balance, discovery.quote, discovery.runs.create |
DISCOVERY_ICP_PROFILE_404 | 404 | icp_profile_not_found: The recipe's qualify step names a customer profile version that is missing, archived or another organization's. | discovery.recipes.create, discovery.runs.create |
DISCOVERY_ORGANIZATION_NOT_FOUND_404 | 404 | organization_not_found: The key's creator is no longer a member of the organization. | discovery.balance, discovery.handles.resolve, discovery.icp_profiles.list, discovery.quote, discovery.recipes.create |
DISCOVERY_RECIPE_404 | 404 | unknown_recipe: The recipe does not exist in this organization. | discovery.runs.create |
DISCOVERY_RUN_NOT_FOUND_404 | 404 | run_not_found: The run does not exist in this workspace (the same answer for another workspace's run). | discovery.candidates.list, discovery.candidates.promote, discovery.runs.cancel, discovery.runs.get, discovery.runs.preview_decision |
DISCOVERY_WORKSPACE_NOT_FOUND_404 | 404 | workspace_not_found: The key's creator is no longer a member of the workspace's organization. | discovery.runs.create, discovery.runs.list |
ENROLLMENT_NOT_FOUND_404 | 404 | enrollment_not_found: No enrollment with this id exists in the key's workspace, or it belongs to a rep the key is not restricted to. | enrollments.get, enrollments.pause, enrollments.resume |
EXCLUDED_HANDLE_NOT_FOUND_404 | 404 | excluded_handle_not_found: The handle is not on this rep's exclusion list. | reps.excluded.remove |
FOLDER_NOT_FOUND_404 | 404 | folder_not_found: No folder with this id exists in this workspace. | discovery.candidates.promote, folders.delete, folders.update, leads.bulk.preview, leads.update |
JOB_NOT_FOUND_404 | 404 | job_not_found: No job with this id exists in the key's workspace. | jobs.cancel, jobs.get, jobs.result, leads.bulk.confirm |
JOB_RESULT_NOT_FOUND_404 | 404 | job_result_not_found: The job is not an export, or ended without a file. | jobs.result |
KNOWLEDGE_NOT_FOUND_404 | 404 | knowledge_not_found: The knowledge entry does not exist, or belongs to a rep outside the key's reps. | setter.knowledge.delete, setter.knowledge.update |
LEAD_NOT_FOUND_404 | 404 | lead_not_found: No lead with this id or handle exists in the key's workspace. A lead of another workspace answers the same. | campaign_leads.upsert_enroll, enrollments.create, enrollments.remove, leads.bulk.preview, leads.delete, leads.enrollments.list, leads.get, leads.lookup, leads.tags.add, leads.tags.remove, leads.update |
REP_NOT_FOUND_404 | 404 | rep_not_found: No rep with this id or username is connected to the workspace, or the key is restricted to other reps. | analytics.ab_comparison, analytics.overview, analytics.setter, campaign_leads.upsert_enroll, conversations.list, enrollments.create, enrollments.list, leads.bulk.preview, leads.update, reps.excluded.add, reps.excluded.list, reps.excluded.remove, reps.get, reps.setter_mode.get, reps.setter_mode.set, setter.knowledge.create, setter.knowledge.list, setter.persona.get, setter.persona.set, setter.test, setter.variants.create, setter.variants.list |
SETTER_DRAFT_NOT_FOUND_404 | 404 | draft_not_found: No draft with this id belongs to this conversation on a rep the key may see. | conversations.drafts.approve, conversations.drafts.dismiss |
SETTER_VARIANT_NOT_FOUND_404 | 404 | setter_variant_not_found: The variant does not exist, or its rep is not in this workspace or is outside the key's rep restriction. | setter.booking.preview, setter.variants.archive, setter.variants.get, setter.variants.promote, setter.variants.publish, setter.variants.start_test, setter.variants.update, setter.variants.versions.list |
SUPPRESSION_NOT_FOUND_404 | 404 | suppression_not_found: No suppressed lead with this handle in this workspace. | suppressions.remove |
TEMPLATE_NOT_FOUND_404 | 404 | template_not_found: No template with this id exists in the key's workspace. | campaigns.create, campaigns.steps.replace, templates.delete, templates.get, templates.stats, templates.update, templates.versions.list |
WEBHOOK_DELIVERY_NOT_FOUND_404 | 404 | webhook_delivery_not_found: No delivery with this id exists for an endpoint this key can see. | webhooks.deliveries.redeliver |
WEBHOOK_ENDPOINT_NOT_FOUND_404 | 404 | webhook_endpoint_not_found: No webhook endpoint with this id exists in this workspace (or, for a rep-restricted key, within its reps). | webhooks.deliveries.list, webhooks.endpoints.delete, webhooks.endpoints.get, webhooks.endpoints.rotate_secret, webhooks.endpoints.test, webhooks.endpoints.update |
API_METHOD_NOT_ALLOWED_405 | 405 | method_not_allowed: The path exists but not with this method. The Allow header lists the methods it takes. | Any operation |
API_IDEMPOTENCY_IN_PROGRESS_409 | 409 | idempotency_in_progress: A request with the same Idempotency-Key is still running. Wait and retry with the same key to receive its result. | Any operation |
API_IDEMPOTENCY_OUTCOME_UNKNOWN_409 | 409 | idempotency_outcome_unknown: An earlier request with this key may have acted, and we cannot tell. The body carries claimed_at. Check the resource (see Idempotency), then retry with a NEW key only if nothing happened. | Any operation |
BULK_COUNT_CHANGED_409 | 409 | bulk_count_changed: expected_count differs from the preview's matched_count. | leads.bulk.confirm |
CALENDAR_INVALID_EMAIL_409 | 409 | calendar_invalid_email: The draft books a meeting and the prospect's email address cannot receive an invitation. Nothing was sent. | conversations.drafts.approve |
CALENDAR_NOT_CONNECTED_409 | 409 | calendar_not_connected: goal_tool is google_calendar and the workspace has no healthy Google Calendar connection. | setter.test |
CALENDAR_SLOT_TAKEN_409 | 409 | calendar_slot_taken: The draft books a time that is no longer free in the calendar. Nothing was sent. | conversations.drafts.approve |
CALENDAR_UNAVAILABLE_409 | 409 | calendar_unavailable: The draft books a meeting and Google Calendar is not connected or lost access. Nothing was sent. | conversations.drafts.approve |
CAMPAIGN_ARCHIVED_409 | 409 | campaign_archived: The campaign is archived and takes no new enrollments. | campaign_leads.upsert_enroll, enrollments.create |
CAMPAIGN_NO_STEPS_409 | 409 | campaign_no_steps: The campaign has no steps yet. | campaigns.start |
CAMPAIGN_STATUS_CONFLICT_409 | 409 | campaign_status_conflict: The campaign is not in a status this transition starts from, or its status changed meanwhile. | campaigns.pause, campaigns.resume, campaigns.start |
CONVERSATION_WINDOW_CLOSED_409 | 409 | conversation_window_closed: More than 24 hours passed since the lead's last message, or the lead never wrote. The body carries reply_window_closes_at. | conversations.drafts.approve |
CUSTOM_FIELD_KEY_TAKEN_409 | 409 | custom_field_key_taken: A custom field with this key already exists. | custom_fields.create |
CUSTOM_FIELD_LIMIT_409 | 409 | custom_field_limit: The workspace already has its 5 custom fields. | custom_fields.create |
DISCOVERY_CANDIDATE_NOT_PROMOTABLE_409 | 409 | candidate_not_promotable: One of the results has no usable Instagram handle; nothing was moved. | discovery.candidates.promote |
DISCOVERY_CEILING_409 | 409 | ceiling_unavailable: The organization's spend ceiling could not be read; nothing was priced or reserved. | discovery.quote, discovery.runs.create |
DISCOVERY_HANDLES_REPLAY_409 | 409 | idempotency_replay_unavailable: A repeated Idempotency-Key whose first answer could not be stored; send the request again with a new key. | discovery.handles.resolve |
DISCOVERY_PREVIEW_NOT_PENDING_409 | 409 | preview_not_pending: The run is not waiting for a preview decision (it was answered, or it moved on). | discovery.runs.preview_decision |
DISCOVERY_QUALIFY_UNAVAILABLE_409 | 409 | qualify_unavailable: The recipe judges accounts against a customer profile, which is not available to this organization right now; the body carries the quote. | discovery.runs.create |
DISCOVERY_RUN_STALE_409 | 409 | run_stale: The run finished too long ago for its results to be trusted; run the search again. | discovery.candidates.promote |
ENROLLMENT_STATUS_CONFLICT_409 | 409 | enrollment_status_conflict: The enrollment is not in a status this transition starts from, or its status changed meanwhile. | enrollments.pause, enrollments.resume |
FOLDER_NAME_TAKEN_409 | 409 | folder_name_taken: A folder with this name already exists in the workspace. | folders.create, folders.update |
JOB_CONFIRMATION_EXPIRED_409 | 409 | job_confirmation_expired: The bulk preview's 15 minute confirmation window has passed. Run the preview again. | leads.bulk.confirm |
JOB_NOT_AWAITING_CONFIRMATION_409 | 409 | job_not_awaiting_confirmation: The job is not a bulk preview waiting for confirmation (it was confirmed, cancelled or expired). | leads.bulk.confirm |
JOB_NOT_CANCELLABLE_409 | 409 | job_not_cancellable: The job has already ended. | jobs.cancel |
JOB_QUEUE_FULL_409 | 409 | job_queue_full: The organization already has three live jobs (queued, running or awaiting confirmation). Wait for one to finish. | leads.bulk.preview, leads.exports.create, leads.imports.create |
JOB_RESULT_NOT_READY_409 | 409 | job_result_not_ready: The export has not finished yet. | jobs.result |
LEAD_AMBIGUOUS_409 | 409 | lead_ambiguous: More than one lead matches the handle or external_id. Resolve the duplicates in DM Hero or address the lead by id. | campaign_leads.upsert_enroll, leads.lookup, leads.upsert |
LEAD_EXISTS_409 | 409 | lead_exists: on_conflict is error and the lead already exists. | leads.upsert |
REP_ARCHIVED_409 | 409 | rep_archived: The rep is archived. Nothing was sent. | conversations.drafts.approve |
REP_NONE_AVAILABLE_409 | 409 | no_rep_available: The key reaches no connected rep to assign the enrollment to. | campaign_leads.upsert_enroll |
REP_NOT_CONNECTED_409 | 409 | rep_not_connected: The rep has no usable Instagram connection. Nothing was sent. | conversations.drafts.approve |
SETTER_CAMPAIGN_ACTIVE_409 | 409 | campaign_active: The lead is still in an active campaign sequence; the AI Setter takes over by itself when they reply. | conversations.setter.hand, conversations.setter.resume |
SETTER_DISABLED_409 | 409 | setter_disabled: The AI Setter is off for this thread's channel (inbound or outbound) on its rep. | conversations.setter.hand, conversations.setter.resume |
SETTER_DRAFT_HANDLED_409 | 409 | draft_already_handled: The draft was already approved or dismissed, here or in the DM Hero inbox, or a newer draft replaced it. List the drafts again. | conversations.drafts.approve, conversations.drafts.dismiss |
SETTER_DRAFT_IN_PROGRESS_409 | 409 | draft_send_in_progress: Another approval of this draft is being sent right now. | conversations.drafts.approve |
SETTER_DRAFT_UNSENDABLE_409 | 409 | draft_unsendable: The draft cannot be sent as it is (too long for Instagram). Dismiss it, or edit it in the DM Hero inbox. | conversations.drafts.approve |
SETTER_HANDLE_EXCLUDED_409 | 409 | handle_excluded: The thread has no lead and its participant is on the rep's excluded accounts. | conversations.setter.hand, conversations.setter.resume |
SETTER_LEAD_OPTED_OUT_409 | 409 | lead_opted_out: The thread's lead is marked not interested, so the AI Setter does not write to them. | conversations.setter.hand, conversations.setter.resume |
SETTER_VARIANT_LIVE_409 | 409 | setter_variant_live: The variant is live. Publish another variant, or switch the rep's setter off, before archiving it. | setter.variants.archive |
WEBHOOK_DELIVERY_IN_FLIGHT_409 | 409 | webhook_delivery_in_flight: The delivery is still pending or being sent; only a finished one can be redelivered. | webhooks.deliveries.redeliver |
WEBHOOK_ENDPOINT_INACTIVE_409 | 409 | webhook_endpoint_inactive: The endpoint is disabled; enable it first. | webhooks.deliveries.redeliver, webhooks.endpoints.test |
WEBHOOK_ENDPOINT_LIMIT_409 | 409 | webhook_endpoint_limit: The workspace already has 15 endpoints. | webhooks.endpoints.create |
WEBHOOK_SECRET_IN_GRACE_409 | 409 | webhook_secret_in_grace: The previous secret still signs for 24 hours after a rotation; rotatable_at says when the next one is possible. | webhooks.endpoints.rotate_secret |
WEBHOOK_TEST_PENDING_409 | 409 | webhook_test_pending: A test event for this endpoint is still queued or being sent. | webhooks.endpoints.test |
JOB_RESULT_EXPIRED_410 | 410 | job_result_expired: The file was deleted 24 hours after the job finished. | jobs.result |
LEAD_PRECONDITION_FAILED_412 | 412 | lead_precondition_failed: If-Match does not match: the lead changed since you read it. | leads.update |
API_PAYLOAD_TOO_LARGE_413 | 413 | payload_too_large: The body is larger than this operation accepts (1 MiB by default, 15 MiB for imports, 2 MiB for bulk previews). A job whose payload is still over 20 MiB after compression answers the same code with error: job_payload_too_large. | Any operation |
JOB_PAYLOAD_QUOTA_413 | 413 | job_payload_quota: The organization's daily volume of job payloads (100 MiB) is used up. It is counted over a rolling 24 hours. | leads.bulk.preview, leads.imports.create |
API_URI_TOO_LONG_414 | 414 | uri_too_long: The query string is longer than 4 KiB. Move filters into fewer, shorter parameters. | Any operation |
API_IDEMPOTENCY_MISMATCH_422 | 422 | idempotency_mismatch: This Idempotency-Key was already used for a different request (other body, path, operation, or a key with other reps). Use a new key for a new request. | Any operation |
TEMPLATE_SPINTAX_EMPTY_422 | 422 | spintax_no_suggestions: Nothing in the body can be safely varied. | templates.spintax.propose |
TEMPLATE_SPINTAX_REFUSED_422 | 422 | spintax_refused: The model declined to vary this body. | templates.spintax.propose |
PENDING_ARCHIVE_SELECTION_423 | 423 | pending_archive_selection: The organization moved to a smaller plan and must choose which workspaces to keep, in the app, before the API answers again. | Any operation |
API_IP_RATE_LIMITED_429 | 429 | ip_rate_limited: Too many requests without a valid key came from this address. Authenticated requests are not counted here. | Any operation |
API_RATE_LIMITED_429 | 429 | rate_limited: The key's or the organization's rate limit for this class of operation is spent. Wait Retry-After seconds. | meta.openapi |
CONVERSATION_RATE_LIMITED_429 | 429 | conversation_rate_limited: More than 10 replies were sent to this conversation in the last hour. | conversations.drafts.approve |
DISCOVERY_QUOTE_DAILY_LIMIT_429 | 429 | discovery_quote_daily_limit: The organization's 1,000 discovery quotes for the day are used up. It is counted over a rolling 24 hours. | discovery.quote, discovery.runs.create |
LEADS_DAILY_LIMIT_429 | 429 | leads_daily_limit: The organization created its daily maximum of leads through the API (100,000). It is counted over a rolling 24 hours. | campaign_leads.upsert_enroll, discovery.candidates.promote, leads.imports.create, leads.upsert, suppressions.add |
SETTER_TEST_RATE_LIMITED_429 | 429 | rate_limited: More than 30 Test Chat calls in a minute for this key (rotations share the count). | setter.test |
TEMPLATE_SPINTAX_RATE_LIMITED_429 | 429 | rate_limited: More than ten proposals in a minute for the key's creator, counted together with the app. | templates.spintax.propose |
API_INTERNAL_500 | 500 | internal_error: Something failed on our side. Retrying is safe with the same Idempotency-Key. Quote the request_id if you contact support. | Any operation |
LEAD_UPSERT_FAILED_500 | 500 | upsert_failed: Reported per item inside a batch upsert: that one lead was not written, the others were. Retry the failed indexes only. | Lead upserts (per item) |
CALENDAR_ERROR_502 | 502 | calendar_error: Google Calendar did not create the event the draft books. Nothing was sent; the Idempotency-Key may be reused. | conversations.drafts.approve |
CONVERSATION_SEND_FAILED_502 | 502 | conversation_send_failed: Instagram refused the message; nothing was sent, the draft stays pending and the Idempotency-Key may be reused. | conversations.drafts.approve |
CONVERSATION_SEND_OUTCOME_UNKNOWN_502 | 502 | conversation_send_outcome_unknown: The connection to Instagram failed mid-send, so the message may have been delivered. The draft is marked as handled and cannot be approved again; any booking it carried stands. | conversations.drafts.approve |
SETTER_TEST_FAILED_502 | 502 | setter_test_failed: The reply could not be generated: a model, calendar or storage call failed while answering the turn. Retry later. | setter.test |
TEMPLATE_SPINTAX_FAILED_502 | 502 | spintax_failed: The model call failed, ran past 110 seconds, or its answer could not be read. Retry later. | templates.spintax.propose |
API_DISABLED_503 | 503 | api_disabled: The developer API is switched off for maintenance. The five original endpoints keep working. | meta.openapi |
API_OVERLOADED_503 | 503 | overloaded: Our capacity, not your limit: the fleet ceiling, a concurrency slot or load shedding refused the request. Wait Retry-After seconds, with jitter. | Any operation |
API_SEND_DISABLED_503 | 503 | api_send_disabled: Sending through the API is switched off for now. | conversations.drafts.approve |
DISCOVERY_HANDLES_PROVIDER_503 | 503 | resolver_unavailable: No account lookup is possible right now. Retry later. | discovery.handles.resolve |
WEBHOOK_TEST_NOT_ENQUEUED_503 | 503 | webhook_test_not_enqueued: The test event could not be queued (outbound webhooks are switched off). Retry later. | webhooks.endpoints.test |
API_TIMEOUT_504 | 504 | timeout: A read did not finish within its time budget. Narrow the request or retry later. Writes are never abandoned this way. | Any operation |
Shared errors
Every authenticated operation can answer the codes marked Any operation above: the key and scope checks (401, 403), the account checks (402, 403, 423), rate limits (429), validation (400), size limits (413, 414), idempotency (409, 422) and capacity (503, 504). The reference lists only the codes specific to each operation.
Job failure codes
An import, export or bulk job that fails carries error: { code, message }. These codes are not HTTP statuses; they say why the job stopped.
| Code | Meaning |
|---|---|
JOB_KEY_REVOKED | The key that created the job was revoked (or found leaked) before the job finished. |
JOB_KEY_GONE | The key that created the job no longer exists. |
JOB_ORG_DISABLED | API access was switched off for the organization while the job was waiting or running. |
JOB_DEVELOPER_API_OFF | The developer API was switched off while the job was running. |
JOB_ACCOUNT_NOT_READY | The organization's account stopped being usable (subscription, suspension or a pending plan choice). |
JOB_SCOPE_LOST | The key's creator no longer has the role the job's scopes need. |
JOB_HANDLER_MISSING | No worker exists for this job kind. Contact support with the job id. |
JOB_FAILED | The job failed three times in a row and was stopped. Work already done stays done and is counted in result. |