Developer docs
API error reference
Status codes, typed error codes, retry handling, and Idempotency-Key behavior.
Status handling
| Status | Meaning | Client action |
|---|---|---|
| 200 | Read succeeded | Continue. |
| 201 | Resource created | Store the returned id and any one-time secret immediately. |
| 202 | Message accepted | Poll message logs or subscribe to status webhooks. |
| 401 | Missing auth context | Attach tenant, user, and role headers or valid API credentials. |
| 403 | Role or MFA required | Ask an owner/operator to perform the privileged action. |
| 404 | Tenant-scoped resource missing | Treat as not found; do not retry without new input. |
| 409 | State conflict | For idempotency conflicts, fix the request body or use a new key. |
| 422 | Validation failed | Fix the request shape before retrying. |
| 423 | Sending halted | Stop sends; review suspension status and appeal/export paths. |
| 429 | Rate or sending limit | Back off and retry after capacity resets. |
| 500 | Unexpected server error | Retry with jitter; escalate if repeated. |
Typed errors
| Code | Status | Why | Fix |
|---|---|---|---|
permission_denied | 403 | Your role is Viewer, which is read-only. | Ask an owner or admin for the Developer role or higher. Open |
api_key_environment_mismatch | 403 | This API key belongs to a different project or mode than the one you used. | Create a key in the same project and mode, then use it. Open |
api_key_project_mismatch | 403 | This API key belongs to a different project or mode than the one you used. | Create a key in the same project and mode, then use it. Open |
api_key_environment_project_mismatch | 422 | This API key belongs to a different project or mode than the one you used. | Create a key in the same project and mode, then use it. Open |
domain_not_verified | 403 | The From address is not on a domain you have verified. | Verify the domain, or confirm this single sender address, then send again. Open |
pre_send_vetting_required | 428 | Production sending starts after your account passes a short review. | Finish the review, or contact support if it is stuck. Open |
sending_halted | 423 | Sending is paused for this workspace. | Read the reason shown, then contact support to appeal. Open |
subscription_restricted | 402 | Your subscription state blocks new sends. Exports stay available. | Fix billing to resume sending. Open |
subscription_suspended | 402 | Your subscription state blocks new sends. Exports stay available. | Fix billing to resume sending. Open |
subscription_canceled | 402 | Your subscription state blocks new sends. Exports stay available. | Fix billing to resume sending. Open |
subscription_quarantined | 402 | Your subscription state blocks new sends. Exports stay available. | Fix billing to resume sending. Open |
plan_monthly_limit_exceeded | 402 | Your plan's monthly email or active contact limit is used up. | Upgrade your plan or wait for the next period. Open |
plan_contact_limit_exceeded | 402 | Your plan's monthly email or active contact limit is used up. | Upgrade your plan or wait for the next period. Open |
hard_spend_cap_exceeded | 429 | Your daily sending cap is reached. | Retry after the daily reset or review your limits. Open |
sending_limit_exceeded | 429 | Your daily sending cap is reached. | Retry after the daily reset or review your limits. Open |
recipient_unsubscribed | 403 | This recipient opted out of this kind of email. | Do not resend to this address in this category. Open |
recipient_suppressed | 422 | A recipient is suppressed or was deleted. | Remove that recipient from the send. Open |
recipient_deleted | 422 | A recipient is suppressed or was deleted. | Remove that recipient from the send. Open |
recipient_suppressed_by_dsr | 422 | A recipient is suppressed or was deleted. | Remove that recipient from the send. Open |
consent_required | 422 | Marketing mail needs recipient consent. Set mailing address under /compliance. | Save your mailing address on /compliance, then record consent for recipients. Open |
physical_address_required | 422 | Marketing mail needs recipient consent. Set mailing address under /compliance. | Save your mailing address on /compliance, then record consent for recipients. Open |
can_spam_footer_required | 422 | Marketing mail needs recipient consent. Set mailing address under /compliance. | Save your mailing address on /compliance, then record consent for recipients. Open |
owner_required | 403 | This action is limited to workspace owners. | Ask a workspace owner. Open |
operator_required | 403 | This is limited to platform operators. | Contact support if you need this changed. Open |
workspace_membership_not_found | 404 | That member no longer exists in this workspace. | Refresh the team list. Open |
team_invitation_not_found | 404 | That invitation was revoked, used or expired. | Refresh the list, or send a new invitation. Open |
team_invitation_identity_mismatch | 403 | This invitation was sent to a different email address. | Sign in with the invited address. Open |
mfa_code_invalid | 400 | The code was wrong or expired. | Use the current code from your authenticator and try again. |
invalid_mfa | 401 | The code was wrong or expired. | Use the current code from your authenticator and try again. |
mfa_required | 403 | This action needs MFA. | Verify with your authenticator and retry, or set up MFA first. Open |
mfa_step_up_required | 403 | This action needs a recent MFA check. | Confirm with your authenticator, then retry. Open |
stream_archived | 409 | The selected stream cannot be used for this action. | Choose or update the stream and try again. Open |
stream_category_mismatch | 422 | The selected stream cannot be used for this action. | Choose or update the stream and try again. Open |
stream_category_type_mismatch | 422 | The selected stream cannot be used for this action. | Choose or update the stream and try again. Open |
stream_environment_mismatch | 422 | The selected stream cannot be used for this action. | Choose or update the stream and try again. Open |
stream_in_use | 409 | The selected stream cannot be used for this action. | Choose or update the stream and try again. Open |
stream_key_exists | 409 | The selected stream cannot be used for this action. | Choose or update the stream and try again. Open |
stream_not_found | 404 | The selected stream cannot be used for this action. | Choose or update the stream and try again. Open |
stream_patch_empty | 422 | The selected stream cannot be used for this action. | Choose or update the stream and try again. Open |
project_scope_mismatch | 403 | The selected stream cannot be used for this action. | Choose or update the stream and try again. Open |
batch_stream_mismatch | 422 | The selected stream cannot be used for this action. | Choose or update the stream and try again. Open |
export_request_expired | 410 | This export is unavailable now. | Request a new export or wait until it is ready. Open |
export_request_not_found | 404 | This export is unavailable now. | Request a new export or wait until it is ready. Open |
export_request_not_ready | 409 | This export is unavailable now. | Request a new export or wait until it is ready. Open |
send_pipeline_aggregate_scope_required | 403 | This operator action cannot continue. | Review the request and retry. Open |
tenant_purge_ownership_lost | 409 | This operator action cannot continue. | Review the request and retry. Open |
comp_idempotency_key_invalid | 422 | This operator action cannot continue. | Review the request and retry. Open |
comp_override_expiry_in_past | 422 | This operator action cannot continue. | Review the request and retry. Open |
durable_comp_store_required | 409 | This operator action cannot continue. | Review the request and retry. Open |
api_key_scope_missing | 403 | This API key lacks permission for this action. | Create a key with the required scope. |
billing_tenant_scope_denied | 403 | This billing target is outside your access. | Choose a permitted workspace. |
comp_free_plan_invalid | 422 | This compensation cannot use the Free plan. | Choose an eligible plan. |
comp_override_fields_required | 422 | This compensation request is incomplete. | Fill the required fields. |
comp_override_idempotency_conflict | 409 | This compensation request conflicts with an earlier one. | Use a new request key for changed details. |
consumer_secret_missing | 503 | This service cannot complete the approval. | Contact support. |
duplicate_message | 409 | This message has already been received. | Check the existing message before retrying. |
inbound_event_conflict | 409 | This inbound event conflicts with an earlier event. | Check the event and try again. |
owner_role_required | 403 | Only an owner can change another owner role. | Ask a workspace owner. |
payload_too_large | 413 | This request is too large. | Reduce its size and try again. |
platform_billing_admin_required | 403 | This action is limited to platform billing operators. | Contact support if you need this changed. |
project_not_found | 404 | That project was not found. | Refresh the project list. |
rate_limited | 429 | Too many requests were sent. | Wait and try again. |
stripe_config_not_configured | 503 | Billing setup is unavailable. | Contact support. |
stripe_response_invalid | 502 | Billing could not finish this request. | Try again shortly. |
tenant_not_suspended | 409 | This workspace is not suspended. | Refresh its status. |
privileged_role_required | 403 | This action needs a privileged workspace role. | Ask an owner or admin for access. Open |
project_settings_role_required | 403 | Your role cannot change project settings. | Ask an owner or admin for access. Open |
stats_window_invalid | 422 | The start or end time lacks a time zone, the start is not before the end, or the range exceeds 90 days. | Choose start and end times with time zones, start before end, and keep the range within 90 days. |
stats_window_outside_retention | 422 | The requested range ends at or before the retained period begins. | Choose a newer valid range that overlaps the retained period. |
stream_stats_disabled | 404 | Statistics for an individual stream are not available. | In Analytics, choose All streams to see Workspace totals (all projects). Open |
campaign_not_found | 404 | The requested campaign is not available in this workspace. | Check the campaign ID and workspace, then reload the campaign. |
invalid_limit | 422 | The requested page size must be between 1 and 100. | Choose a limit from 1 to 100 and try again. |
preference_token_not_found | 404 | The preference token could not be found. | Check the link or request a new preference link from the sender. |
recipient_erased | 410 | The recipient has been erased from this workspace. | Do not recreate or change this recipient through scoped suppression or preference actions. |
recipient_gate_unavailable | 503 | A recipient eligibility check could not be completed. | Check whether the send was accepted before retrying; if it was not, try again later. |
recipient_suppressed_global | 403 | A workspace suppression blocks this recipient. | Review the workspace suppression before attempting another send; a stream change will not remove it. |
recipient_suppressed_stream | 403 | An active suppression for this stream blocks the recipient. | Review the stream suppression and recipient preference before attempting another send. |
recipient_webhook_in_flight | 409 | A delivery for this recipient is active while erasure is being processed. | Wait for the active delivery to finish, then verify the erasure result before retrying. |
scoped_preference_only | 422 | A stream preference link cannot change global unsubscribe or topic settings. | Use this link only to resubscribe to the targeted stream; use a separate global preference link for global changes. |
stream_suppression_owner_required | 403 | This action requires an owner or admin dashboard session; an API key cannot perform it. | Sign in with an owner or admin dashboard session to view or manage stream suppressions. |
stream_suppression_unavailable | 503 | The stream suppression service could not complete a required check or token operation. | Check whether the operation was accepted before retrying; if its outcome is unknown, verify its current state first. |
stream_suppression_write_failed | 500 | The suppression write or its related audit and change record did not complete normally. | Reload the suppression list to check its current state before deciding whether to retry. |
stream_suppressions_disabled | 404 | Scoped suppression controls are currently disabled. | Wait until scoped controls are available; this action does not change global preferences or suppressions. |
suppression_not_found | 404 | The suppression ID is not active in this project and stream. | Reload the stream suppression list and check the ID before trying again. |
tenant_erasure_in_progress | 409 | Tenant erasure is in progress or the workspace has already been purged. | Stop this change and check the workspace erasure status; do not recreate recipient data. |
unsubscribe_origin_unavailable | 503 | The hosted unsubscribe origin is unavailable for this send. | Check whether the send was accepted before retrying; contact support if link generation remains unavailable. |
webhook_delivery_erased | 409 | The delivery was erased or its workspace is being purged. | Do not replay the erased delivery; check the workspace erasure status if needed. |
billing_owner_required | 403 | Your workspace role cannot submit billing or refund cases. | Ask a workspace owner or admin to submit the billing or refund request. |
case_idempotency_conflict | 409 | The submission identifier is already attached to different case details. | Review your existing cases. Start a new request only if it is a separate issue. |
case_operator_action_required | 403 | This action requires support operator permissions. | Reply to the case if you need help; ask an authorized support operator to change its status or assignment. |
case_pagination_invalid | 422 | The case list page or page size is outside the allowed range. | Return to the first page. API clients must use a limit from 1 to 100 and an offset from 0 to 10000. |
conversation_pagination_invalid | 422 | The conversation list page or page size is outside the allowed range. | Return to the first page. API clients must use a limit from 1 to 100 and an offset from 0 to 10000. |
customer_session_required | 403 | This action requires a customer account session. | Sign in to the workspace with your customer account; operators must use the operator support queue. |
support_ai_unavailable | 503 | The assistant could not confirm completion of this action. | Refresh the conversation to check for a reply before retrying. Email support if the assistant remains unavailable. |
support_case_access_limit | 429 | Too many case investigations are active for your operator account. | Continue an existing authorized investigation or wait for one to expire before opening another. |
support_case_already_assigned | 409 | The case is already assigned to a different operator. | Refresh the case to check its current assignment and coordinate with the assigned operator. |
support_case_daily_limit | 429 | Your account has reached the case creation limit for the last 24 hours. | Continue an existing case, email support, or wait until case creation capacity becomes available. |
support_case_intake_disabled | 503 | Support is not accepting new cases through this form. | Check whether a case was already created before retrying. Email support while new case intake is paused. |
support_case_investigation_required | 403 | This action requires a current investigation for this operator and case. | Use the operator support queue to open an investigation for this case, then retry the action. |
support_case_message_limit | 409 | No more messages can be added to this case. | Start a new case for further help or email support. Operators should coordinate the next step with the customer. |
support_case_not_found | 404 | The case cannot be accessed in your current support session. | Refresh the case list and select an available case; contact support if you still need help. |
support_case_reopen_expired | 409 | The allowed reopening period for this resolved case has ended. | Start a new case for further help or email support. |
support_case_version_conflict | 409 | Another action updated the case after you loaded it. | Refresh the case and review its current state before resubmitting your action. |
support_controls_unavailable | 503 | Support controls could not be loaded or updated. | Refresh to check the current controls before retrying. Contact an authorized operator if they remain unavailable. |
support_controls_version_conflict | 409 | Another operator changed the support controls after you loaded them. | Refresh the controls and review the current settings before submitting another change. |
support_session_required | 403 | This action requires a signed-in account session. | Sign in with your PulsePigeon account. Operators must also complete any required MFA verification. |
support_tenant_erased | 410 | Support activity is blocked because this workspace's support data was erased. | Stop retrying this action. Email support if you need help with the workspace erasure. |
transactional_content_promotional | 422 | Transactional, security, and billing mail cannot include promotional phrasing. | Send this as category=marketing with real recipient consent, or remove the promotional phrasing. Open |
unsubscribe_link_unavailable | 503 | The unsubscribe link for this marketing send could not be minted. | Retry the send. Contact support if link generation remains unavailable. Open |
marketing_footer_too_large | 422 | The marketing footer would push this HTML body past the allowed size. | Shorten the HTML body and retry the send. Open |
project_archived | 409 | This project is archived, so new sends and dead-letter replay are refused. | Restore the project, then retry the send. Open |
project_lifecycle_busy | 409 | Another archive or restore of this project is already running. | Wait a moment, then retry. Open |
profile_archived | 409 | Archived contacts are hidden and cannot be updated or receive new events until restored. | Restore the contact from the archived list, then retry. Open |
contact_tombstone_unavailable | 503 | A suppression tombstone could not be written, so the contact was not deleted. | Retry the request shortly. |
profile_erased | 409 | A data-subject erasure still fences writers for this address. | Do not recreate this contact while the erasure fence is active. Marketing to the address stays blocked by the suppression tombstone. |
legal_hold_active | 409 | The legal hold pauses erasure of this data. | Release the hold, then retry the erasure. |
legal_hold_not_found | 404 | The requested hold is unavailable. | Check the hold and try again. |
dsr_request_not_found | 404 | The requested privacy request is unavailable. | Check the request and try again. |
dsr_not_deferred | 409 | Only a deferred privacy request can be reviewed. | Check the request status before reviewing it. |
customer_active | 409 | Active customers are protected from erasure. | Mark the contact as a former customer, then retry the erasure. |
approved_draft_not_current | 409 | The selected draft is no longer the current approved version. | Refresh and review the current draft, approve it, then publish. |
auth_token_required | 401 | A signed-in session is required for this request. | Sign in again, then retry the request. |
content_hash_mismatch | 412 | The submitted draft does not match its current content. | Refresh and review the current draft, approve it, then publish. |
content_snapshot_mismatch | 409 | The draft content changed after its snapshot was saved. | Save a new draft, review and approve it, then publish. |
draft_not_current | 409 | A newer draft has replaced the selected draft. | Refresh and review the current draft, then approve it before publishing. |
duplicate_recipient | 422 | Multiple recipient entries resolve to the same mailbox. | Keep one recipient entry per address, then update the draft. |
etag_mismatch | 412 | The submitted version does not match the current configuration. | Reload the current configuration and retry with its current version. |
if_match_required | 428 | This change must be based on the version you last loaded. | Reload the current configuration and retry with its current version. |
invalid_header | 400 | The submitted version header could not be used. | Reload the current configuration and retry with its current version. |
not_found | 404 | The requested item cannot be opened here. | Refresh the current view and select an available item. |
prompt_snapshot_mismatch | 409 | The draft prompt changed after its snapshot was saved. | Save a new draft, review and approve it, then publish. |
prompt_too_large | 422 | The approved business facts and FAQs exceed the prompt size limit. | Shorten the approved business facts and FAQs, then update the draft. |
publication_active | 409 | Another publication request for this configuration is in progress. | Wait for the existing publication request or review its status before retrying. |
receptionist_permission_denied | 403 | Your role does not allow this receptionist action. | Ask an owner or admin for permission or to complete this action. |
receptionist_session_required | 403 | This action requires an eligible signed-in session. | Use a verified interactive owner or admin session with recent MFA verification. |
receptionist_unavailable | 503 | The request could not be completed right now. | Retry later. |
recipient_proof_changed | 422 | The saved recipient confirmation no longer matches the current mailbox. | Verify or reconfirm the owner notification mailbox and update the draft snapshot. |
recipient_proof_invalid | 422 | The saved recipient confirmation cannot be used. | Verify or reconfirm the owner notification mailbox and update the draft snapshot. |
recipient_unverified | 422 | The selected recipient mailbox has not completed confirmation. | Verify or reconfirm the owner notification mailbox and update the draft snapshot. |
invalid_inbound_event | 422 | The event type or inbound event object did not match the expected schema. | Check the provider inbound webhook format and event mapping. |
invalid_inbound_identity | 422 | A required call identifier or phone number did not match the expected format. | Check the provider inbound webhook identity fields and their formats. |
invalid_provider_event | 422 | The event schema or a required event field was invalid. | Review the provider event format and server integration configuration. |
invalid_provider_payload | 400 | The request body could not be parsed as an accepted JSON object. | Check the provider webhook request format and body encoding. |
invalid_provider_signature | 401 | The request signature was missing or did not pass verification. | Check the server provider signing configuration and webhook setup. |
owned_deadline | 503 | The voice setup request did not finish in time. | Try again in a moment. |
owned_key_issuance_denied | 403 | Only a workspace owner or admin can create voice keys for an active project in their own workspace. | Sign in as an owner or admin and choose an active project in your workspace. |
owned_unavailable | 503 | The service could not complete the request right now. | Try again shortly. |
agent_retention_inventory_unavailable | 503 | The service could not confirm which approvals still need cleanup. | Try again shortly. |
approval_content_unavailable | 503 | The approval content is unavailable or no longer retained. | Refresh and try again, or create a new request. |
cap_operation_busy | 503 | Another change to the same allowance is still running. | Try again in a moment. |
cap_reservation_scope_changed | 409 | The workspace or project scope changed during the request. | Reload and try again. |
notice_authority_invalid | 403 | The recipient authority for this account is no longer valid. | Sign in as an owner or admin and try again. |
notice_authority_required | 403 | Changing who is notified needs recipient authority. | Sign in as an owner or admin and try again. |
tenant_completion_audit_conflict | 409 | A completion record for this workspace already exists with different details. | Contact support with the time of the request. |
tenant_purge_in_progress | 409 | A deletion is already running for this workspace. | Wait for it to finish before retrying. |
voice_cap_owner_unavailable | 503 | The service could not confirm who owns the allowance right now. | Try again shortly. |
voice_notice_content_redacted | 409 | The personal content in this notice was redacted under the retention policy. | Create a new request if you still need it. |
voice_notice_content_unavailable | 409 | The notice content cannot be read in its current state. | Refresh and try again. |
voice_notice_execution_unavailable | 409 | Voice notices are delivered by the voice service, not by approving them. | Use the voice setup page to manage notices. |
voice_notice_transaction_incomplete | 409 | The change was not completed and was rolled back. | Try again. |
voice_notice_unavailable | 409 | The notice no longer exists or was already settled. | Refresh the list. |
voice_personal_redaction_held | 409 | A retention hold is blocking the removal. | Try again after the hold is released. |
voice_personal_redaction_unavailable | 503 | The service could not complete the removal right now. | Try again shortly. |
recording_consent_attestation_required | 422 | The recording notice stays on unless an owner or admin confirms your business obtains consent where the law requires it. | Tick the consent confirmation, or turn the recording notice back on. |
recording_notice_disable_forbidden | 403 | Turning it off makes your business responsible for recording consent. | Turn the recording notice back on, or ask an owner or admin to make this change. |
greeting_contradicts_ai_disclosure | 422 | Every call starts by saying the assistant is an AI, and the greeting cannot contradict that. | Remove wording that claims a person is answering, such as saying you are human or not an AI. |
recording_consent_attestation_unexpected | 422 | The recording notice is on, so there is nothing to confirm. | Clear the consent confirmation while the recording notice is on. |
business_name_invalid | 422 | It contains control characters, line breaks or wording that claims a person is answering. | Use a plain business name on one line. |
faq_identity_answer_must_disclose_ai | 422 | Callers must receive a clear AI identity answer when they ask who or what is speaking. | Answer identity questions by saying you are an AI assistant, or remove this question; callers are always told they are speaking with an AI. |
account_deletion_unavailable | 503 | The deletion service cannot safely accept this request now. | Try again later. |
deletion_confirmation_mismatch | 422 | The entered workspace ID does not match this workspace. | Enter this workspace ID exactly and try again. |
deletion_subscription_active | 409 | Your subscription is still active without a scheduled cancellation. | Set the subscription to cancel in Billing, then request deletion again. Open |
deletion_scheduled | 409 | A scheduled deletion prevents subscription reactivation. | Cancel the account deletion before resuming your subscription. |
deletion_not_scheduled | 409 | There is no scheduled deletion to cancel. | Check the account deletion status. |
account_deletion_in_progress | 409 | The workspace has passed the cancellation window. | Contact support if you need help. |
legal_hold_unavailable | 503 | Legal hold status could not be verified. | Retry shortly. |
invalid_tenant | 422 | The request does not identify a valid workspace. | Choose a valid workspace and try again. |
invalid_reason | 422 | The reason must contain 3 to 500 characters. | Provide a reason between 3 and 500 characters. |
auth_context_required | 401 | No authenticated tenant/user scope was provided. | Send a valid API key or sign in again. |
api_key_not_found | 404 | API key does not exist in the authenticated tenant. | Check the key id, or create a new key. |
idempotency_key_reused | 409 | Same Idempotency-Key was reused with a different body. | Use a new key for a changed request. |
tenant_rate_limited | 429 | Tenant token bucket is empty. | Retry with jitter. |
provider_rate_limited | 429 | Provider token bucket is empty. | Retry with jitter. |
daily_recipient_limit_exceeded | 429 | Warmup daily recipient cap would be exceeded. | Send the rest after the daily window resets. |
outbox_item_not_found | 404 | Dead-letter or scheduled item is not in this tenant. | Check the item id. |
outbox_item_not_dead_letter | 409 | Only dead-letter items can be replayed. | Replay only dead-letter items. |
message_not_cancellable | 409 | Only future pending sends can be cancelled. | Nothing to cancel once a send has started. |
Retry and idempotency
| Rule | Guidance |
|---|---|
| Safe to retry | 401 after token refresh, 429, transient 5xx, provider timeout |
| Do not blind retry | 403, 404, 409, 422, 423 |
| Use jitter | Start at 1-2s, double to 30s, cap retries by workflow criticality |
| Use idempotency | Send Idempotency-Key on POST /v1/messages and setup writes |
Workspace-level guidance
HTTP 423 means a workspace-level action is required. Review your Workspace sending status or a safety restriction, then contact support to appeal or to ask for export access while sending is paused. Subscription states that block new sends (HTTP 402) still leave exports available; fix billing to resume sending.
Related guides
Developer guide · SDK reference · API reference · Webhook verification · API and SDK policies