{
  "info": {
    "name": "WhatsApp API",
    "description": "Send and receive WhatsApp messages over a REST API shaped like Meta's Cloud API.\n\n## Set up in 30 seconds\n\nOpen the collection's **Variables** tab and fill in two values:\n\n| Variable | Where to get it |\n| --- | --- |\n| `api_key` | Console → expand your number → **API keys for this number** → Create key. Shown once. |\n| `phone_number` | The recipient, in E.164 without a `+` — e.g. `919544446002`. |\n\n`session_id` fills itself in: run **Numbers → List numbers** once and a test script captures the first connected number into the variable. Every other request then works unchanged.\n\n`base_url` already points at production.\n\n## You may not need session_id at all\n\n`session_id` says which of your numbers to send FROM. If your key is **scoped to one number** — which is what the console creates — you can drop it from the body entirely and the API uses the key's own number.\n\nSo this works with a scoped key:\n\n    { \"type\": \"text\", \"to\": \"919544446002\", \"text\": \"Hi\" }\n\nAn **account-wide** key has no number to fall back on and returns `session_id is required, or use an API key scoped to a single session`. The requests below all send it explicitly, so they work either way.\n\n## Before you send anything\n\nThis runs on the unofficial WhatsApp protocol, so:\n\n- **Buttons and lists do not render.** An `interactive` payload arrives as a numbered text menu. The reply webhook still carries `interactive.button_reply.id`, identical to the official Cloud API.\n- **Numbers can be banned.** Sends are capped per session with random jitter and this cannot be disabled. Do not use it for cold bulk outreach.\n\nFull documentation: https://wa.adaminnovations.in/guide",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "bearer",
    "bearer": [
      {
        "key": "token",
        "value": "{{api_key}}",
        "type": "string"
      }
    ]
  },
  "variable": [
    {
      "key": "base_url",
      "value": "https://wa.adaminnovations.in",
      "type": "string",
      "description": "Production. Point at http://localhost:3000 to work against a local stack."
    },
    {
      "key": "api_key",
      "value": "wa_live_REPLACE_WITH_YOUR_KEY",
      "type": "string",
      "description": "Create one in the console under the number you want to send from. Shown once. Treat it like a password — it can send messages as your business."
    },
    {
      "key": "phone_number",
      "value": "919544446002",
      "type": "string",
      "description": "Recipient, E.164 without a leading +. Country code is required: 9544446002 is rejected as invalid_phone_number, 919544446002 is accepted."
    },
    {
      "key": "session_id",
      "value": "",
      "type": "string",
      "description": "The number you send FROM. Filled automatically by 'List numbers'."
    },
    {
      "key": "wa_message_id",
      "value": "",
      "type": "string",
      "description": "Filled automatically after you send a message."
    },
    {
      "key": "webhook_id",
      "value": "",
      "type": "string",
      "description": "Filled automatically by 'Create webhook'."
    }
  ],
  "item": [
    {
      "name": "Start here",
      "description": "Run these two in order. After that every other request works.",
      "item": [
        {
          "name": "1. List numbers  (fills session_id)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Prefer a connected number — sending from a disconnected one just 409s.",
                  "const body = pm.response.json()",
                  "const numbers = body.data || []",
                  "const chosen = numbers.find(n => n.status === 'connected') || numbers[0]",
                  "",
                  "pm.test('authenticated', () => pm.response.to.have.status(200))",
                  "",
                  "if (!chosen) {",
                  "    console.warn('No numbers on this account yet. Add one in the console first.')",
                  "    return",
                  "}",
                  "",
                  "pm.collectionVariables.set('session_id', chosen.id)",
                  "console.log(`session_id set to ${chosen.name} (${chosen.status})`)",
                  "",
                  "pm.test('a number is connected', () => {",
                  "    pm.expect(chosen.status, 'no connected number — scan a QR in the console').to.eql('connected')",
                  "})"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/sessions",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "sessions"
              ]
            },
            "description": "Every number on your account and its live status. The test script stores the first connected one in `session_id`."
          }
        },
        {
          "name": "2. Send a text message",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('accepted', () => pm.response.to.have.status(201))",
                  "const id = pm.response.json().wa_message_id",
                  "if (id) pm.collectionVariables.set('wa_message_id', id)"
                ]
              }
            },
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// A fresh idempotency key per run. In your own code, derive this from",
                  "// the thing you are notifying about (order id, ticket id) instead —",
                  "// that is what makes a retry after a timeout safe.",
                  "pm.collectionVariables.set('idempotency_key', 'postman-' + Date.now())"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{idempotency_key}}"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"session_id\": \"{{session_id}}\",\n  \"type\": \"text\",\n  \"to\": \"{{phone_number}}\",\n  \"text\": \"Hello from Postman.\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/v1/messages",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "messages"
              ]
            },
            "description": "`Idempotency-Key` makes a retry safe: the same key replays the original response instead of sending twice. Reusing a key with a different body returns 409."
          }
        }
      ]
    },
    {
      "name": "Numbers",
      "item": [
        {
          "name": "Add a number",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Support line\",\n  \"phone_number\": \"{{phone_number}}\",\n  \"engine\": \"baileys\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/v1/sessions",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "sessions"
              ]
            },
            "description": "The number given here is the ONLY number that may link. If a different one scans the QR it is unlinked immediately and its credentials destroyed."
          }
        },
        {
          "name": "Get a number",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/sessions/{{session_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "sessions",
                "{{session_id}}"
              ]
            }
          }
        },
        {
          "name": "Connect  (returns a QR code)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"prefer_pairing_code\": true\n}"
            },
            "url": {
              "raw": "{{base_url}}/v1/sessions/{{session_id}}/connect",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "sessions",
                "{{session_id}}",
                "connect"
              ]
            },
            "description": "Returns a PNG data URI plus an 8-character pairing code. Scan in WhatsApp → Settings → Linked Devices, or type the code. QR codes expire — poll 'Current QR' for the rotated one."
          }
        },
        {
          "name": "Current QR / pairing code",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/sessions/{{session_id}}/qr",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "sessions",
                "{{session_id}}",
                "qr"
              ]
            }
          }
        },
        {
          "name": "Restart",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/sessions/{{session_id}}/restart",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "sessions",
                "{{session_id}}",
                "restart"
              ]
            },
            "description": "Reconnects using stored credentials. No QR re-scan."
          }
        },
        {
          "name": "Log out",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/sessions/{{session_id}}/logout",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "sessions",
                "{{session_id}}",
                "logout"
              ]
            },
            "description": "Unlinks the device and destroys its credentials. The number must scan a new QR to come back."
          }
        },
        {
          "name": "Delete",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/sessions/{{session_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "sessions",
                "{{session_id}}"
              ]
            },
            "description": "Irreversible. Unlinks the number and deletes its message history."
          }
        }
      ]
    },
    {
      "name": "Messages",
      "item": [
        {
          "name": "Text",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"session_id\": \"{{session_id}}\",\n  \"type\": \"text\",\n  \"to\": \"{{phone_number}}\",\n  \"text\": \"Your order #1234 has shipped.\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/v1/messages",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "messages"
              ]
            }
          }
        },
        {
          "name": "Text  (no session_id — scoped key only)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"type\": \"text\",\n  \"to\": \"{{phone_number}}\",\n  \"text\": \"Sent without naming a number.\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/v1/messages",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "messages"
              ]
            },
            "description": "Identical to the request above, minus `session_id`. A key scoped to one number already knows which number to send from, so naming it is redundant.\n\nWith an account-wide key this returns 400: there is no number to fall back on and the API will not guess. Scope your keys per number and you never type a session ID again."
          }
        },
        {
          "name": "Send a file directly  (upload + send, nothing kept)",
          "request": {
            "method": "POST",
            "header": [],
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "file",
                  "type": "file",
                  "src": [],
                  "description": "Pick a file here"
                },
                {
                  "key": "to",
                  "value": "{{phone_number}}",
                  "type": "text"
                },
                {
                  "key": "session_id",
                  "value": "{{session_id}}",
                  "type": "text"
                },
                {
                  "key": "caption",
                  "value": "Sent straight from a file",
                  "type": "text"
                }
              ]
            },
            "url": {
              "raw": "{{base_url}}/v1/messages/media",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "messages",
                "media"
              ]
            },
            "description": "Pick a file in the Body tab and send. No upload step, no media_id, nothing stored afterwards.\n\nThe type is worked out from the file: images send as image, .webp as sticker, video and audio as themselves, everything else as a document. Add a `type` field to override, or `filename` to control what the recipient sees on a document.\n\nFiles up to 16 MB never touch disk. Larger ones are staged in storage only because they will not fit in one request to the engine, and are deleted straight after — including when the send fails.\n\nUse **Media → Upload** instead if you will send the same file more than once: that keeps it and gives you a media_id to reuse."
          }
        },
        {
          "name": "Image",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"session_id\": \"{{session_id}}\",\n  \"type\": \"image\",\n  \"to\": \"{{phone_number}}\",\n  \"media\": { \"url\": \"https://picsum.photos/600/400\" },\n  \"caption\": \"Your order is on its way\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/v1/messages",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "messages"
              ]
            },
            "description": "Media goes under `media`, as one of `url` (we fetch it server-side), `media_id` (from POST /v1/media) or `base64`. `caption` and `filename` are siblings of `media`, not inside it.\n\nTo send a local file with no hosting at all, use **Send a file directly** above."
          }
        },
        {
          "name": "Document",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"session_id\": \"{{session_id}}\",\n  \"type\": \"document\",\n  \"to\": \"{{phone_number}}\",\n  \"media\": { \"url\": \"https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf\" },\n  \"filename\": \"invoice-1234.pdf\",\n  \"caption\": \"Your invoice\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/v1/messages",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "messages"
              ]
            },
            "description": "`filename` is what the recipient sees. Sits alongside `media`, not inside it."
          }
        },
        {
          "name": "Location",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"session_id\": \"{{session_id}}\",\n  \"type\": \"location\",\n  \"to\": \"{{phone_number}}\",\n  \"latitude\": 11.2588,\n  \"longitude\": 75.7804,\n  \"name\": \"Our store\",\n  \"address\": \"Calicut, Kerala\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/v1/messages",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "messages"
              ]
            },
            "description": "`latitude` and `longitude` are top-level numbers, not nested under a `location` object."
          }
        },
        {
          "name": "Interactive: buttons  (numbered menu)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"session_id\": \"{{session_id}}\",\n  \"type\": \"interactive\",\n  \"to\": \"{{phone_number}}\",\n  \"interactive\": {\n    \"type\": \"button\",\n    \"body\": \"How can we help?\",\n    \"action\": {\n      \"buttons\": [\n        { \"id\": \"sales\", \"title\": \"Sales\" },\n        { \"id\": \"support\", \"title\": \"Support\" }\n      ]\n    }\n  }\n}"
            },
            "url": {
              "raw": "{{base_url}}/v1/messages",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "messages"
              ]
            },
            "description": "Note the **`\"type\": \"button\"`** inside `interactive` — it is required, and omitting it returns `400 Invalid discriminator value. Expected 'button' | 'list'`.\n\nButtons do not render on this protocol. The recipient sees:\n\n    How can we help?\n\n    1. Sales\n    2. Support\n\n    Reply with a number (1-2).\n\nWhen they reply `1`, your message.received webhook carries `interactive.button_reply.id = \"sales\"` — byte-identical to Meta's Cloud API. Write your handler once; only the rendering improves if you migrate.\n\nLimits: 1–3 buttons, titles up to 20 characters."
          }
        },
        {
          "name": "Interactive: list  (numbered menu)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"session_id\": \"{{session_id}}\",\n  \"type\": \"interactive\",\n  \"to\": \"{{phone_number}}\",\n  \"interactive\": {\n    \"type\": \"list\",\n    \"header\": \"Our services\",\n    \"body\": \"Pick what you need and we will connect you.\",\n    \"footer\": \"Reply any time\",\n    \"action\": {\n      \"button\": \"View options\",\n      \"sections\": [\n        {\n          \"title\": \"Support\",\n          \"rows\": [\n            { \"id\": \"order_status\", \"title\": \"Order status\", \"description\": \"Track a recent order\" },\n            { \"id\": \"returns\", \"title\": \"Returns\", \"description\": \"Start a return or refund\" }\n          ]\n        },\n        {\n          \"title\": \"Sales\",\n          \"rows\": [\n            { \"id\": \"pricing\", \"title\": \"Pricing\" },\n            { \"id\": \"demo\", \"title\": \"Book a demo\" }\n          ]\n        }\n      ]\n    }\n  }\n}"
            },
            "url": {
              "raw": "{{base_url}}/v1/messages",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "messages"
              ]
            },
            "description": "A list menu. Same degradation as buttons: the sections are flattened into one numbered menu, and replying with a number produces `interactive.list_reply.id` set to the row id you chose.\n\nRequires **`\"type\": \"list\"`** inside `interactive`.\n\nLimits: up to 10 sections, 10 rows each; row titles 24 characters, descriptions 72, and the `action.button` label 20."
          }
        },
        {
          "name": "Message history",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/messages?session_id={{session_id}}&limit=25",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "messages"
              ],
              "query": [
                {
                  "key": "session_id",
                  "value": "{{session_id}}"
                },
                {
                  "key": "limit",
                  "value": "25"
                }
              ]
            }
          }
        },
        {
          "name": "React",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"session_id\": \"{{session_id}}\",\n  \"emoji\": \"\\ud83d\\udc4d\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/v1/messages/{{wa_message_id}}/reaction",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "messages",
                "{{wa_message_id}}",
                "reaction"
              ]
            }
          }
        },
        {
          "name": "Mark as read",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"session_id\": \"{{session_id}}\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/v1/messages/{{wa_message_id}}/read",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "messages",
                "{{wa_message_id}}",
                "read"
              ]
            }
          }
        },
        {
          "name": "Edit",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"session_id\": \"{{session_id}}\",\n  \"text\": \"Correction: your order ships tomorrow.\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/v1/messages/{{wa_message_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "messages",
                "{{wa_message_id}}"
              ]
            },
            "description": "WhatsApp allows edits for roughly 15 minutes after sending."
          }
        },
        {
          "name": "Delete for everyone",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"session_id\": \"{{session_id}}\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/v1/messages/{{wa_message_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "messages",
                "{{wa_message_id}}"
              ]
            }
          }
        },
        {
          "name": "Typing indicator",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"session_id\": \"{{session_id}}\",\n  \"to\": \"{{phone_number}}\",\n  \"state\": \"typing\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/v1/presence",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "presence"
              ]
            }
          }
        }
      ]
    },
    {
      "name": "Webhooks",
      "description": "Inbound messages and delivery receipts are pushed to your endpoint. Every delivery is signed — verify against the RAW body, since re-serialising parsed JSON will not reproduce the same bytes. Working verification code for Node, PHP and Python is in the guide.",
      "item": [
        {
          "name": "Create webhook",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const body = pm.response.json()",
                  "if (body.id) pm.collectionVariables.set('webhook_id', body.id)",
                  "// The secret is returned once and never again. Save it now.",
                  "if (body.secret) console.log('WEBHOOK SECRET (shown once):', body.secret)"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"url\": \"https://your-app.example.com/webhooks/whatsapp\",\n  \"events\": []\n}"
            },
            "url": {
              "raw": "{{base_url}}/v1/webhooks",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "webhooks"
              ]
            },
            "description": "An empty `events` array subscribes to everything. The response includes `secret` — it is shown ONCE and is required to verify signatures."
          }
        },
        {
          "name": "List webhooks",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/webhooks",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "webhooks"
              ]
            }
          }
        },
        {
          "name": "Send a signed test delivery",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/webhooks/{{webhook_id}}/test",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "webhooks",
                "{{webhook_id}}",
                "test"
              ]
            },
            "description": "Fires a real signed request at your endpoint and reports the status code it returned. The fastest way to prove your signature verification works."
          }
        },
        {
          "name": "Delivery attempts",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/webhooks/{{webhook_id}}/deliveries",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "webhooks",
                "{{webhook_id}}",
                "deliveries"
              ]
            },
            "description": "Failed deliveries retry at 1m, 5m, 30m, 2h, 6h, then dead-letter. 4xx other than 429 are not retried."
          }
        },
        {
          "name": "Delete webhook",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/webhooks/{{webhook_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "webhooks",
                "{{webhook_id}}"
              ]
            }
          }
        }
      ]
    },
    {
      "name": "Media",
      "item": [
        {
          "name": "Upload",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const id = pm.response.json().id",
                  "if (id) pm.collectionVariables.set('media_id', id)"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [],
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "file",
                  "type": "file",
                  "src": []
                }
              ]
            },
            "url": {
              "raw": "{{base_url}}/v1/media",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "media"
              ]
            },
            "description": "Pick a file in the Body tab. Returns a `media_id` you can pass to any media message instead of `link` — upload once, send many times."
          }
        },
        {
          "name": "Download",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/media/{{media_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "media",
                "{{media_id}}"
              ]
            }
          }
        }
      ]
    },
    {
      "name": "Utility",
      "item": [
        {
          "name": "Is this number on WhatsApp?",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/contacts/{{phone_number}}/exists",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "contacts",
                "{{phone_number}}",
                "exists"
              ]
            },
            "description": "Check before sending. Messaging a number with no WhatsApp account returns recipient_not_on_whatsapp."
          }
        },
        {
          "name": "Engine capabilities",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/capabilities",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "capabilities"
              ]
            },
            "description": "What this engine actually supports. Read this rather than assuming — it is the source of truth for whether buttons render."
          }
        },
        {
          "name": "Health  (no auth)",
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/health",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "health"
              ]
            }
          }
        }
      ]
    }
  ]
}
