{
  "info": {
    "name": "IotaBot API",
    "description": "WhatsApp, Instagram, email and website chat — one REST API. Set the IOTABOT_API_KEY variable (use a test key, ib_test_…, to try everything without sending). Docs: https://iotabot.com/docs/api",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "bearer",
    "bearer": [
      {
        "key": "token",
        "value": "{{IOTABOT_API_KEY}}",
        "type": "string"
      }
    ]
  },
  "variable": [
    {
      "key": "baseUrl",
      "value": "https://api.iotabot.com/v1"
    },
    {
      "key": "IOTABOT_API_KEY",
      "value": ""
    }
  ],
  "item": [
    {
      "name": "Conversations",
      "description": "List and read conversations across website chat, WhatsApp, Instagram and email; close, reopen, hand to a human, assign and tag them.",
      "item": [
        {
          "name": "List conversations, newest first",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/conversations",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "conversations"
              ],
              "query": [
                {
                  "key": "channel",
                  "value": "",
                  "disabled": true,
                  "description": "website, whatsapp, instagram or email"
                },
                {
                  "key": "status",
                  "value": "",
                  "disabled": true,
                  "description": "open or closed"
                },
                {
                  "key": "website_id",
                  "value": "",
                  "disabled": true,
                  "description": ""
                },
                {
                  "key": "updated_since",
                  "value": "",
                  "disabled": true,
                  "description": "ISO 8601 date — conversations with a message since then"
                },
                {
                  "key": "limit",
                  "value": "",
                  "disabled": true,
                  "description": "1–100, default 25"
                },
                {
                  "key": "starting_after",
                  "value": "",
                  "disabled": true,
                  "description": "the next_cursor of the previous page"
                }
              ]
            },
            "description": "List conversations, newest first\n\nPermission: conversations:read"
          }
        },
        {
          "name": "One conversation with its contact and mode",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/conversations/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "conversations",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": ""
                }
              ]
            },
            "description": "One conversation with its contact and mode\n\nPermission: conversations:read"
          }
        },
        {
          "name": "Close, reopen, hand to a human or back to the AI, assign, tag",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/conversations/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "conversations",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": ""
                }
              ]
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"mode\": \"human\",\n  \"tags\": [\n    \"vip\",\n    \"refund\"\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "description": "status: \"closed\" closes it (a website visitor is asked for a rating); status: \"open\" reopens it with the AI. mode: \"human\" puts it in the agent queue so the AI stops; mode: \"ai\" gives it back. assigned_agent_id is a team member's user id, or null to unassign. tags replaces the tags.\n\nPermission: conversations:write"
          }
        },
        {
          "name": "The thread, oldest first",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/conversations/:id/messages",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "conversations",
                ":id",
                "messages"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "",
                  "disabled": true,
                  "description": ""
                },
                {
                  "key": "starting_after",
                  "value": "",
                  "disabled": true,
                  "description": ""
                }
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": ""
                }
              ]
            },
            "description": "Email messages carry html — sanitised, safe to show in a browser — and html_raw, the body exactly as it arrived, plus email with from, to, cc, in_reply_to and references.\n\nPermission: messages:read"
          }
        }
      ]
    },
    {
      "name": "Messages",
      "description": "Send messages on any channel with one endpoint — text, WhatsApp templates and marketing messages, and email with HTML and attachments — read delivery status, and check whose messaging window is open.",
      "item": [
        {
          "name": "Send on any channel — checked now, delivered in the background",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{$guid}}"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/messages",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "messages"
              ]
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"conversation_id\": \"6a4b9e21c3f1a2b3c4d5e6f7\",\n  \"text\": \"Your order #1042 shipped yesterday — it arrives Friday.\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "description": "Reply in an existing conversation with conversation_id — on an Instagram comment thread that is a public reply under the customer's latest comment, or a private DM to them with reply_as: \"dm\" — or start one: WhatsApp with channel, to (a phone number) and an approved template; email with channel, to, from (one of your verified addresses) and subject. Everything the channel would refuse is refused here with a specific code. Attach files with attachments (uploaded file_ids or inline base64, 10 MB in all): any file on email and WhatsApp, images, video and audio on Instagram. An accepted message returns 202 with status: \"queued\"; its outcome arrives as a message.status webhook and on GET /messages/{id}. Send an Idempotency-Key header so a retry never sends twice.\n\nPermission: messages:send"
          }
        },
        {
          "name": "One message and its delivery status",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/messages/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "messages",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": ""
                }
              ]
            },
            "description": "status is queued, sent, delivered, read or failed for messages you sent, and received for customer messages.\n\nPermission: messages:read"
          }
        },
        {
          "name": "Everyone you can message right now, soonest-closing window first",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/messaging-window",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "messaging-window"
              ],
              "query": [
                {
                  "key": "channel",
                  "value": "",
                  "disabled": true,
                  "description": "whatsapp or instagram — both when left out"
                },
                {
                  "key": "closes_within_minutes",
                  "value": "",
                  "disabled": true,
                  "description": "1–1440: only windows closing within this many minutes"
                },
                {
                  "key": "limit",
                  "value": "",
                  "disabled": true,
                  "description": "1–100, default 25"
                },
                {
                  "key": "starting_after",
                  "value": "",
                  "disabled": true,
                  "description": "the next_cursor of the previous page"
                }
              ]
            },
            "description": "WhatsApp and Instagram allow a free-form message only within 24 hours of the customer's last message. This lists every WhatsApp and Instagram DM conversation whose window is open now — opted-out customers and conversations where an agent paused integration replies are left out — with window_closes_at and seconds_left, sorted so the windows closing first come first. closes_within_minutes keeps only those closing soon. Use it to reach people before their window shuts; send with POST /messages. Instagram comment threads are not listed (their private reply has its own 7-day clock — check one with POST /messaging-window/check).\n\nPermission: conversations:read"
          }
        },
        {
          "name": "Can these people be messaged now, and until when?",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{$guid}}"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/messaging-window/check",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "messaging-window",
                "check"
              ]
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"items\": [\n    {\n      \"instagram_username\": \"priya.styles\"\n    },\n    {\n      \"phone\": \"+919812345678\"\n    },\n    {\n      \"conversation_id\": \"6a4b9e21c3f1a2b3c4d5e6f7\"\n    }\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "description": "Up to 100 people in one call, each named by one of conversation_id, instagram_username, instagram_user_id or phone (WhatsApp) — mix them freely. Add instagram_account_id or whatsapp_account_id to an item to look on one of your accounts only. Each row says can_send (a free-form message would be accepted now), window_closes_at, seconds_left and, when it cannot, reason: window_closed, no_conversation (they never wrote to you), opted_out, integration_paused, ambiguous (the username or number matches on several of your accounts — matches lists them), channel_not_allowed, invalid_phone, conversation_not_found, invalid_item (not exactly one identifier), and for comment threads private_reply_used or no_comment. On WhatsApp template_allowed says whether an approved template can still go — it can at any time, window or not. On an Instagram comment thread (thread: \"comment\") the window is the private reply's: once per comment, within 7 days. Usernames are matched as IotaBot last saw them, so a renamed account may show no_conversation — instagram_user_id (from webhooks) always matches. Email and website chat have no window (window_closes_at: null). Rows come back in the order sent; one bad item never fails the rest. Checks use no API quota.\n\nPermission: conversations:read"
          }
        },
        {
          "name": "Send a WhatsApp marketing template through Meta's Marketing Messages API",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{$guid}}"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/whatsapp/marketing-messages",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "whatsapp",
                "marketing-messages"
              ]
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"to\": \"+919812345678\",\n  \"whatsapp_account_id\": \"6a4c01aa…\",\n  \"template\": {\n    \"name\": \"festive_offer\",\n    \"language\": \"en_US\",\n    \"parameters\": [\n      \"Priya\"\n    ]\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "description": "For promotions, offers and announcements: an approved MARKETING template (see GET /whatsapp/templates — category), to a phone number with to or in an existing WhatsApp conversation with conversation_id. Meta delivers it through its Marketing Messages API, with delivery optimised for marketing — the same route IotaBot campaigns use. A number not yet onboarded to it (marketing_messages on GET /channels) still sends: Meta falls back to the Cloud API. Utility and authentication templates are refused with template_not_marketing; send those with POST /messages. Everything else is as POST /messages: checked now, 202 with status: \"queued\", the outcome as a message.status webhook and on GET /messages/{id}, and an Idempotency-Key header makes retries safe.\n\nPermission: messages:send"
          }
        },
        {
          "name": "Approved WhatsApp templates you can send",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/whatsapp/templates",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "whatsapp",
                "templates"
              ]
            },
            "description": "Approved WhatsApp templates you can send\n\nPermission: messages:send"
          }
        },
        {
          "name": "Email addresses you can send from",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/email/senders",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "email",
                "senders"
              ]
            },
            "description": "Email addresses you can send from\n\nPermission: messages:send"
          }
        },
        {
          "name": "Upload an attachment (base64, up to 10 MB) and use its id in POST /messages",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{$guid}}"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/files",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "files"
              ]
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"filename\": \"invoice.pdf\",\n  \"content_type\": \"application/pdf\",\n  \"content_base64\": \"JVBERi0xLjQK…\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "description": "Upload an attachment (base64, up to 10 MB) and use its id in POST /messages\n\nPermission: messages:send"
          }
        }
      ]
    },
    {
      "name": "Contacts",
      "description": "Read, create and update the customers your workspace knows across channels, including their opt-outs.",
      "item": [
        {
          "name": "Contacts across channels, newest first",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/contacts",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "contacts"
              ],
              "query": [
                {
                  "key": "email",
                  "value": "",
                  "disabled": true,
                  "description": ""
                },
                {
                  "key": "phone",
                  "value": "",
                  "disabled": true,
                  "description": ""
                },
                {
                  "key": "updated_since",
                  "value": "",
                  "disabled": true,
                  "description": ""
                },
                {
                  "key": "limit",
                  "value": "",
                  "disabled": true,
                  "description": ""
                },
                {
                  "key": "starting_after",
                  "value": "",
                  "disabled": true,
                  "description": ""
                }
              ]
            },
            "description": "Contacts belong to the whole workspace, so keys limited to some websites cannot use them.\n\nPermission: contacts:read"
          }
        },
        {
          "name": "One contact",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/contacts/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "contacts",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": ""
                }
              ]
            },
            "description": "One contact\n\nPermission: contacts:read"
          }
        },
        {
          "name": "Create a contact (email or phone)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{$guid}}"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/contacts",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "contacts"
              ]
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Priya Sharma\",\n  \"email\": \"priya@example.com\",\n  \"phone\": \"+919812345678\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "description": "Create a contact (email or phone)\n\nPermission: contacts:write"
          }
        },
        {
          "name": "Update a contact or its opt-outs",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/contacts/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "contacts",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": ""
                }
              ]
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Priya S.\",\n  \"whatsapp_opt_out\": true\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "description": "Update a contact or its opt-outs\n\nPermission: contacts:write"
          }
        }
      ]
    },
    {
      "name": "Webhooks API",
      "description": "Manage webhook endpoints as code, re-read an event, list deliveries and replay one.",
      "item": [
        {
          "name": "Your webhook endpoints, and the IP addresses webhooks come from",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/webhook-endpoints",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "webhook-endpoints"
              ]
            },
            "description": "Your webhook endpoints, and the IP addresses webhooks come from\n\nPermission: webhooks:manage"
          }
        },
        {
          "name": "Add an endpoint — verified straight away; the signing secret is returned once",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{$guid}}"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/webhook-endpoints",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "webhook-endpoints"
              ]
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"url\": \"https://crm.acme.com/iotabot\",\n  \"description\": \"CRM\",\n  \"channels\": [\n    \"whatsapp\",\n    \"email\"\n  ],\n  \"events\": [\n    \"message.received\",\n    \"message.status\"\n  ],\n  \"metadataOnly\": false,\n  \"testEvents\": false\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "description": "The URL must answer the endpoint.verification event before it receives anything. metadataOnly: true sends ids and status without content or customer details; testEvents: true also sends sandbox events.\n\nPermission: webhooks:manage"
          }
        },
        {
          "name": "Change URL (re-verifies), events, channels, websites or switch it on or off",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/webhook-endpoints/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "webhook-endpoints",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": ""
                }
              ]
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"events\": [\n    \"message.received\",\n    \"message.sent\"\n  ],\n  \"enabled\": true\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "description": "Change URL (re-verifies), events, channels, websites or switch it on or off\n\nPermission: webhooks:manage"
          }
        },
        {
          "name": "Delete an endpoint — deliveries still retrying are dropped",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/webhook-endpoints/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "webhook-endpoints",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": ""
                }
              ]
            },
            "description": "Delete an endpoint — deliveries still retrying are dropped\n\nPermission: webhooks:manage"
          }
        },
        {
          "name": "New signing secret; the old one keeps co-signing for grace_hours (0–168)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{$guid}}"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/webhook-endpoints/:id/roll-secret",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "webhook-endpoints",
                ":id",
                "roll-secret"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": ""
                }
              ]
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"grace_hours\": 24\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "description": "New signing secret; the old one keeps co-signing for grace_hours (0–168)\n\nPermission: webhooks:manage"
          }
        },
        {
          "name": "Re-read a webhook event — the same envelope your endpoint received",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/events/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "events",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "evt_…"
                }
              ]
            },
            "description": "Re-read a webhook event — the same envelope your endpoint received"
          }
        },
        {
          "name": "Webhook delivery log, newest first",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/webhook-deliveries",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "webhook-deliveries"
              ],
              "query": [
                {
                  "key": "endpoint_id",
                  "value": "",
                  "disabled": true,
                  "description": ""
                },
                {
                  "key": "status",
                  "value": "",
                  "disabled": true,
                  "description": "pending, retrying, succeeded, failed"
                },
                {
                  "key": "limit",
                  "value": "",
                  "disabled": true,
                  "description": ""
                },
                {
                  "key": "starting_after",
                  "value": "",
                  "disabled": true,
                  "description": ""
                }
              ]
            },
            "description": "Webhook delivery log, newest first\n\nPermission: webhooks:manage"
          }
        },
        {
          "name": "Send a delivery's event to its endpoint again",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Idempotency-Key",
                "value": "{{$guid}}"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/webhook-deliveries/:id/replay",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "webhook-deliveries",
                ":id",
                "replay"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "whd_…"
                }
              ]
            },
            "description": "Send a delivery's event to its endpoint again\n\nPermission: webhooks:manage"
          }
        }
      ]
    },
    {
      "name": "Channels & usage",
      "description": "The websites, numbers, accounts and mailboxes a key can use, and this month's API quota.",
      "item": [
        {
          "name": "The websites, WhatsApp numbers, Instagram accounts and email addresses this key can use",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/channels",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "channels"
              ]
            },
            "description": "Use it to discover ids you need elsewhere — a whatsapp_account_id to start a WhatsApp conversation, or a website_id to filter conversations."
          }
        },
        {
          "name": "API messages used this month, the limit, and when it resets",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/usage",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "usage"
              ]
            },
            "description": "Only messages the channel accepted count. Webhooks never count."
          }
        }
      ]
    },
    {
      "name": "Sandbox",
      "description": "Build and test against the real API with test keys: sandbox data only, nothing ever sent, no quota — and simulated customer messages on any channel.",
      "item": [
        {
          "name": "A customer message on any channel, as if it had just arrived (test keys only)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{$guid}}"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/test/inbound",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "test",
                "inbound"
              ]
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"channel\": \"whatsapp\",\n  \"from\": \"+15550000001\",\n  \"name\": \"Priya Sharma\",\n  \"text\": \"Hi, where is my order #1042?\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "description": "Use a test key (ib_test_…). Add comment: true on Instagram to simulate a comment instead of a DM. The message lands in a sandbox conversation — the same from continues it — and produces the same webhooks a real one would, marked livemode: false, for endpoints with test events on. Reply to it with POST /messages and the same test key."
          }
        }
      ]
    }
  ]
}