WhatsApp + Instagram
REST API, v1.
Bearer auth. Cursor pagination. Idempotency keys. Signed webhooks. Free and Creator keys are read-only; paid Business and Developer plans can use write routes when their scopes, channels, and limits allow it.
Pick a runtime
const API_BASE = process.env.IR_API_URL ?? 'https://api.instantreply.co';
const headers = { 'Authorization': `Bearer ${process.env.IR_API_KEY}` };
// List active conversations
const res = await fetch(`${API_BASE}/v1/conversations?status=active`, { headers });
const { data, pagination } = await res.json();
for (const c of data) {
if (c.last_message_preview?.includes('refund')) {
await fetch(`${API_BASE}/v1/conversations/${c.id}`, {
method: 'PATCH', headers: { ...headers, 'Content-Type': 'application/json' },
body: JSON.stringify({ tags: ['priority:refund'] }),
});
}
}v1 surface
Core routes, one auth header.
Live keys access the routes allowed by their scopes. Test keys cannot read tenant data or perform live operations; they only return synthetic responses for POST /messages, conversation replies, campaign sends, automation triggers, and comment replies. Every other test-key request returns 403. Plan access also applies: Free and Creator are read-only, while paid Business and Developer plans can use write routes within their scopes and limits.
- GET
/v1/conversationsList conversations, cursor pagination - GET
/v1/conversations/:idFetch one conversation - PATCH
/v1/conversations/:idUpdate status, assignee, tags - GET
/v1/conversations/:id/messagesList messages in a conversation - POST
/v1/conversations/:id/messagesSend text, media, quick replies, or a CTA URL; integration_id selects the sender account. - GET
/v1/messagesList sent and received messages with delivery status - POST
/v1/messagesSend text, media, quick replies, or a CTA URL from a selected account. - GET
/v1/messages/:idFetch one message with delivery status - POST
/v1/messages/:id/reactionSet or clear a reaction on an inbound WhatsApp message - GET
/v1/ticketsList support tickets with filters and cursor pagination - GET
/v1/tickets/:idFetch one support ticket - PATCH
/v1/tickets/:idUpdate ticket status, priority, or assignee - GET
/v1/contactsList contacts - GET
/v1/contacts/:idFetch one contact - PATCH
/v1/contacts/:idUpdate name, email, lead stage - GET
/v1/channelsList each connected account with group and AI status - POST
/v1/channels/connect-sessionsCreate a secure browser handoff for an owner to authorize another Meta account - PATCH
/v1/channels/:integration_idToggle this account's AI replies or assign/unassign its group - GET
/v1/channels/groupsList independent AI brain groups - POST
/v1/channels/groupsCreate a group with its own AI brain configuration - PATCH
/v1/channels/groups/:group_idUpdate group name, AI gate, or brain settings - DELETE
/v1/channels/groups/:group_idDelete a group and return its accounts to workspace defaults - GET
/v1/channels/groups/:group_id/knowledgeList knowledge scoped to one group brain - POST
/v1/channels/groups/:group_id/knowledgeAdd prompt-injection-scanned group knowledge - POST
/v1/channels/groups/:group_id/knowledge/filesParse and add a supported knowledge file in memory - DELETE
/v1/channels/groups/:group_id/knowledge/:entry_idDelete one group knowledge entry - GET
/v1/channels/groups/:group_id/providersList group AI providers; secrets are masked - PUT
/v1/channels/groups/:group_id/providers/:providerConfigure a group ElevenLabs voice or custom reply endpoint - DELETE
/v1/channels/groups/:group_id/providers/:providerRemove a group AI provider - GET
/v1/channels/:integration_id/providersList channel provider overrides; secrets are masked - PUT
/v1/channels/:integration_id/providers/:providerConfigure a channel ElevenLabs voice or custom reply endpoint - DELETE
/v1/channels/:integration_id/providers/:providerRemove a channel provider override - GET
/v1/analytics/summaryDashboard analytics by date range - GET
/v1/usageTier, rate-limit headroom, request count - GET
/v1/statusUnauthenticated API dependency health (database and Redis) - GET
/v1/keysList API keys for the workspace - POST
/v1/keysCreate a scoped live or test key - POST
/v1/keys/:id/rotateRotate an API key - DELETE
/v1/keys/:idRevoke an API key - GET
/v1/webhooksList registered webhook endpoints - POST
/v1/webhooksRegister a webhook, returns signing secret - DELETE
/v1/webhooks/:idRevoke a webhook endpoint - GET
/v1/webhooks/:id/deliveriesInspect recent webhook delivery attempts - POST
/v1/webhooks/sendTrigger a WhatsApp journey/send immediately - POST
/v1/webhooks/trigger-campaignCompatibility alias for /v1/webhooks/send - POST
/v1/webhooks/sign-payloadGenerate a test HMAC signature for a payload - GET
/v1/journeysList active WhatsApp journeys and required variables - GET
/v1/trigger/limitsRead journey quota, usage, and remaining capacity - POST
/v1/triggerEnroll a phone into a WhatsApp journey - GET
/v1/trigger/historyList recent journey trigger enrollments - POST
/v1/trigger/validateDry-run a journey trigger payload - POST
/v1/trigger/batchBatch enroll up to 100 recipients - DELETE
/v1/trigger/enrollmentsStop enrollments for a phone/journey - POST
/v1/eventsFire a semantic event mapped to a journey - GET
/v1/templatesList WhatsApp message templates - POST
/v1/templatesCreate and submit a template to Meta - GET
/v1/templates/:idFetch one template - DELETE
/v1/templates/:idDelete a template from Meta and the local registry - POST
/v1/templates/generate/validateValidate a draft objective before storing - POST
/v1/templates/:id/validateValidate category fit + UTILITY coaching (flagged phrases, cost gap) - POST
/v1/templates/:id/submitSubmit template to Meta for approval - POST
/v1/templates/:id/sendSend an approved template with optional sender-number selection - GET
/v1/trigger/status/:idExplain a delivery outcome - what happened, fault (ours vs Meta), next step - GET
/v1/campaignsList broadcast campaigns - POST
/v1/campaignsCreate a campaign draft - GET
/v1/campaigns/:idFetch one campaign - PATCH
/v1/campaigns/:idUpdate a draft or scheduled campaign - POST
/v1/campaigns/:id/sendStart a draft campaign - GET
/v1/pipeline/leadsList pipeline leads - GET
/v1/pipeline/leads/:idFetch one pipeline lead - PATCH
/v1/pipeline/leads/:idUpdate pipeline lead fields - PATCH
/v1/pipeline/leads/:id/stageMove a lead to a new stage - GET
/v1/pipeline/stagesList pipeline stages - GET
/v1/automationsList automations - GET
/v1/automations/:idFetch one automation - POST
/v1/automations/:id/triggerManually trigger an automation - GET
/v1/commentsList tracked comments - GET
/v1/comments/:idFetch one comment - POST
/v1/comments/:id/replyReply to a tracked comment - PATCH
/v1/comments/settingsEnable or disable AI comment auto-replies - GET
/v1/developer/capabilitiesScopes and features available to the current key - GET
/v1/developer/onboardingIntegration checklist - which steps are done - GET
/v1/developer/limitsRate limits and quotas for the current plan tier - GET
/v1/developer/troubleshooting/errors/:codePlain-English explanation of a Meta or platform error code
Channels and AI brains
Connect accounts, then give each group its own brain.
A workspace can connect multiple WhatsApp numbers, Instagram accounts, and Facebook Pages. Assign each account to one group, or leave it on the workspace brain. Each group has its own business facts, FAQs, reply style, platform prompts, platform enablement, and AI on/off switch. Use a one-account group for a dedicated brain.
The free trial allows 10 Meta messaging accounts total across WhatsApp numbers, Instagram accounts, and Facebook Messenger Pages. A workspace owner completes Meta OAuth in the browser; API and MCP keys never accept Meta access tokens. At the limit, upgrade or ask InstantReply about self-managed deployment. That requires your own Meta app and Meta verification and App Review. Meta may request a permission-flow screen recording; budget a month or more for planning because review timing varies.
{
"name": "Retail team",
"ai_enabled": true,
"brain_config": {
"business_name": "Example Shop",
"tone": "friendly",
"language": "en",
"company_info": "Online shop with same-day delivery in Dubai.",
"faqs": "Returns accepted within 14 days.",
"platform_prompts": {
"instagram": "Keep replies short."
},
"enabled_platforms": {
"messenger": false
},
"emoji_usage": "sparingly"
}
}{
"content": "Here are the details",
"integration_id": "connected-account-uuid",
"media": {
"type": "document",
"url": "https://files.example.com/guide.pdf",
"filename": "guide.pdf",
"caption": "Product guide"
},
"buttons": [
"See pricing",
"Talk to a person"
],
"cta_url": {
"url": "https://example.com/book",
"display_text": "Book now"
}
}{
"enabled": true,
"config": {
"voice_id": "your-elevenlabs-voice-id",
"model_id": "eleven_multilingual_v2",
"output_format": "mp3_44100_128",
"language_code": "en",
"apply_text_normalization": "auto",
"apply_language_text_normalization": false,
"seed": 123,
"pronunciation_dictionary_locators": [
{
"pronunciation_dictionary_id": "dictionary-id",
"version_id": "version-id"
}
],
"voice_settings": {
"stability": 0.5,
"similarity_boost": 0.75,
"style": 0,
"use_speaker_boost": true,
"speed": 1
}
},
"secret": "your-elevenlabs-api-key"
}{
"enabled": true,
"config": {
"url": "https://reply.example.com/instantreply",
"timeout_ms": 5000,
"send_knowledge_context": true,
"send_conversation_history": true,
"send_verified_tool_context": true
},
"secret": "your-webhook-signing-key"
}curl -X POST "https://api.instantreply.co/v1/channels/groups/GROUP_ID/knowledge/files" \
-H "Authorization: Bearer $IR_API_KEY" \
-F "type=faq" \
-F "title=Opening hours" \
-F "file=@opening-hours.pdf"Message inputs: content (1–4096 chars), optional integration_id (connected sender), media.type (image/document/audio/video), media.url (HTTPS), media.caption (≤1024 chars), media.filename (≤255 chars), buttons (1–10 labels, each ≤20 chars), cta_url.url (HTTPS) and cta_url.display_text (≤20 chars). An Idempotency-Key can be sent on either message endpoint. Meta controls actual media MIME, codec, size, permissions, interactive-message, and 24-hour-window eligibility. WhatsApp reactions accept emoji (≤8 chars) or empty string to clear; reactions are not exposed on Instagram/Messenger. Group knowledge file upload accepts TXT, MD, CSV, TSV, JSON, HTML/HTM, PDF, DOCX, XLSX; one file ≤10 MiB, Office entries ≤20 MiB each/50 MiB expanded, XLSX ≤5,000 rows and 100 columns per sheet, extracted text ≤100,000 chars. Files are parsed in memory and source bytes are not retained. MCP agents add extracted text through add_channel_group_knowledge. Audience imports accept CSV/TSV/TXT or XLSX, max 5 MiB and 25,000 contacts; no contact rows are sent to an AI service. ElevenLabs settings accept voice_id, model_id, two MP3 output_format values, optional voice_settings (stability/similarity_boost/style 0–1, use_speaker_boost, speed 0.7–1.2), language_code, apply_text_normalization (auto/on/off), apply_language_text_normalization, uint32 seed, up to three pronunciation_dictionary_locators (each dictionary id + version id), and a secret API key. Custom reply webhooks accept HTTPS url, timeout_ms (1,000–10,000), send_knowledge_context, send_conversation_history, send_verified_tool_context booleans and a signing secret; signed requests include X-InstantReply-Timestamp and X-InstantReply-Signature headers (`sha256=HMAC-SHA256(secret, timestamp + "." + rawBody)`) with a JSON body containing version, task, reply_instructions, conversation_history, verified_knowledge, verified_tool_context, response_schema. The endpoint returns { reply: string }; only bounded context is sent and provider replies pass the existing grounding and fake-action checks. Secret values are write-only and encrypted at rest.
Design
Boring choices, on purpose.
- 01
Cursor pagination, always
Every list endpoint returns has_more and next_cursor. No skipped records under load, no off-by-one.
- 02
Idempotency-Key on message sends
POST /v1/messages requires a 1–200 character key. Matching retries replay the response; reusing a key with a different body returns 409.
- 03
X-Request-ID on every response
Use the response ID to correlate a request with your logs and support reports. You can also send your own valid ID.
- 04
Errors carry actionable codes
API errors include status, code, and message; validation failures may also include field details. X-Request-ID is a separate response header.
Errors
One envelope, every time.
No nested success flags. No mixed casing. No silent 200s on failure. If the call broke, the body tells you exactly which call, what it expected, and where the docs live.
// 422 Unprocessable Entity
{
"error": {
"code": "INVALID_PLATFORM",
"message": "Channel 'tiktok' is not yet supported on v1.",
"doc_url": "https://www.instantreply.co/api-docs#errors",
"request_id": "req_8f2a0b1c"
}
}Webhooks
Register once. Save the secret now.
POST /v1/webhooks with your endpoint URL and the events you want. The response returns a whsec_ signing secret exactly once — it is never stored in a readable form and never shown again. Copy it straight into your INSTANTREPLY_WEBHOOK_SECRET.
Lost it? There's no reveal endpoint — delete the webhook (DELETE /v1/webhooks/:id) and register a new one to get a fresh secret. Use it to verify X-InstantReply-Signature on every delivery.
// POST /v1/webhooks (scope: webhooks:write)
// Request
{
"url": "https://your-app.com/instantreply/webhook",
"events": ["*"],
"description": "Inbound events"
}
// 201 Created — the secret is returned ONCE, here only.
{
"id": "b1e7…",
"url": "https://your-app.com/instantreply/webhook",
"events": ["*"],
"secret": "whsec_9f2c…" // ← store this now; never shown again
}WhatsApp delivery
Why some messages don't send.
Some blocks we can catch up front. Some only WhatsApp can decide, after it has already accepted the send.
Blocked before sending
Template not yet approved by Meta, or the recipient opted out. The journey stops immediately with a reason — you're never charged for it.
Surfaced after sending (Meta-side)
Error 131049: Meta accepts the send, then throttles delivery based on the recipient's own engagement — no API can predict it in advance. We auto-pause a template after 5 throttled sends in an hour so you stop burning quota.
// message.delivery_failed webhook
{
"event": "message.delivery_failed",
"data": {
"template_name": "order_promo",
"error_code": "131049",
"error_reason": "WhatsApp limited this message to protect users \
from too many marketing messages. Switch to a Utility template or \
message people who've recently engaged.",
"throttled": true
}
}