API documentation
API errors
Every IotaBot API error code, its HTTP status, and what to do about it.
Errors use HTTP status codes and one body shape, so your code can branch on error.code rather than on the message text:
{
"error": {
"code": "window_closed",
"message": "More than 24 hours since the customer last wrote — WhatsApp only allows an approved template now",
"request_id": "0f6047d9-bf3d-4ca7-adac-33011910b01d"
}
}request_id is also sent as the X-Request-Id header on every response. Quote it when you contact support — it finds the request in your logs and ours.
| Code | Status | What it means |
|---|---|---|
invalid_api_key | 401 | The key is missing, malformed or unknown. Send it as Authorization: Bearer ib_live_…. |
key_revoked | 401 | The key was revoked. Create a new one. |
key_expired | 401 | The key passed its expiry date. Roll or replace it. |
key_rolled | 401 | This is a rolled-away secret whose grace period ended. Use the new secret. |
test_key_required | 403 | Simulated messages need a test key (ib_test_…). |
step_up_required | 403 | Portal only: creating, rolling or showing a secret needs a fresh emailed code. |
plan_upgrade_required | 403 | API keys and webhooks are on paid plans. Upgrade the workspace to use them. |
account_inactive | 403 | The workspace is suspended or closed. |
ip_not_allowed | 403 | The request came from an address outside the key's IP allowlist. |
insufficient_scope | 403 | The key lacks the permission this endpoint needs (shown on each endpoint). |
channel_not_allowed | 403 | The key is limited to other channels. |
website_not_allowed | 403 | The key is limited to other websites. |
contacts_need_all_websites | 403 | Contacts belong to the whole workspace; use a key that is not limited to some websites. |
invalid_request | 400 | The body or parameters do not make sense together — the message says which. |
invalid_version | 400 | The IotaBot-Version header names a version the API does not serve. Leave it out to use the key's own version. |
invalid_limit | 400 | limit must be between 1 and 100. |
invalid_cursor | 400 | starting_after is not a cursor this API returned. |
invalid_phone | 400 | Give phone numbers with their country code, e.g. +919812345678. |
text_required | 400 | The message has no content for its channel (text, html, a template or attachments). |
from_required | 400 | A new email needs from, one of your verified addresses. |
subject_required | 400 | A new email needs a subject. |
invalid_email | 400 | to is not an email address. |
whatsapp_account_required | 400 | The key reaches several WhatsApp numbers — say which with whatsapp_account_id. |
invalid_attachment | 400 | Each attachment needs file_id, or filename with content_base64. |
file_not_found | 400 | No uploaded file with that id. |
invalid_idempotency_key | 400 | Idempotency-Key must be at most 255 characters. |
file_rejected | 400 | The file type is not allowed, the file is empty, or it is over 10 MB. |
unsupported_attachment | 400 | Instagram takes images, video and audio only. |
file_already_used | 400 | An uploaded file can be attached to one message only. |
attachments_too_large | 400 | Attachments may total 10 MB per message. |
conversation_not_found | 404 | No conversation with that id that this key can see. |
message_not_found | 404 | No message with that id that this key can see. |
contact_not_found | 404 | No contact with that id. |
integration_paused | 409 | An agent paused integration replies on this conversation. Stop retrying; a conversation.updated event tells you when it resumes. |
contact_exists | 409 | Another contact already has this email or phone; the message includes its id. |
idempotency_in_progress | 409 | A request with this Idempotency-Key is still being processed. Retry shortly. |
window_closed | 422 | More than 24 hours since the customer last wrote. On WhatsApp send an approved template; on Instagram you must wait for them to write. |
no_whatsapp_account | 422 | This key cannot reach any connected WhatsApp number. |
endpoint_disabled | 400 | The webhook endpoint is off or deleted, so it cannot be replayed to. |
event_expired | 400 | The event is past your log retention period. |
template_not_approved | 422 | No approved template with that name and language on this number (GET /whatsapp/templates). |
template_not_marketing | 422 | POST /whatsapp/marketing-messages sends MARKETING templates only. Send utility and authentication templates with POST /messages. |
conversation_closed | 422 | Website chats accept messages only while open; reopen a conversation before changing its mode. |
sender_not_verified | 422 | The email domain is not verified yet, so nothing can be sent from it. |
sender_not_found | 422 | from is not one of your email addresses this key can use (GET /email/senders). |
recipient_suppressed | 422 | An address bounced permanently or reported your email as spam, so nothing can be sent to it. |
recipient_unsubscribed | 422 | The address unsubscribed: you can answer their emails, but not start a new thread. |
private_reply_expired | 422 | Instagram allows a private reply (reply_as: "dm") only within 7 days of the comment. Reply publicly instead. |
private_reply_used | 422 | The comment already has its private reply — Instagram allows one per comment. Once the customer answers, reply in their DM conversation. |
recipient_opted_out | 422 | The customer opted out of this channel (they replied STOP, or the contact is marked opted out). Nothing can be sent to them there. |
no_recipient | 422 | The email conversation has no customer address to send to. |
agent_not_found | 422 | assigned_agent_id is not an active agent in this workspace. |
idempotency_key_reused | 422 | The Idempotency-Key was already used with a different request. |
rate_limited | 429 | Over the key's rate limit (20 a second and 300 a minute on paid plans). Wait for Retry-After seconds. |
api_quota_exceeded | 429 | The workspace used its monthly API messages. It resets on the 1st; the plan sets the limit. |
