{
  "info": {
    "name": "5c SMS & Dingo Mail API",
    "_postman_id": "8f4d2a1c-3b6e-4f7a-9c0d-5e8b1a2c3d4e",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
    "description": "# 5c SMS & Dingo Mail API\n\nOne API for business messaging across two channels: SMS through **5c SMS** and email through **Dingo Mail**. Send and track SMS, hold two-way conversations, read inbound messages, and manage opt-outs, sender IDs and virtual numbers. Send transactional and bulk email from your own verified sending domains, with delivery status on every message. Endpoints are secure, JSON over HTTPS, and grouped in the sidebar by area.\n\n## Getting started\n\n**Step 1: Generate your API key.**\n1. Go to https://www.5centsms.com.au/dashboard/api\n2. In **API Key Management**, set a **Key Alias** to identify the key.\n3. Click **Create New API Key**.\n4. Copy the generated **Key ID** and **Key Secret**.\n\nThe same key authenticates both the 5c SMS endpoints and the Dingo Mail email endpoints.\n\n> **Important:** Store your credentials securely. The Key Secret is shown only once and cannot be retrieved again.\n\n**Step 2: Send your first SMS.**\n```bash\ncurl -X POST {{base_url_sms}}/sms \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"key-id\": \"Paste Key ID here\",\n    \"key-secret\": \"Paste Key Secret here\",\n    \"sender\": \"0404123123\",\n    \"to\": \"Paste recipient number here\",\n    \"message\": \"Hello World\"\n  }'\n```\n\n**Step 3: Send your first email.**\n```bash\ncurl -X POST {{base_url_dingo}}/email \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"key-id\": \"Paste Key ID here\",\n    \"key-secret\": \"Paste Key Secret here\",\n    \"SenderEmail\": \"news@mail.example.com\",\n    \"SenderName\": \"Example\",\n    \"Subject\": \"Hello\",\n    \"Text\": \"Hello World\",\n    \"Recipient\": \"Paste recipient email here\"\n  }'\n```\n\nEmail sends from a verified sending domain. Register one with **Create Domain** (under Email Domains) or in the dashboard, then use any address at that domain as the `SenderEmail`.\n\n## Authentication\n\nEvery request authenticates with your `key-id` and `key-secret` supplied **in the JSON request body** (set them once as the `key_id` / `key_secret` collection variables and they apply to every request here). All requests must use **HTTPS** — a plain HTTP request is rejected with `400 HTTPS Required`.\n\n## Responses & errors\n\nEvery response is JSON and carries an `error` field. The HTTP status follows from it:\n\n- **HTTP 200** — success; `error` is an empty string (`\"\"`).\n- **HTTP 400** — the request failed; the reason is in `error`.\n- **HTTP 401** — authentication failed (see the table below).\n- **HTTP 503** — the API is in maintenance; retry shortly.\n\nAn unsupported method/path combination returns `Unsupported method. Please see our API Docs`.\n\n**Authentication errors (HTTP 401):**\n\n| Error | Cause |\n| --- | --- |\n| `Failed (Invalid Key ID)` | The `key-id` is missing or not recognised. |\n| `Failed (Invalid Key Secret)` | The `key-secret` does not match the key. |\n| `Failed (Invalid API ID or Key)` | The key id/secret pair is invalid. |\n| `Failed (Invalid Username or API Key)` | Legacy credential pair is invalid. |\n\n## API hosts\n\nTwo base URLs back this collection, preset as variables:\n\n- `{{base_url_sms}}` (5c SMS): SMS, Conversations, Account, Logins, Sender IDs, Virtual Numbers.\n- `{{base_url_dingo}}` (Dingo Mail): Email and Email Domains.\n\n## Pagination\n\nList endpoints return results **newest-first, one page at a time**. To fetch the next page, pass the **last item's `id`** as the `after` query parameter. `after` is a 24-character hex cursor (a MongoDB ObjectId), **not** an offset or page number.\n\nEach list response includes `next_page` — a ready-to-use relative path already containing the `after` value for the following page — and `count` (items on the current page). Follow `next_page` until it is absent or empty.\n\n`next_page` is a **root-relative** path beginning with `/` (for example `/api/v5/sms?after=...`). Resolve it against the API host (scheme + host of the base URL), not against the current request path, then re-send with the same credentials.\n\nCursor-paginated endpoints (Conversations) return `next_cursor` instead: pass it back as the `before` parameter, and stop when it is null.\n\nPage sizes are fixed per endpoint:\n\n| Endpoint | Page size |\n| --- | --- |\n| List Emails | 20 (override with `limit`, 1–100) |\n| List Inbound Messages | 50 |\n| List Opt-outs | 1000 |\n\nA malformed `after` returns HTTP 400 — `Failed (Invalid Page)` on the SMS / Inbox / Opt-out endpoints, `Invalid after parameter` on List Emails (which also rejects a bad `limit` with `Invalid limit parameter`). Logins, Sender IDs, Virtual Numbers, and Email Domains return full unpaginated lists.\n\n## Message status codes\n\nThe `status` field on a message maps to a human-readable `status_text`. For the full list of status codes and their meanings, see [Message Status List](https://www.5centsms.com.au/web/status).\n\n## Data retention\n\nMessage data is retained for 365 days from the date of processing, after which it is permanently purged. Contact support if you require a different retention timeframe.\n\n## Using Postman\n\nDownload this collection and import it into Postman (**Import → File**, or paste the raw link). Set the `key_id` and `key_secret` collection variables once under the collection's **Variables** tab, then open any request and click **Send**. Each request's **Documentation** pane (the right-hand panel) shows its parameters, response fields, and errors."
  },
  "variable": [
    {
      "key": "base_url_sms",
      "value": "https://www.5centsms.com.au/api/v5"
    },
    {
      "key": "base_url_dingo",
      "value": "https://api.dingomail.com.au/api/v5"
    },
    {
      "key": "key_id",
      "value": ""
    },
    {
      "key": "key_secret",
      "value": ""
    }
  ],
  "item": [
    {
      "name": "SMS",
      "item": [
        {
          "name": "Send SMS",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/sms",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "sms"
              ]
            },
            "description": "Send a single SMS to one or more recipients.\n\n#### Request body parameters\n| Field | Type | Required | Description |\n| --- | --- | --- | --- |\n| `sender` | string | Yes | Sender ID. Max 13 characters; max 11 if alphanumeric ([0-9a-zA-Z] only). |\n| `to` | string | Yes | Recipient number(s), comma-separated. International or 04 format. |\n| `message` | string | Yes | Message content. Hex-encoded UTF-16 when `unicode` is true. |\n| `test` | boolean | No | Simulate the send — no SMS delivered, no credits charged. Default false. |\n| `unicode` | boolean | No | Send as UTF-16; `message` must be hex-encoded. Must be enabled on your account. Deprecated for new accounts; use a Unified Virtual Number for rich text messaging. Default false. |\n| `schedule` | integer | No | UNIX epoch seconds for future delivery, max 356 days ahead. |\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `messages` | array | One entry per recipient. |\n| `messages[].destination` | string | Recipient number. |\n| `messages[].id` | string | Message id. |\n| `messages[].status` | integer | Status code (see Introduction). |\n| `messages[].status_text` | string | Human-readable status. |\n| `messages[].credits` | number | Credits charged. |\n| `messages[].schedule` | integer | Scheduled epoch — only when the message is scheduled (status 1005). |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Failed (Invalid Sender ID)` | 400 | `sender` missing, >13 chars, or alphanumeric >11 chars / invalid characters. |\n| `Failed (Invalid Destination)` | 400 | `to` missing. |\n| `Failed (Invalid Message)` | 400 | `message` missing. |\n\n#### Notes\n- With `unicode`, emoji and non-GSM text are supported; e.g. `Testing🎉` hex-encodes to `00540065007300740069006E0067D83CDF89`.\n- Use `test: true` to verify integration without sending or charging.",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\",\n  \"sender\": \"0404123123\",\n  \"to\": \"0412333555\",\n  \"message\": \"Hello World\",\n  \"test\": false,\n  \"unicode\": false,\n  \"schedule\": 0\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"messages\": [\n    {\n      \"destination\": \"0412333555\",\n      \"id\": \"683554c596937cc4b90f5cf7\",\n      \"status\": 1011,\n      \"status_text\": \"Sending...\",\n      \"credits\": 1\n    }\n  ]\n}"
            },
            {
              "name": "400 Bad Request",
              "code": 400,
              "status": "Bad Request",
              "body": "{\n  \"error\": \"Failed (Invalid Sender ID)\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "List Sent SMS",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/sms",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "sms"
              ],
              "query": [
                {
                  "key": "after",
                  "value": "",
                  "description": "24-hex cursor — the `id` of the last message on the previous page.",
                  "disabled": true
                }
              ]
            },
            "description": "List your sent messages, newest first.\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `messages` | array | Sent messages this page. |\n| `messages[].destination` | string | Recipient number. |\n| `messages[].sender` | string | Sender ID the message was sent from (mobile number, virtual number, or alphanumeric sender ID). |\n| `messages[].id` | string | Message id. |\n| `messages[].status` | integer | Status code (see Introduction). |\n| `messages[].status_text` | string | Human-readable status. |\n| `messages[].message_text` | string | Message content. |\n| `messages[].credits` | number | Credits charged. |\n| `messages[].send_timestamp` | integer | Epoch seconds when sent. |\n| `messages[].delivery_timestamp` | integer | Epoch seconds when delivery confirmed. |\n| `messages[].delivery_network` | string | Delivering network, when known. |\n| `next_page` | string | Relative path for the next page. Omitted when there are no further results. |\n| `count` | integer | Messages on this page. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Failed (Invalid Page)` | 400 | `after` is not a valid 24-hex cursor. |\n\n#### Notes\n- See **Pagination** in the Introduction for the `after` cursor convention.\n- `next_page` is omitted when there are no further results.",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"messages\": [\n    {\n      \"destination\": \"0412333555\",\n      \"sender\": \"5CENTSMS\",\n      \"id\": \"683554c596937cc4b90f5cf7\",\n      \"status\": 1002,\n      \"status_text\": \"Sent (Delivery Confirmed)\",\n      \"message_text\": \"Hello World\",\n      \"credits\": 1,\n      \"send_timestamp\": 1717000000,\n      \"delivery_timestamp\": 1717000020,\n      \"delivery_network\": \"Telstra\"\n    },\n    {\n      \"destination\": \"0412333556\",\n      \"sender\": \"0428000000\",\n      \"id\": \"683554c596937cc4b90f5cf6\",\n      \"status\": 1001,\n      \"status_text\": \"Sent\",\n      \"message_text\": \"Your verification code is 4821\",\n      \"credits\": 1,\n      \"send_timestamp\": 1716999900,\n      \"delivery_timestamp\": 0,\n      \"delivery_network\": \"Optus\"\n    },\n    {\n      \"destination\": \"0412333557\",\n      \"sender\": \"SHOPCO\",\n      \"id\": \"683554c596937cc4b90f5cf5\",\n      \"status\": 1002,\n      \"status_text\": \"Sent (Delivery Confirmed)\",\n      \"message_text\": \"Your order has shipped\",\n      \"credits\": 1,\n      \"send_timestamp\": 1716999800,\n      \"delivery_timestamp\": 1716999830,\n      \"delivery_network\": \"Vodafone\"\n    }\n  ],\n  \"next_page\": \"/api/v5/sms?after=683554c596937cc4b90f5cf5\",\n  \"count\": 3\n}"
            },
            {
              "name": "200 OK (Last Page)",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"messages\": [\n    {\n      \"destination\": \"0412333558\",\n      \"sender\": \"5CENTSMS\",\n      \"id\": \"683554c596937cc4b90f5cf4\",\n      \"status\": 1002,\n      \"status_text\": \"Sent (Delivery Confirmed)\",\n      \"message_text\": \"Appointment reminder\",\n      \"credits\": 1,\n      \"send_timestamp\": 1716999700,\n      \"delivery_timestamp\": 1716999730,\n      \"delivery_network\": \"Telstra\"\n    }\n  ],\n  \"count\": 1\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Get SMS Status",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/sms/:id",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "sms",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "Message id."
                }
              ]
            },
            "description": "Fetch the current status and details of a single sent message.\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `message` | object | The message (same fields as List Sent SMS). |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Failed (Unauthorised)` | 400 | Message not found, or not owned by this account. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"message\": {\n    \"destination\": \"0412333555\",\n    \"sender\": \"5CENTSMS\",\n    \"id\": \"683554c596937cc4b90f5cf7\",\n    \"status\": 1002,\n    \"status_text\": \"Sent (Delivery Confirmed)\",\n    \"message_text\": \"Hello World\",\n    \"credits\": 1,\n    \"send_timestamp\": 1717000000,\n    \"delivery_timestamp\": 1717000020,\n    \"delivery_network\": \"Telstra\"\n  }\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Cancel / Delete SMS",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/sms/:id",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "sms",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "Message id."
                }
              ]
            },
            "description": "Cancel a still-scheduled message (status 1005), or delete a sent message's record.\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Failed (Unauthorised)` | 400 | Message not found, or not owned by this account. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "List Inbound Messages",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/inbox",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "inbox"
              ],
              "query": [
                {
                  "key": "after",
                  "value": "",
                  "description": "24-hex cursor — the `id` of the last message on the previous page.",
                  "disabled": true
                }
              ]
            },
            "description": "List inbound (received) messages, newest first (50 per page).\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `messages` | array | Inbound messages this page. |\n| `messages[].id` | string | Inbound message id. |\n| `messages[].to` | string | Your number that received it. |\n| `messages[].from` | string | Sender number. |\n| `messages[].message` | string | Message content. |\n| `messages[].date` | integer | Epoch seconds received. |\n| `messages[].contact_name` | string | Matched contact name, when known. |\n| `next_page` | string | Relative path for the next page. Always present on this endpoint — stop paging when `messages` is empty (`count` is 0). |\n| `count` | integer | Messages on this page. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Failed (Invalid Page)` | 400 | `after` is not a valid 24-hex cursor. |\n\n#### Notes\n- Page size is fixed at 50. Follow `next_page` (which carries the `after` cursor) until a page comes back with an empty `messages` array.",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"messages\": [\n    {\n      \"id\": \"683554c596937cc4b90f5cf7\",\n      \"to\": \"0400000000\",\n      \"from\": \"0412333555\",\n      \"message\": \"STOP\",\n      \"date\": 1717000000,\n      \"contact_name\": \"Jane\"\n    },\n    {\n      \"id\": \"683554c596937cc4b90f5cf3\",\n      \"to\": \"0400000000\",\n      \"from\": \"0412999888\",\n      \"message\": \"Yes, please book me in\",\n      \"date\": 1716999500,\n      \"contact_name\": \"Tom\"\n    }\n  ],\n  \"next_page\": \"/api/v5/inbox?after=683554c596937cc4b90f5cf3\",\n  \"count\": 2\n}"
            },
            {
              "name": "200 OK (Last Page)",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"messages\": [],\n  \"next_page\": \"/api/v5/inbox?after=\",\n  \"count\": 0\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Delete Inbound Message",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/inbox/:id",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "inbox",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "Inbound message id."
                }
              ]
            },
            "description": "Delete a single inbound message record.\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Failed (Unauthorised)` | 400 | Message not found, or not owned by this account. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "List Opt-outs",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/optouts",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "optouts"
              ],
              "query": [
                {
                  "key": "after",
                  "value": "",
                  "description": "24-hex cursor — the `id` of the last record on the previous page.",
                  "disabled": true
                }
              ]
            },
            "description": "List numbers that have opted out, newest first (1000 per page).\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `numbers` | array | Opted-out numbers this page. |\n| `numbers[].id` | string | Opt-out record id. |\n| `numbers[].number` | string | Opted-out number. |\n| `numbers[].timestamp` | integer | Epoch seconds of opt-out. |\n| `count` | integer | Records on this page. |\n| `next_page` | string | Relative path for the next page. Always present on this endpoint — stop paging when `numbers` is empty (`count` is 0). |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Failed (Invalid Page)` | 400 | `after` is not a valid 24-hex cursor. |\n\n#### Notes\n- Page size is fixed at 1000. Follow `next_page` (which carries the `after` cursor) until a page comes back with an empty `numbers` array.",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"numbers\": [\n    {\n      \"id\": \"683554c596937cc4b90f5cf7\",\n      \"number\": \"0412333555\",\n      \"timestamp\": 1717000000\n    },\n    {\n      \"id\": \"683554c596937cc4b90f5cf2\",\n      \"number\": \"0412333777\",\n      \"timestamp\": 1716999000\n    }\n  ],\n  \"count\": 2,\n  \"next_page\": \"/api/v5/optouts?after=683554c596937cc4b90f5cf2\"\n}"
            },
            {
              "name": "200 OK (Last Page)",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"numbers\": [],\n  \"count\": 0,\n  \"next_page\": \"/api/v5/optouts?after=\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Delete Opt-out",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/optouts/:number",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "optouts",
                ":number"
              ],
              "variable": [
                {
                  "key": "number",
                  "value": "",
                  "description": "The opted-out number to remove."
                }
              ]
            },
            "description": "Remove a number from your opt-out list.\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `message` | string | Confirmation message. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"message\": \"Number removed.\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "List Status Codes",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/statuslist",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "statuslist"
              ]
            },
            "description": "Return the full catalogue of message status codes and their descriptions. The list is global (not account-specific) and matches the codes returned in the `status` field of `GET /sms` and `GET /sms/{id}` and the descriptions used in the Dashboard.\n\nAuthentication is still required (`key-id` / `key-secret` in the request body) so the call is rate-limited and logged like every other v5 endpoint.\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `status_codes` | array | One object per status code. |\n| `status_codes[].code` | number | Numeric status code (e.g. `1002`). |\n| `status_codes[].description` | string | Human-readable description (e.g. `Sent (Delivery Confirmed)`). |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Unsupported method. Please see our API Docs` | 400 | The endpoint was called with `POST` or `DELETE`. |\n\nAuthentication errors are documented once in the collection intro (401 auth-error table).",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"status_codes\": [\n    { \"code\": 1000, \"description\": \"Sending... (Queued)\" },\n    { \"code\": 1001, \"description\": \"Sent\" },\n    { \"code\": 1002, \"description\": \"Sent (Delivery Confirmed)\" }\n  ]\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "Conversations",
      "item": [
        {
          "name": "List Conversations",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/conversations?per_page=50",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "conversations"
              ],
              "query": [
                {
                  "key": "before",
                  "value": "",
                  "disabled": true,
                  "description": "Cursor from a previous response's next_cursor. Omit for the first page."
                },
                {
                  "key": "per_page",
                  "value": "50",
                  "description": "Page size, max 200. Default 50."
                },
                {
                  "key": "q",
                  "value": "",
                  "disabled": true,
                  "description": "Digit search on the contact number."
                },
                {
                  "key": "replies",
                  "value": "1",
                  "disabled": true,
                  "description": "Set to 1 to return only conversations that have an inbound reply."
                },
                {
                  "key": "unread",
                  "value": "1",
                  "disabled": true,
                  "description": "Set to 1 to return only unread conversations."
                }
              ]
            },
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            },
            "description": "List the account's two-way SMS conversations, newest activity first, cursor-paginated. Requires Conversations (threads) enabled and at least one virtual mobile number (VMN).\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `conversations` | array | One entry per conversation, newest activity first. |\n| `conversations[].id` | string | Conversation id. |\n| `conversations[].remote_number` | string | Contact number, normalised to `61` international form. |\n| `conversations[].contact_name` | string | Matched contact name, or empty. |\n| `conversations[].last_message_at` | integer | Epoch seconds of the latest message. |\n| `conversations[].unread` | boolean | Unread flag. |\n| `conversations[].has_inbound` | boolean | True once the contact has replied. |\n| `conversations[].last_local_number` | string | The VMN this thread is on. |\n| `conversations[].created_at` | integer | Epoch seconds the conversation began. |\n| `next_cursor` | string or null | Pass as `before` for the next page; null on the last page. |\n| `count` | integer | Conversations on this page. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Conversations are not enabled for this account. Enable Conversations in your 5cSMS dashboard settings, or contact support to turn on two-way messaging.` | 400 | Account lacks the `ENABLE_THREADS` flag. Response also carries `error_code: \"threads_disabled\"`. |\n| `Conversations require a dedicated virtual mobile number (VMN). Add a VMN in your 5cSMS dashboard under Virtual Numbers, then inbound replies will thread automatically.` | 400 | Threads on but the account has no VMN. Response also carries `error_code: \"no_vmn\"`. |\n| `Failed (Method Not Supported)` | 400 | A POST or DELETE was sent to any `/conversations` route. The endpoint is read-only. |\n\n#### Notes\n- Gate failures return an extra `error_code` field (`threads_disabled` or `no_vmn`) so an integrator can branch programmatically.\n- Follow `next_cursor` (passed back as the `before` parameter) until it is null.\n- `q` matches digits in the remote number; `replies=1` and `unread=1` are independent filters.\n- To send, use `POST /api/v5/sms`. Replies thread into the conversation automatically."
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"conversations\": [\n    {\n      \"id\": \"66b0a1c2d3e4f5a6b7c8d9e0\",\n      \"remote_number\": \"61412345678\",\n      \"contact_name\": \"Jane Smith\",\n      \"last_message_at\": 1749600120,\n      \"unread\": true,\n      \"has_inbound\": true,\n      \"last_local_number\": \"61480000000\",\n      \"created_at\": 1749000000\n    }\n  ],\n  \"next_cursor\": \"1749600120_66b0a1c2d3e4f5a6b7c8d9e0\",\n  \"count\": 1\n}"
            },
            {
              "name": "200 OK (Last Page)",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"conversations\": [\n    {\n      \"id\": \"66a90b1c2d3e4f5a6b7c8d90\",\n      \"remote_number\": \"61412000111\",\n      \"contact_name\": \"\",\n      \"last_message_at\": 1748900000,\n      \"unread\": false,\n      \"has_inbound\": false,\n      \"last_local_number\": \"61480000000\",\n      \"created_at\": 1748900000\n    }\n  ],\n  \"next_cursor\": null,\n  \"count\": 1\n}"
            },
            {
              "name": "400 Bad Request",
              "code": 400,
              "status": "Bad Request",
              "body": "{\n  \"error\": \"Conversations are not enabled for this account. Enable Conversations in your 5cSMS dashboard settings, or contact support to turn on two-way messaging.\",\n  \"error_code\": \"threads_disabled\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Resolve Conversation by Number",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/conversations?number=0412345678",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "conversations"
              ],
              "query": [
                {
                  "key": "number",
                  "value": "0412345678",
                  "description": "Required. Recipient MSISDN in 04..., +61..., or 61... form; normalised to 61 international form."
                }
              ]
            },
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            },
            "description": "Resolve a single conversation by the remote phone number. The number is normalised to `61` international form, so `04...`, `+61...`, and `61...` inputs all resolve.\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `conversation` | object or null | The matching conversation summary (same fields as a List entry, plus `contact_name`), or `null` when the number has no thread. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Conversations are not enabled for this account. Enable Conversations in your 5cSMS dashboard settings, or contact support to turn on two-way messaging.` | 400 | Account lacks the `ENABLE_THREADS` flag. Response also carries `error_code: \"threads_disabled\"`. |\n| `Conversations require a dedicated virtual mobile number (VMN). Add a VMN in your 5cSMS dashboard under Virtual Numbers, then inbound replies will thread automatically.` | 400 | Threads on but the account has no VMN. Response also carries `error_code: \"no_vmn\"`. |\n\n#### Notes\n- A number with no conversation returns `{\"error\": \"\", \"conversation\": null}`. This is a success, not an error.\n- Gate failures return an extra `error_code` field (`threads_disabled` or `no_vmn`)."
          },
          "response": [
            {
              "name": "200 OK (resolved)",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"conversation\": {\n    \"id\": \"66b0a1c2d3e4f5a6b7c8d9e0\",\n    \"remote_number\": \"61412345678\",\n    \"contact_name\": \"Jane Smith\",\n    \"last_message_at\": 1749600120,\n    \"unread\": true,\n    \"has_inbound\": true,\n    \"last_local_number\": \"61480000000\",\n    \"created_at\": 1749000000\n  }\n}"
            },
            {
              "name": "200 OK (no thread)",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"conversation\": null\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Get Conversation Messages",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/conversations/:id",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "conversations",
                ":id"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "50",
                  "disabled": true,
                  "description": "Page size, 1 to 100. Default 50."
                }
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "66b0a1c2d3e4f5a6b7c8d9e0",
                  "description": "Conversation id."
                }
              ]
            },
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            },
            "description": "Fetch the conversation summary plus the most recent page of messages (oldest to newest within the page). Opening a conversation marks it read.\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `conversation` | object | Conversation summary, including `contact_name`. |\n| `messages` | array | Messages, oldest to newest within the page. |\n| `messages[].id` | string | Message id. |\n| `messages[].message` | string | Message text. |\n| `messages[].timestamp` | integer | Epoch seconds. |\n| `messages[].type` | string | `MT` (sent) or `MO` (received). |\n| `messages[].status` | integer or null | Delivery status code (MT only). See the message status table in the Introduction. |\n| `messages[].status_text` | string | Human-readable status, e.g. \"Delivered\" (MT only). |\n| `messages[].scheduled` | boolean | Present and true when the message is scheduled (MT only). |\n| `messages[].cancelled` | boolean | Present and true when the message was cancelled (MT only). |\n| `messages[].cancellable` | boolean | Present and true when the message can still be cancelled (MT only). |\n| `messages[].images` | array | Image URLs (MMS only). |\n| `has_more` | boolean | True when older messages remain (page back with `action=older`). |\n| `oldest_mo_ts` | integer | Oldest received-message watermark on this page (use as `before_mo`). |\n| `oldest_mt_ts` | integer | Oldest sent-message watermark on this page (use as `before_mt`). |\n| `newest_mo_ts` | integer | Newest received-message watermark (use as `since_mo` when polling). |\n| `newest_mt_ts` | integer | Newest sent-message watermark (use as `since_mt` when polling). |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Conversations are not enabled for this account. Enable Conversations in your 5cSMS dashboard settings, or contact support to turn on two-way messaging.` | 400 | Account lacks the `ENABLE_THREADS` flag. Response also carries `error_code: \"threads_disabled\"`. |\n| `Conversations require a dedicated virtual mobile number (VMN). Add a VMN in your 5cSMS dashboard under Virtual Numbers, then inbound replies will thread automatically.` | 400 | Threads on but the account has no VMN. Response also carries `error_code: \"no_vmn\"`. |\n| `Failed (Conversation Not Found)` | 400 | The `id` was not found or is not owned by this account. |\n\n#### Notes\n- Side effect: opening a conversation marks it read (`unread` becomes false).\n- Page back through history with the **Page Older Messages** request, and fetch new messages with the **Poll Conversation** request.\n- To send a reply, use `POST /api/v5/sms`. Replies thread into the conversation automatically."
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"conversation\": {\n    \"id\": \"66b0a1c2d3e4f5a6b7c8d9e0\",\n    \"remote_number\": \"61412345678\",\n    \"contact_name\": \"Jane Smith\",\n    \"last_message_at\": 1749600120,\n    \"unread\": false,\n    \"has_inbound\": true,\n    \"last_local_number\": \"61480000000\",\n    \"created_at\": 1749000000\n  },\n  \"messages\": [\n    {\n      \"id\": \"66a0a1c2d3e4f5a6b7c8d9e0\",\n      \"message\": \"Your booking is confirmed\",\n      \"timestamp\": 1749600060,\n      \"type\": \"MT\",\n      \"status\": 1002,\n      \"status_text\": \"Sent (Delivery Confirmed)\"\n    },\n    {\n      \"id\": \"66a1a1c2d3e4f5a6b7c8d9e0\",\n      \"message\": \"Yes please\",\n      \"timestamp\": 1749600120,\n      \"type\": \"MO\"\n    }\n  ],\n  \"has_more\": false,\n  \"oldest_mo_ts\": 1749600120,\n  \"oldest_mt_ts\": 1749600060,\n  \"newest_mo_ts\": 1749600120,\n  \"newest_mt_ts\": 1749600060\n}"
            },
            {
              "name": "400 Bad Request",
              "code": 400,
              "status": "Bad Request",
              "body": "{\n  \"error\": \"Failed (Conversation Not Found)\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Poll Conversation",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/conversations/:id?action=poll&since_mo=1749600120&since_mt=1749600060",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "conversations",
                ":id"
              ],
              "query": [
                {
                  "key": "action",
                  "value": "poll",
                  "description": "Required. Set to poll."
                },
                {
                  "key": "since_mo",
                  "value": "1749600120",
                  "description": "Required. Received-message watermark (epoch seconds); use the prior response's newest_mo_ts."
                },
                {
                  "key": "since_mt",
                  "value": "1749600060",
                  "description": "Required. Sent-message watermark (epoch seconds); use the prior response's newest_mt_ts."
                }
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "66b0a1c2d3e4f5a6b7c8d9e0",
                  "description": "Conversation id."
                }
              ]
            },
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            },
            "description": "Return only messages newer than the supplied watermarks. Use the `newest_mo_ts` and `newest_mt_ts` from the initial load (or the previous poll) as `since_mo` and `since_mt`. A poll that returns new messages marks the conversation read.\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `messages` | array | Only messages newer than the watermarks (same message shape as **Get Conversation Messages**). |\n| `now` | integer | Server epoch seconds, for poll cadence. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Conversations are not enabled for this account. Enable Conversations in your 5cSMS dashboard settings, or contact support to turn on two-way messaging.` | 400 | Account lacks the `ENABLE_THREADS` flag. Response also carries `error_code: \"threads_disabled\"`. |\n| `Conversations require a dedicated virtual mobile number (VMN). Add a VMN in your 5cSMS dashboard under Virtual Numbers, then inbound replies will thread automatically.` | 400 | Threads on but the account has no VMN. Response also carries `error_code: \"no_vmn\"`. |\n| `Failed (Conversation Not Found)` | 400 | The `id` was not found or is not owned by this account. |\n| `Failed (Invalid Watermark)` | 400 | `since_mo` or `since_mt` is missing or not a digit string. |\n\n#### Notes\n- Side effect: a poll that returns one or more new messages marks the conversation read.\n- `since_mo` and `since_mt` must be integer epoch-second values."
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"messages\": [\n    {\n      \"id\": \"66a2a1c2d3e4f5a6b7c8d9e0\",\n      \"message\": \"On my way\",\n      \"timestamp\": 1749600300,\n      \"type\": \"MO\"\n    }\n  ],\n  \"now\": 1749600305\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Page Older Messages",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/conversations/:id?action=older&before_mo=1749600120&before_mt=1749600060",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "conversations",
                ":id"
              ],
              "query": [
                {
                  "key": "action",
                  "value": "older",
                  "description": "Required. Set to older."
                },
                {
                  "key": "before_mo",
                  "value": "1749600120",
                  "description": "Required. Use the prior response's oldest_mo_ts."
                },
                {
                  "key": "before_mt",
                  "value": "1749600060",
                  "description": "Required. Use the prior response's oldest_mt_ts."
                },
                {
                  "key": "limit",
                  "value": "50",
                  "disabled": true,
                  "description": "Page size, 1 to 100. Default 50."
                }
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "66b0a1c2d3e4f5a6b7c8d9e0",
                  "description": "Conversation id."
                }
              ]
            },
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            },
            "description": "Page back through history older than the supplied cursors. Use the `oldest_mo_ts` and `oldest_mt_ts` from the previous page as `before_mo` and `before_mt`.\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `messages` | array | The older page (same message shape as **Get Conversation Messages**). |\n| `has_more` | boolean | True when still-older messages remain. |\n| `oldest_mo_ts` | integer | Oldest received-message watermark on this page (use as the next `before_mo`). |\n| `oldest_mt_ts` | integer | Oldest sent-message watermark on this page (use as the next `before_mt`). |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Conversations are not enabled for this account. Enable Conversations in your 5cSMS dashboard settings, or contact support to turn on two-way messaging.` | 400 | Account lacks the `ENABLE_THREADS` flag. Response also carries `error_code: \"threads_disabled\"`. |\n| `Conversations require a dedicated virtual mobile number (VMN). Add a VMN in your 5cSMS dashboard under Virtual Numbers, then inbound replies will thread automatically.` | 400 | Threads on but the account has no VMN. Response also carries `error_code: \"no_vmn\"`. |\n| `Failed (Conversation Not Found)` | 400 | The `id` was not found or is not owned by this account. |\n| `Failed (Invalid Watermark)` | 400 | `before_mo` or `before_mt` is missing or not a digit string. |\n\n#### Notes\n- This request does not mark the conversation read (unlike the initial load and a poll that returns new messages).\n- Keep paging with the returned `oldest_mo_ts` / `oldest_mt_ts` until `has_more` is `false` — the final page comes back with an empty `messages` array and both watermarks 0."
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"messages\": [\n    {\n      \"id\": \"669f0a1c2d3e4f5a6b7c8d90\",\n      \"message\": \"Earlier message\",\n      \"timestamp\": 1749500050,\n      \"type\": \"MO\"\n    }\n  ],\n  \"has_more\": true,\n  \"oldest_mo_ts\": 1749500050,\n  \"oldest_mt_ts\": 1749500000\n}"
            },
            {
              "name": "200 OK (No Older Messages)",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"messages\": [],\n  \"has_more\": false,\n  \"oldest_mo_ts\": 0,\n  \"oldest_mt_ts\": 0\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "Email",
      "item": [
        {
          "name": "Send Email",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_dingo}}/email",
              "host": [
                "{{base_url_dingo}}"
              ],
              "path": [
                "email"
              ]
            },
            "description": "Send a transactional email. The sender must be on a registered, validated domain (see Email Domains).\n\n#### Request body parameters\n| Field | Type | Required | Description |\n| --- | --- | --- | --- |\n| `SenderEmail` | string | Yes | From address. Must be on a registered, validated domain. |\n| `Subject` | string | Yes | Email subject. |\n| `Text` | string | Yes | Plain-text body. |\n| `Html` | string | No | HTML body. Defaults to `Text` if omitted. |\n| `SenderName` | string | No | Display name for the From address. |\n| `Recipient` | string or array | Conditional | To recipient(s). At least one of Recipient / CC / BCC is required; combined max 50. |\n| `CC` | string or array | Conditional | CC recipient(s). |\n| `BCC` | string or array | Conditional | BCC recipient(s). |\n| `ReplyTo` | string | No | Reply-To address. |\n| `Attachments` | array | No | Array of `{url, filename?}`. Requires the DINGO_ATTACHMENT account flag; max 5. |\n| `test` | boolean | No | Sandbox mode. When true, the request is fully validated (sender authorization, recipient limits, attachments) and a message record is created, but the email is **not** sent and **does not** consume email quota. Assessed as true unless the parameter is omitted or is one of these values: `false`, `\"false\"`, `0`, `\"0\"`. |\n\n**Test mode:** pass `test: true` to validate an email end-to-end without sending or billing. A `v2_emails` record is still created (visible via `GET /api/v5/email` with `Status: test`), but SES is never called.\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `messages` | array | One entry per send. |\n| `messages[].Id` | string | Email id. |\n| `messages[].Status` | string | Email status. |\n| `messages[].Recipients` | object | `{To, CC, TotalCount}` — BCC omitted for privacy. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Email not enabled. Please contact us.` | 400 | Email is not enabled on this account. |\n| `Required parameter: {field} missing` | 400 | A required field (SenderEmail / Subject / Text) is missing. |\n| `Required parameter: At least one of Recipient, CC, or BCC must be provided` | 400 | No recipient supplied. |\n| `Too many recipients. Maximum 50 allowed, got {n}.` | 400 | Combined recipients exceed 50. |\n| `Invalid email addresses: {list}` | 400 | One or more recipient addresses are malformed. |\n| `Invalid Sender Email` | 400 | `SenderEmail` is malformed. |\n| `Invalid Sender Domain` | 400 | Sender domain is not registered or validated on this account. |\n| `Invalid Sender Domain or Mailbox` | 400 | For hosted-mailbox senders, the address is not one of the account's active mailboxes. |\n| `Attachments not enabled for this account` | 403 | Attachments require the DINGO_ATTACHMENT flag. |\n| `Maximum {n} attachments allowed` | 400 | Too many attachments (max 5). |\n| `Each attachment must have a url field` | 400 | An attachment entry is missing `url`. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\",\n  \"SenderEmail\": \"news@mail.example.com\",\n  \"SenderName\": \"Example\",\n  \"Subject\": \"Welcome\",\n  \"Text\": \"Hello there\",\n  \"Html\": \"<p>Hello there</p>\",\n  \"Recipient\": \"jane@example.com\",\n  \"ReplyTo\": \"support@example.com\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"messages\": [\n    {\n      \"Id\": \"683554c596937cc4b90f5cf7\",\n      \"Status\": \"queued\",\n      \"Recipients\": {\n        \"To\": [\n          \"jane@example.com\"\n        ],\n        \"CC\": [],\n        \"TotalCount\": 1\n      }\n    }\n  ]\n}"
            },
            {
              "name": "200 OK (test mode)",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"messages\": [\n    {\n      \"Id\": \"6650f1a2b3c4d5e6f7a8b9c0\",\n      \"Status\": \"test\",\n      \"Recipients\": {\n        \"To\": [\n          \"someone@example.com\"\n        ],\n        \"CC\": [],\n        \"TotalCount\": 1\n      }\n    }\n  ]\n}"
            },
            {
              "name": "400 Bad Request",
              "code": 400,
              "status": "Bad Request",
              "body": "{\n  \"error\": \"Invalid Sender Domain\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "List Emails",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_dingo}}/email",
              "host": [
                "{{base_url_dingo}}"
              ],
              "path": [
                "email"
              ],
              "query": [
                {
                  "key": "after",
                  "value": "",
                  "description": "24-hex cursor — the `id` of the last email on the previous page.",
                  "disabled": true
                },
                {
                  "key": "limit",
                  "value": "20",
                  "description": "Page size, 1–100. Default 20.",
                  "disabled": true
                }
              ]
            },
            "description": "List sent emails, newest first.\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `emails` | array | Emails this page. |\n| `emails[].id` | string | Email id. |\n| `emails[].status` | string | Email status. |\n| `emails[].from` | string | From address. |\n| `emails[].sender_name` | string | From display name. |\n| `emails[].to` | array | To recipients. |\n| `emails[].cc` | array | CC recipients. |\n| `emails[].subject` | string | Subject. |\n| `emails[].created_at` | integer | Epoch seconds created. |\n| `emails[].method` | string | Send method. |\n| `count` | integer | Emails on this page. |\n| `next_page` | string | Relative path for the next page — present only when `count >= limit`. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Invalid after parameter` | 400 | `after` is not a valid 24-hex cursor. |\n| `Invalid limit parameter` | 400 | `limit` is outside 1–100. |\n\n#### Notes\n- `next_page` appears only when a full page is returned (`count >= limit`); follow it until it is absent. It echoes any non-default `limit`. The paging examples below use `limit=2` to stay short.",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"emails\": [\n    {\n      \"id\": \"683554c596937cc4b90f5cf7\",\n      \"status\": \"sent\",\n      \"from\": \"news@mail.example.com\",\n      \"sender_name\": \"Example\",\n      \"to\": [\n        \"jane@example.com\"\n      ],\n      \"cc\": [],\n      \"subject\": \"Welcome\",\n      \"created_at\": 1717000000,\n      \"method\": \"api\"\n    },\n    {\n      \"id\": \"683554c596937cc4b90f5cf5\",\n      \"status\": \"sent\",\n      \"from\": \"news@mail.example.com\",\n      \"sender_name\": \"Example\",\n      \"to\": [\n        \"tom@example.com\"\n      ],\n      \"cc\": [],\n      \"subject\": \"Your receipt\",\n      \"created_at\": 1716999000,\n      \"method\": \"api\"\n    }\n  ],\n  \"count\": 2,\n  \"next_page\": \"/api/v5/email?after=683554c596937cc4b90f5cf5&limit=2\"\n}"
            },
            {
              "name": "200 OK (Last Page)",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"emails\": [\n    {\n      \"id\": \"683554c596937cc4b90f5cf1\",\n      \"status\": \"sent\",\n      \"from\": \"news@mail.example.com\",\n      \"sender_name\": \"Example\",\n      \"to\": [\n        \"amy@example.com\"\n      ],\n      \"cc\": [],\n      \"subject\": \"Order shipped\",\n      \"created_at\": 1716998000,\n      \"method\": \"api\"\n    }\n  ],\n  \"count\": 1\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Get Email",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_dingo}}/email/:id",
              "host": [
                "{{base_url_dingo}}"
              ],
              "path": [
                "email",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "Email id."
                }
              ]
            },
            "description": "Fetch a single email, including its full text and HTML bodies.\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `email` | object | id, status, from, sender_name, to, cc, subject, text, html, reply_to, created_at, method. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Invalid email ID` | 400 | `id` is not a valid 24-hex id. |\n| `Email not found` | 400 | No such email, or not owned by this account. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"email\": {\n    \"id\": \"683554c596937cc4b90f5cf7\",\n    \"status\": \"sent\",\n    \"from\": \"news@mail.example.com\",\n    \"sender_name\": \"Example\",\n    \"to\": [\n      \"jane@example.com\"\n    ],\n    \"cc\": [],\n    \"subject\": \"Welcome\",\n    \"text\": \"Hello there\",\n    \"html\": \"<p>Hello there</p>\",\n    \"reply_to\": \"support@example.com\",\n    \"created_at\": 1717000000,\n    \"method\": \"api\"\n  }\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "List Inbound Emails",
          "request": {
            "method": "GET",
            "header": [
              { "key": "Content-Type", "value": "application/json" }
            ],
            "url": {
              "raw": "{{base_url_dingo}}/inboundemail",
              "host": [ "{{base_url_dingo}}" ],
              "path": [ "inboundemail" ],
              "query": [
                { "key": "after", "value": "", "description": "24-hex cursor — the `id` of the last inbound email on the previous page.", "disabled": true },
                { "key": "limit", "value": "20", "description": "Page size, 1–1000. Default 20.", "disabled": true },
                { "key": "mailbox_address", "value": "", "description": "Optional. Restrict to inbound routed to this hosted mailbox address (lowercased). Omit for all inbound.", "disabled": true }
              ]
            },
            "description": "List received (inbound) emails for the account, newest first. Returns both hosted-mailbox-routed and custom-domain-routed inbound mail; pass `mailbox_address` to restrict to one hosted mailbox.\n\n#### Request body\n| Field | Type | Required | Description |\n| --- | --- | --- | --- |\n| `key-id` | string | Yes | API key id (see intro auth). |\n| `key-secret` | string | Yes | API key secret (see intro auth). |\n\n#### Query parameters\n| Field | Type | Required | Description |\n| --- | --- | --- | --- |\n| `after` | string | No | 24-hex cursor; `id` of the last row on the previous page. |\n| `limit` | integer | No | Page size 1–1000, default 20. |\n| `mailbox_address` | string | No | Restrict to one hosted mailbox address. |\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `emails` | array | Inbound emails this page, newest first. |\n| `emails[].id` | string | Inbound email id. |\n| `emails[].from` | string | Sender address. |\n| `emails[].to` | string | Raw To header the mail was delivered to. |\n| `emails[].subject` | string | Subject. |\n| `emails[].has_attachments` | boolean | True if the email has any attachment (inline or file). |\n| `emails[].mailbox_address` | string | Present only for hosted-mailbox-routed inbound. |\n| `emails[].created_at` | integer | Epoch seconds received. |\n| `count` | integer | Emails on this page. |\n| `next_page` | string | Relative path for the next page — present only when `count >= limit`. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Invalid after parameter` | 400 | `after` is not a valid 24-hex cursor. |\n| `Invalid limit parameter` | 400 | `limit` is non-numeric. |\n| `Invalid mailbox_address parameter` | 400 | `mailbox_address` is not a string. |\n| `Unsupported method` | 400 | The route was called with POST or DELETE (read-only). |\n\n#### Notes\n- `next_page` appears only when a full page is returned (`count >= limit`); follow it until it is absent. It echoes any non-default `limit` and the `mailbox_address` filter. The paging examples below use `limit=2` to stay short.",
            "body": {
              "mode": "raw",
              "options": { "raw": { "language": "json" } },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"emails\": [\n    {\n      \"id\": \"683554c596937cc4b90f5cf7\",\n      \"from\": \"jane@example.com\",\n      \"to\": \"support@mail.example.com\",\n      \"subject\": \"Re: Welcome\",\n      \"has_attachments\": false,\n      \"created_at\": 1717000000\n    },\n    {\n      \"id\": \"683554c596937cc4b90f5cf5\",\n      \"from\": \"tom@example.com\",\n      \"to\": \"hello@mail.example.com\",\n      \"subject\": \"Invoice attached\",\n      \"has_attachments\": true,\n      \"mailbox_address\": \"hello@mail.example.com\",\n      \"created_at\": 1716999000\n    }\n  ],\n  \"count\": 2,\n  \"next_page\": \"/api/v5/inboundemail?after=683554c596937cc4b90f5cf5&limit=2\"\n}"
            },
            {
              "name": "200 OK (Last Page)",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"emails\": [\n    {\n      \"id\": \"683554c596937cc4b90f5cf1\",\n      \"from\": \"amy@example.com\",\n      \"to\": \"support@mail.example.com\",\n      \"subject\": \"Question about my order\",\n      \"has_attachments\": false,\n      \"created_at\": 1716998000\n    }\n  ],\n  \"count\": 1\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Get Inbound Email",
          "request": {
            "method": "GET",
            "header": [
              { "key": "Content-Type", "value": "application/json" }
            ],
            "url": {
              "raw": "{{base_url_dingo}}/inboundemail/:id",
              "host": [ "{{base_url_dingo}}" ],
              "path": [ "inboundemail", ":id" ],
              "variable": [
                { "key": "id", "value": "", "description": "Inbound email id." }
              ]
            },
            "description": "Fetch a single received email, including its full text and HTML bodies and attachment metadata. Scoped to the owning account.\n\n#### Request body\n| Field | Type | Required | Description |\n| --- | --- | --- | --- |\n| `key-id` | string | Yes | API key id (see intro auth). |\n| `key-secret` | string | Yes | API key secret (see intro auth). |\n\n#### Path parameters\n| Field | Type | Required | Description |\n| --- | --- | --- | --- |\n| `id` | string | Yes | 24-hex inbound email id. |\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `email.id` | string | Inbound email id. |\n| `email.from` | string | Sender address. |\n| `email.to` | string | Raw To header. |\n| `email.subject` | string | Subject. |\n| `email.text` | string | Plain-text body. |\n| `email.html` | string | HTML body (inline `cid:` images already rewritten to S3 URLs). |\n| `email.attachments` | array | Attachment metadata. |\n| `email.attachments[].filename` | string | File name. |\n| `email.attachments[].content_type` | string | MIME type. |\n| `email.attachments[].size` | integer | Bytes. |\n| `email.attachments[].url` | string | Presigned S3 download URL, valid for 1 hour. |\n| `email.attachments[].content_id` | string | MIME Content-ID (inline images). |\n| `email.attachments[].is_inline` | boolean | True for embedded inline images. |\n| `email.mailbox_address` | string | Present only for hosted-mailbox-routed inbound. |\n| `email.mailbox_id` | string | Present only with `mailbox_address`. |\n| `email.created_at` | integer | Epoch seconds received. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Invalid inbound email ID` | 400 | `id` is not a valid 24-hex id. |\n| `Inbound email not found` | 400 | No such inbound email, or not owned by this account. |\n| `Unsupported method` | 400 | The route was called with POST or DELETE (read-only). |",
            "body": {
              "mode": "raw",
              "options": { "raw": { "language": "json" } },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"email\": {\n    \"id\": \"683554c596937cc4b90f5cf7\",\n    \"from\": \"jane@example.com\",\n    \"to\": \"support@mail.example.com\",\n    \"subject\": \"Re: Welcome\",\n    \"text\": \"Thanks!\",\n    \"html\": \"<p>Thanks!</p>\",\n    \"attachments\": [],\n    \"created_at\": 1717000000\n  }\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "Account",
      "item": [
        {
          "name": "Get Balance",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/balance",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "balance"
              ]
            },
            "description": "Get your account's credit balance and billing mode.\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `balance` | object | Balance details. |\n| `balance.postpaid` | boolean | True if the account is postpaid. |\n| `balance.credits` | number | Available credits. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"balance\": {\n    \"postpaid\": false,\n    \"credits\": 1234.5\n  }\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Pre-warm Send Queue",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/prewarm",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "prewarm"
              ]
            },
            "description": "Pre-warm the send queue ahead of a large campaign. Requires the QUEUE_PREWARM account flag; usable once per 60 minutes.\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `message` | string | Confirmation message. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Account does not have queue pre-warming enabled` | 400 | QUEUE_PREWARM flag not set on the account. |\n| `Queue was already pre-warmed recently. Please wait {n} minutes before trying again.` | 400 | Pre-warmed within the last 60 minutes. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"message\": \"Queue pre-warming initiated.\"\n}"
            },
            {
              "name": "400 Bad Request",
              "code": 400,
              "status": "Bad Request",
              "body": "{\n  \"error\": \"Account does not have queue pre-warming enabled\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "Logins",
      "item": [
        {
          "name": "Create Login",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/logins",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "logins"
              ]
            },
            "description": "Create (or re-invite) a sub-login on your account.\n\n#### Request body parameters\n| Field | Type | Required | Description |\n| --- | --- | --- | --- |\n| `name` | string | Yes | Login's display name. |\n| `email` | string | Yes | Login's email address. |\n| `role` | string | No | One of `admin`, `manager`, `sender`, `reporting`. Default `admin`. |\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `login_id` | string | The login's id. |\n| `is_new` | boolean | True if a new login was created. |\n| `message` | string | Confirmation message. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Missing required field: name` | 400 | `name` not supplied. |\n| `Missing required field: email` | 400 | `email` not supplied. |\n| `Invalid email address` | 400 | `email` is malformed. |\n| `Invalid role. Must be one of: admin, manager, sender, reporting` | 400 | `role` is not an allowed value. |\n| `Login already exists on this account` | 400 | A login with that email already exists here. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\",\n  \"name\": \"Jane Smith\",\n  \"email\": \"jane@example.com\",\n  \"role\": \"admin\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"login_id\": \"683554c596937cc4b90f5cf7\",\n  \"is_new\": true,\n  \"message\": \"Login created\"\n}"
            },
            {
              "name": "400 Bad Request",
              "code": 400,
              "status": "Bad Request",
              "body": "{\n  \"error\": \"Invalid role. Must be one of: admin, manager, sender, reporting\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "List Logins",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/logins",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "logins"
              ]
            },
            "description": "List all logins on your account.\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `logins` | array | Logins on the account. |\n| `logins[].login_id` | string | Login id. |\n| `logins[].name` | string | Display name. |\n| `logins[].email` | string | Email address. |\n| `logins[].role` | string | Role. |\n| `count` | integer | Number of logins. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"logins\": [\n    {\n      \"login_id\": \"683554c596937cc4b90f5cf7\",\n      \"name\": \"Jane Smith\",\n      \"email\": \"jane@example.com\",\n      \"role\": \"admin\"\n    }\n  ],\n  \"count\": 1\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Change Login Role",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/logins/:login_id",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "logins",
                ":login_id"
              ],
              "variable": [
                {
                  "key": "login_id",
                  "value": "",
                  "description": "Login id."
                }
              ]
            },
            "description": "Change a login's role.\n\n#### Request body parameters\n| Field | Type | Required | Description |\n| --- | --- | --- | --- |\n| `action` | string | Yes | Must be `change_role`. |\n| `role` | string | Yes | One of `admin`, `manager`, `sender`, `reporting`. |\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `login_id` | string | The login's id. |\n| `role` | string | The new role. |\n| `message` | string | Confirmation message. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Invalid login_id format` | 400 | `login_id` is not a valid id. |\n| `Missing required field: role` | 400 | `role` not supplied. |\n| `Invalid role. Must be one of: admin, manager, sender, reporting` | 400 | `role` is not an allowed value. |\n| `Login not found on this account` | 400 | No such login on this account. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\",\n  \"action\": \"change_role\",\n  \"role\": \"manager\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"login_id\": \"683554c596937cc4b90f5cf7\",\n  \"role\": \"manager\",\n  \"message\": \"Role updated\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Delete Login",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/logins/:login_id",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "logins",
                ":login_id"
              ],
              "variable": [
                {
                  "key": "login_id",
                  "value": "",
                  "description": "Login id."
                }
              ]
            },
            "description": "Remove a login from your account.\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `login_id` | string | The removed login's id. |\n| `message` | string | Confirmation message. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Invalid login_id format` | 400 | `login_id` is not a valid id. |\n| `Login not found on this account` | 400 | No such login on this account. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"login_id\": \"683554c596937cc4b90f5cf7\",\n  \"message\": \"Login removed from account\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "Campaigns",
      "item": [
        {
          "name": "Create campaign",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/campaigns",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "campaigns"
              ]
            },
            "description": "Create an SMS, email, or combined campaign. A campaign is created in `DRAFT` status. In a single call you can also set the campaign's content, add recipients inline, and send or schedule it — or do each step separately with the other endpoints in this folder.\n\nCampaigns are an account resource on the 5c SMS host (`{{base_url_sms}}`) for both channels.\n\n#### Request body parameters\n| Field | Type | Required | Description |\n| --- | --- | --- | --- |\n| `type` | string | No | `sms`, `email`, or `both`. Default `sms`. |\n| `title` | string | No | Campaign title. |\n| `sms_sender_id` | string | No | SMS sender ID or virtual number (SMS / both). |\n| `sms_body` | string | No | SMS message body (SMS / both). |\n| `sms_optout_enabled` | boolean | No | Append an opt-out link to the SMS. |\n| `sms_unicode_enabled` | boolean | No | Send the SMS as unicode. Requires the `UTF16` account feature. |\n| `sender_email` | string | No | From address; must be at one of your validated sending domains (email / both). |\n| `sender_name` | string | No | From name (email / both). |\n| `subject` | string | No | Email subject line (email / both). |\n| `email_body` | string | No | Email HTML body (email / both). |\n| `email_text` | string | No | Email plain-text body (email / both). |\n| `numbers` | string or array | No | Phone numbers to add as individual recipients. Array, or a comma/newline-delimited string. Max 100 individual recipients per call (numbers + emails combined). |\n| `emails` | string or array | No | Email addresses to add as individual recipients. Same format and cap as `numbers`. |\n| `contact_list_ids` | string or array | No | Contact list ids to add as recipient sources. Each must be a 24-char hex id you own. |\n| `send` | boolean | No | If true, send the campaign immediately after it is built. |\n| `schedule` | integer or string | No | Unix timestamp or a `strtotime`-parseable string. Schedules the send for that time; supplying it implies `send`. |\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. Carries the send error if an inline `send`/`schedule` failed its gating (the campaign is still created). |\n| `campaign_id` | string | The new campaign's id. |\n| `status` | string | `DRAFT`, or `SENDING` / `SCHEDULED` if sent inline. |\n| `recipient_count` | integer | Recipients currently on the campaign. |\n| `recipients_added` | integer | Individual recipients added (present only when inline recipients were supplied). |\n| `recipient_errors` | array | Per-recipient errors for skipped individuals (present only when inline recipients were supplied). |\n| `send` | object | The send result (present only when `send`/`schedule` was requested): `error`, `campaign_id`, `status`, `message`. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Invalid campaign type` | 400 | `type` is not `sms`, `email`, or `both`. |\n| `Unicode requires the UTF16 feature on your account` | 400 | `sms_unicode_enabled` set without the `UTF16` account feature. |\n\n> **Note — inline send is not atomic.** When you pass `send`/`schedule` and the send gating fails (see the **Send / schedule campaign** endpoint for those errors), the campaign persists as `DRAFT`, the top-level `error` carries the send failure, and `send` holds the detail. Fix the issue and call the Send endpoint — do not re-create.",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\",\n  \"type\": \"sms\",\n  \"title\": \"July promo\",\n  \"sms_sender_id\": \"EXAMPLE\",\n  \"sms_body\": \"Hello from Acme!\",\n  \"sms_optout_enabled\": true,\n  \"contact_list_ids\": [\"683554c596937cc4b90f5cf7\"],\n  \"send\": false\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"campaign_id\": \"665f1a2b3c4d5e6f7a8b9c0d\",\n  \"status\": \"DRAFT\",\n  \"recipient_count\": 240,\n  \"recipients_added\": 0,\n  \"recipient_errors\": []\n}"
            },
            {
              "name": "400 Bad Request",
              "code": 400,
              "status": "Bad Request",
              "body": "{\n  \"error\": \"Invalid campaign type\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "List campaigns",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/campaigns",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "campaigns"
              ]
            },
            "description": "List your account's campaigns, most-recent-first. Returns up to the 200 most recent campaigns; older campaigns are purged 365 days after creation.\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `campaigns` | array | Campaigns, newest first. |\n| `campaigns[].campaign_id` | string | Campaign id. |\n| `campaigns[].title` | string | Campaign title. |\n| `campaigns[].status` | string | `DRAFT`, `SCHEDULED`, `SENDING`, or `SENT`. |\n| `campaigns[].type` | array | Channels: any of `sms`, `email`. |\n| `campaigns[].recipient_count` | integer | Recipients on the campaign. |\n| `campaigns[].schedule` | integer | Scheduled send time (unix), or null. |\n| `count` | integer | Number of campaigns returned. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"campaigns\": [\n    {\n      \"campaign_id\": \"665f1a2b3c4d5e6f7a8b9c0d\",\n      \"title\": \"July promo\",\n      \"status\": \"SENT\",\n      \"type\": [\"sms\"],\n      \"recipient_count\": 240,\n      \"schedule\": null\n    }\n  ],\n  \"count\": 1\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Get campaign",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/campaigns/:id",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "campaigns",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "Campaign id (24-char hex)."
                }
              ]
            },
            "description": "Fetch one campaign: its settings, recipients, and combined SMS/email delivery stats.\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `campaign_id` | string | Campaign id. |\n| `title` | string | Campaign title. |\n| `status` | string | `DRAFT`, `SCHEDULED`, `SENDING`, or `SENT`. |\n| `type` | array | Channels: any of `sms`, `email`. |\n| `recipient_count` | integer | Recipients on the campaign. |\n| `schedule` | integer | Scheduled send time (unix), or null. |\n| `sms` | object | SMS settings (present for `sms`/`both`): `sender_id`, `body`, `optout_enabled`, `unicode_enabled`. |\n| `email` | object | Email settings (present for `email`/`both`): `sender_email`, `sender_name`, `subject`, `has_body`. |\n| `recipients` | array | Recipient rows: `id`, `type` (`individual`/`contact_list`), and either `recipient_type`+`value` or `contact_list_id`+`contact_list_name`+`contact_list_count`, plus `created`. |\n| `stats` | object | Delivery stats: `type` (`sms`/`email`/`both`/`none`); `sms` and/or `email` count objects. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Invalid campaign_id format` | 400 | The path id is not a 24-char hex id. |\n| `Campaign not found` | 400 | No such campaign on this account. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"campaign_id\": \"665f1a2b3c4d5e6f7a8b9c0d\",\n  \"title\": \"July promo\",\n  \"status\": \"SENT\",\n  \"type\": [\"sms\"],\n  \"recipient_count\": 240,\n  \"schedule\": null,\n  \"sms\": {\n    \"sender_id\": \"EXAMPLE\",\n    \"body\": \"Hello from Acme!\",\n    \"optout_enabled\": true,\n    \"unicode_enabled\": false\n  },\n  \"recipients\": [\n    {\n      \"id\": \"665f1a2b3c4d5e6f7a8b9c11\",\n      \"type\": \"contact_list\",\n      \"contact_list_id\": \"683554c596937cc4b90f5cf7\",\n      \"contact_list_name\": \"VIP\",\n      \"contact_list_count\": 240,\n      \"created\": 1721700000\n    }\n  ],\n  \"stats\": {\n    \"type\": \"sms\",\n    \"sms\": { \"total\": 240, \"queued\": 0, \"sent\": 40, \"delivered\": 195, \"failed\": 5 }\n  }\n}"
            },
            {
              "name": "400 Bad Request",
              "code": 400,
              "status": "Bad Request",
              "body": "{\n  \"error\": \"Campaign not found\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Update campaign",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/campaigns/:id",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "campaigns",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "Campaign id (24-char hex)."
                }
              ]
            },
            "description": "Update a draft campaign's settings. Only campaigns in `DRAFT` status can be edited. Supply `action: update` plus any of the content fields; only the fields you send are changed.\n\n#### Request body parameters\n| Field | Type | Required | Description |\n| --- | --- | --- | --- |\n| `action` | string | Yes | Must be `update`. |\n| `type` | string | No | `sms`, `email`, or `both`. |\n| `title` | string | No | Campaign title. |\n| `sms_sender_id` | string | No | SMS sender ID or virtual number. |\n| `sms_body` | string | No | SMS message body. |\n| `sms_optout_enabled` | boolean | No | Append an opt-out link to the SMS. |\n| `sms_unicode_enabled` | boolean | No | Send as unicode. Requires the `UTF16` account feature. |\n| `sender_email` | string | No | From address; must be at one of your validated domains. |\n| `sender_name` | string | No | From name. |\n| `subject` | string | No | Email subject line. |\n| `email_body` | string | No | Email HTML body. |\n| `email_text` | string | No | Email plain-text body. |\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `campaign_id` | string | Campaign id. |\n| `message` | string | Confirmation message. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Invalid campaign_id format` | 400 | The path id is not a 24-char hex id. |\n| `Campaign not found` | 400 | No such campaign on this account. |\n| `Invalid or missing action` | 400 | `action` is absent or not a supported action. |\n| `Cannot edit campaign - not in draft status` | 400 | The campaign is not in `DRAFT`. |\n| `Invalid campaign type` | 400 | `type` is not `sms`, `email`, or `both`. |\n| `Unicode requires the UTF16 feature on your account` | 400 | `sms_unicode_enabled` set without the `UTF16` account feature. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\",\n  \"action\": \"update\",\n  \"title\": \"July promo (final)\",\n  \"sms_body\": \"Hello from Acme! Reply STOP to opt out.\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"campaign_id\": \"665f1a2b3c4d5e6f7a8b9c0d\",\n  \"message\": \"Campaign updated\"\n}"
            },
            {
              "name": "400 Bad Request",
              "code": 400,
              "status": "Bad Request",
              "body": "{\n  \"error\": \"Cannot edit campaign - not in draft status\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Add recipients",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/campaigns/:id",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "campaigns",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "Campaign id (24-char hex)."
                }
              ]
            },
            "description": "Add recipients to a draft campaign — individual numbers, individual emails, and/or whole contact lists. Only campaigns in `DRAFT` status accept recipients. Individual numbers and emails are capped at 100 combined per call; contact lists have no per-call cap. Duplicates and invalid entries are skipped and reported in `errors` while the rest are added (the top-level `error` stays empty).\n\n#### Request body parameters\n| Field | Type | Required | Description |\n| --- | --- | --- | --- |\n| `action` | string | Yes | Must be `add_recipients`. |\n| `numbers` | string or array | No | Phone numbers. Array, or comma/newline-delimited string. |\n| `emails` | string or array | No | Email addresses. Same format as `numbers`. |\n| `contact_list_ids` | string or array | No | Contact list ids (24-char hex) you own. |\n\nAt least one of `numbers`, `emails`, or `contact_list_ids` must be non-empty.\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `added` | integer | Individual recipients successfully added. |\n| `errors` | array | Per-item messages for skipped entries — e.g. `Number already added: ...`, `Invalid email address: ...`, `Invalid contact list id: ...`, or `Maximum 100 individual recipients per call`. |\n| `recipient_count` | integer | Total recipients on the campaign after the call. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Invalid campaign_id format` | 400 | The path id is not a 24-char hex id. |\n| `Campaign not found` | 400 | No such campaign on this account. |\n| `Invalid or missing action` | 400 | `action` is absent or not a supported action. |\n| `Cannot modify recipients - not in draft status` | 400 | The campaign is not in `DRAFT`. |\n| `No recipients provided` | 400 | None of `numbers`/`emails`/`contact_list_ids` supplied. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\",\n  \"action\": \"add_recipients\",\n  \"numbers\": [\"0404123123\", \"0404123124\"],\n  \"contact_list_ids\": [\"683554c596937cc4b90f5cf7\"]\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"added\": 2,\n  \"errors\": [],\n  \"recipient_count\": 242\n}"
            },
            {
              "name": "400 Bad Request",
              "code": 400,
              "status": "Bad Request",
              "body": "{\n  \"error\": \"No recipients provided\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Remove recipient",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/campaigns/:id",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "campaigns",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "Campaign id (24-char hex)."
                }
              ]
            },
            "description": "Remove a single recipient row (an individual, or a whole contact-list source) from a draft campaign. Use the recipient `id` from **Get campaign**'s `recipients` array. Only campaigns in `DRAFT` status can be modified.\n\n#### Request body parameters\n| Field | Type | Required | Description |\n| --- | --- | --- | --- |\n| `action` | string | Yes | Must be `remove_recipient`. |\n| `recipient_id` | string | Yes | The recipient row id (24-char hex). |\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `recipient_count` | integer | Total recipients remaining. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Invalid campaign_id format` | 400 | The path id is not a 24-char hex id. |\n| `Campaign not found` | 400 | No such campaign on this account. |\n| `Invalid or missing action` | 400 | `action` is absent or not a supported action. |\n| `Cannot modify recipients - not in draft status` | 400 | The campaign is not in `DRAFT`. |\n| `recipient_id is required` | 400 | `recipient_id` missing or not a valid id. |\n| `Recipient not found` | 400 | No such recipient row on this campaign. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\",\n  \"action\": \"remove_recipient\",\n  \"recipient_id\": \"665f1a2b3c4d5e6f7a8b9c11\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"recipient_count\": 2\n}"
            },
            {
              "name": "400 Bad Request",
              "code": 400,
              "status": "Bad Request",
              "body": "{\n  \"error\": \"recipient_id is required\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Clear recipients",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/campaigns/:id",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "campaigns",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "Campaign id (24-char hex)."
                }
              ]
            },
            "description": "Remove all recipients from a draft campaign. Only campaigns in `DRAFT` status can be modified.\n\n#### Request body parameters\n| Field | Type | Required | Description |\n| --- | --- | --- | --- |\n| `action` | string | Yes | Must be `clear_recipients`. |\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `recipient_count` | integer | Always `0` on success. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Invalid campaign_id format` | 400 | The path id is not a 24-char hex id. |\n| `Campaign not found` | 400 | No such campaign on this account. |\n| `Invalid or missing action` | 400 | `action` is absent or not a supported action. |\n| `Cannot modify recipients - not in draft status` | 400 | The campaign is not in `DRAFT`. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\",\n  \"action\": \"clear_recipients\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"recipient_count\": 0\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Send / schedule campaign",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/campaigns/:id",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "campaigns",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "Campaign id (24-char hex)."
                }
              ]
            },
            "description": "Send a draft campaign now, or schedule it for a future time. The campaign must be in `DRAFT` and pass all readiness and account-gating checks. Omit `schedule` to send immediately; supply it to schedule.\n\nSend-time gating enforces, per channel: an active email plan and a verified sending domain (email), and SMS credits or a postpaid account (SMS). Unified Virtual Number senders are capped at 100 recipients; a coupon may only be attached to a Unified Virtual Number sender.\n\n#### Request body parameters\n| Field | Type | Required | Description |\n| --- | --- | --- | --- |\n| `action` | string | Yes | Must be `send`. |\n| `schedule` | integer or string | No | Unix timestamp or a `strtotime`-parseable string, in the future. Omit to send immediately. |\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `campaign_id` | string | Campaign id. |\n| `status` | string | `SENDING` (immediate) or `SCHEDULED`. |\n| `message` | string | Confirmation message. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Invalid campaign_id format` | 400 | The path id is not a 24-char hex id. |\n| `Campaign not found` | 400 | No such campaign on this account. |\n| `Invalid or missing action` | 400 | `action` is absent or not a supported action. |\n| `Campaign is not in draft status` | 400 | The campaign is not in `DRAFT`. |\n| `You need an active email plan to send email campaigns` | 400 | Email campaign with no email quota. |\n| `You need a verified domain to send email campaigns` | 400 | Email campaign with no verified sending domain. |\n| `You need SMS credits or a postpaid account to send SMS campaigns` | 400 | SMS campaign with zero balance and not postpaid. |\n| `Unified campaigns are limited to 100 recipients.` | 400 | Unified Virtual Number sender with more than 100 recipients. |\n| `A coupon can only be attached when the Sender ID is a Unified Virtual Number. Remove the coupon or choose a unified sender.` | 400 | Coupon attached with a non-unified, non-blue sender. |\n| `Scheduled time must be in the future` | 400 | `schedule` did not parse or is not in the future. |\n| `No recipients added` | 400 | Readiness: campaign has no recipients. |\n| `SMS sender ID is required` | 400 | Readiness: SMS channel missing sender ID. |\n| `SMS message body is required` | 400 | Readiness: SMS channel missing body. |\n| `SMS message is too long (max 760 characters)` | 400 | Readiness: SMS body exceeds the GSM length limit. |\n| `Unicode SMS message is too long (max 335 characters)` | 400 | Readiness: unicode SMS body exceeds the unicode length limit. |\n| `Sender email is required` | 400 | Readiness: email channel missing sender email. |\n| `Sender name is required` | 400 | Readiness: email channel missing sender name. |\n| `Subject line is required` | 400 | Readiness: email channel missing subject. |\n| `Email body is required` | 400 | Readiness: email channel missing body. |\n| `Campaign is already sending` | 400 | Send already in progress. |\n| `Campaign is already scheduled` | 400 | A schedule is already set. |\n| `Campaign has already been sent` | 400 | The campaign was already sent. |\n| `Failed to start campaign` | 400 | The send could not be started (defensive fallback). |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\",\n  \"action\": \"send\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"campaign_id\": \"665f1a2b3c4d5e6f7a8b9c0d\",\n  \"status\": \"SENDING\",\n  \"message\": \"Campaign started\"\n}"
            },
            {
              "name": "400 Bad Request",
              "code": 400,
              "status": "Bad Request",
              "body": "{\n  \"error\": \"You need a verified domain to send email campaigns\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Cancel schedule",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/campaigns/:id",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "campaigns",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "Campaign id (24-char hex)."
                }
              ]
            },
            "description": "Cancel a scheduled campaign and return it to `DRAFT`. Only works while the campaign is `SCHEDULED` and more than 30 minutes remain before the scheduled send.\n\n#### Request body parameters\n| Field | Type | Required | Description |\n| --- | --- | --- | --- |\n| `action` | string | Yes | Must be `cancel_schedule`. |\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `campaign_id` | string | Campaign id. |\n| `status` | string | `DRAFT`. |\n| `message` | string | Confirmation message. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Invalid campaign_id format` | 400 | The path id is not a 24-char hex id. |\n| `Campaign not found` | 400 | No such campaign on this account. |\n| `Invalid or missing action` | 400 | `action` is absent or not a supported action. |\n| `Campaign is not scheduled` | 400 | The campaign is not in `SCHEDULED`. |\n| `Cannot cancel - less than 30 minutes until scheduled send` | 400 | Too close to the scheduled send time. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\",\n  \"action\": \"cancel_schedule\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"campaign_id\": \"665f1a2b3c4d5e6f7a8b9c0d\",\n  \"status\": \"DRAFT\",\n  \"message\": \"Schedule cancelled\"\n}"
            },
            {
              "name": "400 Bad Request",
              "code": 400,
              "status": "Bad Request",
              "body": "{\n  \"error\": \"Cannot cancel - less than 30 minutes until scheduled send\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Delete campaign",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/campaigns/:id",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "campaigns",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "Campaign id (24-char hex)."
                }
              ]
            },
            "description": "Delete a campaign and its recipients. Only draft campaigns can be deleted — campaigns that are sending, scheduled, or already sent cannot be removed.\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `campaign_id` | string | The deleted campaign's id. |\n| `message` | string | Confirmation message. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Invalid campaign_id format` | 400 | The path id is not a 24-char hex id. |\n| `Campaign not found` | 400 | No such campaign on this account. |\n| `Cannot delete campaign` | 400 | The campaign is sending, scheduled, or sent. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"campaign_id\": \"665f1a2b3c4d5e6f7a8b9c0d\",\n  \"message\": \"Campaign deleted\"\n}"
            },
            {
              "name": "400 Bad Request",
              "code": 400,
              "status": "Bad Request",
              "body": "{\n  \"error\": \"Cannot delete campaign\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "Sender IDs",
      "item": [
        {
          "name": "List Sender IDs",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/senderid",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "senderid"
              ]
            },
            "description": "List the sender IDs registered on your account.\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `senderids` | array | Registered sender IDs. |\n| `senderids[].id` | string | Sender ID record id. |\n| `senderids[].senderid` | string | The sender ID value. |\n| `senderids[].status` | string | Approval status. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"senderids\": [\n    {\n      \"id\": \"683554c596937cc4b90f5cf7\",\n      \"senderid\": \"EXAMPLE\",\n      \"status\": \"approved\"\n    }\n  ]\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Create Sender ID",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/senderid",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "senderid"
              ]
            },
            "description": "Register a new sender ID for approval.\n\n#### Request body parameters\n| Field | Type | Required | Description |\n| --- | --- | --- | --- |\n| `senderid` | string | Yes | The sender ID to register. |\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `message` | string | Confirmation message. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Missing Sender ID` | 400 | `senderid` not supplied. |\n| `Invalid new Sender ID` | 400 | `senderid` is not valid. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\",\n  \"senderid\": \"EXAMPLE\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"message\": \"Sender ID created\"\n}"
            },
            {
              "name": "400 Bad Request",
              "code": 400,
              "status": "Bad Request",
              "body": "{\n  \"error\": \"Invalid new Sender ID\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Delete Sender ID",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/senderid/:id",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "senderid",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "Sender ID record id."
                }
              ]
            },
            "description": "Remove a sender ID from your account.\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "Virtual Numbers",
      "item": [
        {
          "name": "List Virtual Numbers",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/virtualnumber",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "virtualnumber"
              ]
            },
            "description": "List the virtual numbers on your account.\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `virtualnumbers` | array | Virtual numbers. |\n| `virtualnumbers[].id` | string | Virtual number record id. |\n| `virtualnumbers[].number` | string | Number in international format. |\n| `virtualnumbers[].number_formatted` | string | Human-formatted number. |\n| `virtualnumbers[].country` | string | Country code. |\n| `virtualnumbers[].type` | string | `standard` or `unified`. |\n| `count` | integer | Number of virtual numbers. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"virtualnumbers\": [\n    {\n      \"id\": \"683554c596937cc4b90f5cf7\",\n      \"number\": \"61400000000\",\n      \"number_formatted\": \"0400 000 000\",\n      \"country\": \"AU\",\n      \"type\": \"standard\"\n    }\n  ],\n  \"count\": 1\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Purchase Virtual Number",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/virtualnumber",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "virtualnumber"
              ]
            },
            "description": "Purchase a virtual number. Charges the card on file and creates a monthly subscription. Default limit 2 numbers unless the MORE_VMNS flag is set.\n\n#### Request body parameters\n| Field | Type | Required | Description |\n| --- | --- | --- | --- |\n| `country` | string | Yes | Only `AU` is supported for API purchases. |\n| `type` | string | No | `standard` or `unified`. Default `standard`; `unified` requires the ENABLE_UNIFIED flag. |\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `message` | string | Confirmation message. |\n| `virtualnumber` | object | `{id, number, number_formatted, country}`. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Missing required parameter: country` | 400 | `country` not supplied. |\n| `Invalid country. Currently only AU is supported for API requests.` | 400 | `country` is not `AU`. |\n| `Invalid type. Must be \"standard\" or \"unified\".` | 400 | `type` is not an allowed value. |\n| `Your account is not enabled for Unified Mobile Numbers. Please contact us.` | 400 | `type: unified` without the ENABLE_UNIFIED flag. |\n| `Your account is currently on hold. Please contact us to purchase a virtual number.` | 400 | Account is on hold. |\n| `Maximum virtual numbers reached. Contact us to request increaed quota.` | 400 | Number limit reached (default 2). |\n| `No payment method on file. Please add a payment method in the dashboard.` | 400 | No card on file. |\n| `Invalid subscription plan` | 400 | Subscription plan could not be resolved. |\n| `Your payment was declined by your bank.` | 400 | Card charge declined. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\",\n  \"country\": \"AU\",\n  \"type\": \"standard\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"message\": \"Virtual number purchased\",\n  \"virtualnumber\": {\n    \"id\": \"683554c596937cc4b90f5cf7\",\n    \"number\": \"61400000000\",\n    \"number_formatted\": \"0400 000 000\",\n    \"country\": \"AU\"\n  }\n}"
            },
            {
              "name": "400 Bad Request",
              "code": 400,
              "status": "Bad Request",
              "body": "{\n  \"error\": \"Invalid country. Currently only AU is supported for API requests.\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Delete Virtual Number",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/virtualnumber/:id",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "virtualnumber",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "Virtual number record id."
                }
              ]
            },
            "description": "Release a virtual number and cancel its subscription.\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `message` | string | Confirmation message. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Invalid virtual number ID format` | 400 | `id` is not a valid id. |\n| `Virtual number not found` | 400 | No such virtual number. |\n| `Unauthorized - you do not own this virtual number` | 400 | Number not owned by this account. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"message\": \"Virtual number released\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "Email Domains",
      "item": [
        {
          "name": "List Domains",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_dingo}}/domain",
              "host": [
                "{{base_url_dingo}}"
              ],
              "path": [
                "domain"
              ]
            },
            "description": "List the email domains registered on your account.\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `domains` | array | Registered domains. |\n| `domains[].id` | string | Domain record id. |\n| `domains[].domain` | string | Domain name. |\n| `domains[].subdomain` | string | Sending subdomain. |\n| `domains[].status` | string | Verification status. |\n| `domains[].dkim_names` | array | DKIM record names. |\n| `domains[].verification` | string | SES verification state. |\n| `count` | integer | Number of domains. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"domains\": [\n    {\n      \"id\": \"683554c596937cc4b90f5cf7\",\n      \"domain\": \"example.com\",\n      \"subdomain\": \"email\",\n      \"status\": \"verified\",\n      \"dkim_names\": [\n        \"k1._domainkey\"\n      ],\n      \"verification\": \"verified\"\n    }\n  ],\n  \"count\": 1\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Get Domain",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_dingo}}/domain/:id",
              "host": [
                "{{base_url_dingo}}"
              ],
              "path": [
                "domain",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "Domain record id."
                }
              ]
            },
            "description": "Fetch a single email domain, including its DKIM and verification details.\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `domain` | object | id, domain, subdomain, status, dkim_names, verification. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Failed (Domain not found or unauthorized)` | 400 | No such domain, or not owned by this account. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"domain\": {\n    \"id\": \"683554c596937cc4b90f5cf7\",\n    \"domain\": \"example.com\",\n    \"subdomain\": \"email\",\n    \"status\": \"verified\",\n    \"dkim_names\": [\n      \"k1._domainkey\"\n    ],\n    \"verification\": \"verified\"\n  }\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Create Domain",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_dingo}}/domain",
              "host": [
                "{{base_url_dingo}}"
              ],
              "path": [
                "domain"
              ]
            },
            "description": "Register a new email domain. Email must be enabled and your plan's domain limit must not be exceeded.\n\n#### Request body parameters\n| Field | Type | Required | Description |\n| --- | --- | --- | --- |\n| `domain` | string | Yes | Domain name (valid domain format). |\n| `subdomain` | string | No | Sending subdomain; letters, numbers, hyphens. Default `email`. If a full host is supplied it is auto-corrected and a `warning` is returned. |\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `message` | object | `{id, domain}`. |\n| `warning` | string | Present only when the subdomain was auto-corrected. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Email not enabled. Please contact us.` | 400 | Email is not enabled on this account. |\n| `Domain limit reached. Your plan allows {n} domain{s}. Please upgrade your plan or contact us.` | 400 | Plan domain limit reached. |\n| `Missing Domain` | 400 | `domain` not supplied. |\n| `Invalid Domain` | 400 | `domain` is not a valid domain. |\n| `Domain already registered. Please contact us.` | 400 | Domain already exists. |\n| `Invalid subdomain. Use only letters, numbers, and hyphens.` | 400 | `subdomain` contains invalid characters. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\",\n  \"domain\": \"example.com\",\n  \"subdomain\": \"email\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"message\": {\n    \"id\": \"683554c596937cc4b90f5cf7\",\n    \"domain\": \"example.com\"\n  }\n}"
            },
            {
              "name": "400 Bad Request",
              "code": 400,
              "status": "Bad Request",
              "body": "{\n  \"error\": \"Invalid Domain\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "Mailboxes",
      "description": "Hosted mailboxes on the shared mailbox domain — send-and-receive addresses that need no domain of your own. Your plan's mailbox quota caps how many active mailboxes the account may have. Mailboxes are never deleted via the API: `DELETE` returns `Unsupported method. Please see our API Docs` (HTTP 400). Disable a mailbox instead; mailboxes are removed only when the account closes.",
      "item": [
        {
          "name": "List Mailboxes",
          "request": {
            "method": "GET",
            "header": [ { "key": "Content-Type", "value": "application/json" } ],
            "url": {
              "raw": "{{base_url_dingo}}/mailboxes",
              "host": [ "{{base_url_dingo}}" ],
              "path": [ "mailboxes" ]
            },
            "description": "List the hosted mailboxes on your account, active and inactive.\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `mailboxes` | array | One object per mailbox. |\n| `mailboxes[].id` | string | Mailbox id (use with Enable / Disable Mailbox). |\n| `mailboxes[].address` | string | Full mailbox address. |\n| `mailboxes[].active` | boolean | `false` when the mailbox is disabled — inbound to it is dropped and it cannot send. |\n| `mailboxes[].created` | integer | Unix timestamp the mailbox was created, or `null`. |\n| `count` | integer | Number of mailboxes returned. |\n| `error` | string | Empty on success. |\n\n#### Errors\nNo endpoint-specific errors — authentication errors per the intro's 401 table.",
            "body": {
              "mode": "raw",
              "options": { "raw": { "language": "json" } },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            }
          },
          "response": [
            { "name": "200 OK", "code": 200, "status": "OK", "body": "{\n  \"error\": \"\",\n  \"count\": 2,\n  \"mailboxes\": [\n    {\n      \"id\": \"66a1f2c3d4e5f6a7b8c9d0e1\",\n      \"address\": \"newsletter@mydingo.au\",\n      \"active\": true,\n      \"created\": 1753142400\n    },\n    {\n      \"id\": \"66a1f2c3d4e5f6a7b8c9d0e2\",\n      \"address\": \"old@mydingo.au\",\n      \"active\": false,\n      \"created\": 1750464000\n    }\n  ]\n}" },
            { "name": "401 Unauthorized", "code": 401, "status": "Unauthorized", "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}" }
          ]
        },
        {
          "name": "Create Mailbox",
          "request": {
            "method": "POST",
            "header": [ { "key": "Content-Type", "value": "application/json" } ],
            "url": {
              "raw": "{{base_url_dingo}}/mailboxes",
              "host": [ "{{base_url_dingo}}" ],
              "path": [ "mailboxes" ]
            },
            "description": "Create a hosted mailbox. The new mailbox is active immediately and can send and receive at `<name>@mydingo.au`. Creation counts against your plan's mailbox quota (active mailboxes only).\n\n#### Request body\n| Field | Type | Required | Description |\n| --- | --- | --- | --- |\n| `name` | string | yes | The local part (the text before the `@`). Lowercased. Letters, numbers and `. _ -` only, must start and end with a letter or number, maximum 64 characters. `+` is not allowed. Reserved names (role addresses such as `postmaster`, `abuse`, `admin`, and brand terms) are rejected. Must be globally unique across all accounts. |\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `mailbox.id` | string | The new mailbox id. |\n| `mailbox.address` | string | The full mailbox address. |\n| `mailbox.active` | boolean | Always `true` on create. |\n| `mailbox.created` | integer | Unix timestamp. |\n| `message` | string | `Mailbox created`. |\n| `error` | string | Empty on success. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Mailboxes are not included in your plan` | 400 | The account's mailbox quota is 0. |\n| `Unable to create mailboxes at this time. Please contact us.` | 400 | Please contact us |\n| `Mailbox limit reached` | 400 | The account already has as many active mailboxes as its quota allows. |\n| `Name Required. Please see our API Docs` | 400 | `name` missing or empty. |\n| `Mailbox name may only contain letters, numbers, and . _ - characters.` | 400 | `name` fails the format rule. |\n| `Mailbox name must be 64 characters or fewer.` | 400 | `name` longer than 64 characters. |\n| `That mailbox name is reserved. Please choose another name.` | 400 | `name` is on the reserved list. |\n| `That mailbox is already taken. Please choose another name.` | 400 | The address exists on any account (including disabled mailboxes). |",
            "body": {
              "mode": "raw",
              "options": { "raw": { "language": "json" } },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\",\n  \"name\": \"newsletter\"\n}"
            }
          },
          "response": [
            { "name": "200 OK", "code": 200, "status": "OK", "body": "{\n  \"error\": \"\",\n  \"message\": \"Mailbox created\",\n  \"mailbox\": {\n    \"id\": \"66a1f2c3d4e5f6a7b8c9d0e1\",\n    \"address\": \"newsletter@mydingo.au\",\n    \"active\": true,\n    \"created\": 1753142400\n  }\n}" },
            { "name": "400 Bad Request", "code": 400, "status": "Bad Request", "body": "{\n  \"error\": \"That mailbox is already taken. Please choose another name.\"\n}" },
            { "name": "401 Unauthorized", "code": 401, "status": "Unauthorized", "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}" }
          ]
        },
        {
          "name": "Enable / Disable Mailbox",
          "request": {
            "method": "POST",
            "header": [ { "key": "Content-Type", "value": "application/json" } ],
            "url": {
              "raw": "{{base_url_dingo}}/mailboxes/:id",
              "host": [ "{{base_url_dingo}}" ],
              "path": [ "mailboxes", ":id" ],
              "variable": [ { "key": "id", "value": "", "description": "Mailbox id (from List Mailboxes)." } ]
            },
            "description": "Enable or disable a hosted mailbox. A disabled mailbox drops inbound email, cannot be used as a sender address, and does not count toward the mailbox quota — but keeps its address reserved to your account. Re-enabling is blocked while the account is already at its active-mailbox quota. There is no delete: mailboxes are removed only when the account closes.\n\n#### Request body\n| Field | Type | Required | Description |\n| --- | --- | --- | --- |\n| `active` | boolean | yes | `true` to enable, `false` to disable. |\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `mailbox` | object | The updated mailbox (`id`, `address`, `active`, `created`). |\n| `message` | string | `Mailbox updated`. |\n| `error` | string | Empty on success. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Invalid Mailbox ID` | 400 | The path id is not a 24-character hex id. |\n| `Mailbox Not Found` | 400 | No mailbox with that id on this account. |\n| `active Required. Please see our API Docs` | 400 | `active` missing or not a boolean. |\n| `Mailbox limit reached` | 400 | Enabling would exceed the account's active-mailbox quota. |",
            "body": {
              "mode": "raw",
              "options": { "raw": { "language": "json" } },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\",\n  \"active\": false\n}"
            }
          },
          "response": [
            { "name": "200 OK", "code": 200, "status": "OK", "body": "{\n  \"error\": \"\",\n  \"message\": \"Mailbox updated\",\n  \"mailbox\": {\n    \"id\": \"66a1f2c3d4e5f6a7b8c9d0e1\",\n    \"address\": \"newsletter@mydingo.au\",\n    \"active\": false,\n    \"created\": 1753142400\n  }\n}" },
            { "name": "401 Unauthorized", "code": 401, "status": "Unauthorized", "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}" }
          ]
        }
      ]
    },
    {
      "name": "Contacts",
      "item": [
        {
          "name": "Create Contact List",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/lists",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "lists"
              ]
            },
            "description": "Create a contact list.\n\n#### Request body parameters\n| Field | Type | Required | Description |\n| --- | --- | --- | --- |\n| `name` | string | Yes | List name. |\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `list.id` | string | New list id. |\n| `list.name` | string | List name. |\n| `list.status` | string | `active` on creation. |\n| `list.count` | integer | Contact count (0 for a new list). |\n| `message` | string | Confirmation message. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Missing required field: name` | 400 | `name` not supplied or empty. |\n| `List update is not supported` | 400 | POSTing to a list id (`/lists/{id}`) — renaming or changing a list's status is not available via the API; manage it in the dashboard. |\n\n#### Notes\n- Lists are create-and-delete only through the API. There is no rename or archive endpoint.\n- Deleting a list also deletes every contact in it (see Delete List).",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\",\n  \"name\": \"VIP Customers\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"list\": {\n    \"id\": \"683554c596937cc4b90f5cf7\",\n    \"name\": \"VIP Customers\",\n    \"status\": \"active\",\n    \"count\": 0\n  },\n  \"message\": \"List created\"\n}"
            },
            {
              "name": "400 Bad Request",
              "code": 400,
              "status": "Bad Request",
              "body": "{\n  \"error\": \"Missing required field: name\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "List Contact Lists",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/lists",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "lists"
              ]
            },
            "description": "List all contact lists on your account, each with its current contact count.\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `lists` | array | Contact lists. |\n| `lists[].id` | string | List id. |\n| `lists[].name` | string | List name. |\n| `lists[].status` | string | `active` or `archived`. |\n| `lists[].count` | integer | Number of contacts in the list. |\n| `count` | integer | Number of lists. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"lists\": [\n    {\n      \"id\": \"683554c596937cc4b90f5cf7\",\n      \"name\": \"VIP Customers\",\n      \"status\": \"active\",\n      \"count\": 128\n    }\n  ],\n  \"count\": 1\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Get Contact List",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/lists/:id",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "lists",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "Contact list id (24-hex)."
                }
              ]
            },
            "description": "Get a single contact list by id.\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `list.id` | string | List id. |\n| `list.name` | string | List name. |\n| `list.status` | string | `active` or `archived`. |\n| `list.count` | integer | Number of contacts in the list. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Invalid list ID format` | 400 | The id is not a 24-hex value. |\n| `List not found` | 400 | No such list on this account. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"list\": {\n    \"id\": \"683554c596937cc4b90f5cf7\",\n    \"name\": \"VIP Customers\",\n    \"status\": \"active\",\n    \"count\": 128\n  }\n}"
            },
            {
              "name": "400 Bad Request",
              "code": 400,
              "status": "Bad Request",
              "body": "{\n  \"error\": \"List not found\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Delete Contact List",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/lists/:id",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "lists",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "Contact list id (24-hex)."
                }
              ]
            },
            "description": "Delete a contact list **and every contact in it**. This cannot be undone.\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `message` | string | Confirmation message. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Invalid list ID format` | 400 | The id is not a 24-hex value. |\n| `List not found` | 400 | No such list on this account. |\n\n#### Notes\n- The delete cascades: every contact whose `list_id` is this list is removed along with the list.",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"message\": \"List and its contacts deleted\"\n}"
            },
            {
              "name": "400 Bad Request",
              "code": 400,
              "status": "Bad Request",
              "body": "{\n  \"error\": \"List not found\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Create Contact",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/contacts",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "contacts"
              ]
            },
            "description": "Create one contact, or many in a single call.\n\n- **Single:** send the contact fields at the top level.\n- **Batch:** send a `contacts` array. Every row is inserted into the one `list_id`, and the response reports per-row results. Batch is best-effort — a row that isn't a JSON object is skipped and reported in `results[]`; the rest still insert. Max 1000 rows per call.\n\n#### Request body parameters\n| Field | Type | Required | Description |\n| --- | --- | --- | --- |\n| `list_id` | string | Yes | Target list id; must belong to your account. |\n| `number` | string | No | Phone number; normalised to 614… format on save. |\n| `first_name` | string | No | Contact's first name. |\n| `last_name` | string | No | Contact's last name. |\n| `email` | string | No | Contact's email address. |\n| `meta` | object | No | Custom fields — arbitrary key/value pairs. Scalar values only (array/object values are dropped); `.` and `$` in keys are replaced with `_`. |\n| `contacts` | array | No | Batch mode: array of contact objects, each carrying the fields above except `list_id`. Presence of this field switches to batch mode. |\n\n#### Response fields (single)\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `contact` | object | The created contact: `id`, `list_id`, `number`, `first_name`, `last_name`, `email`, `meta`. |\n| `message` | string | Confirmation message. |\n\n#### Response fields (batch)\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty — a batch always returns HTTP 200; per-row failures are in `results`. |\n| `created` | integer | Rows inserted. |\n| `failed` | integer | Rows skipped. |\n| `results` | array | Per row: `{index, id}` on success or `{index, error}` on failure. |\n| `message` | string | `Batch processed`. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Missing required field: list_id` | 400 | `list_id` not supplied. |\n| `Invalid list ID format` | 400 | `list_id` not a 24-hex value. |\n| `List not found` | 400 | The list doesn't exist or isn't yours. |\n| `contacts must be a non-empty array` | 400 | Batch mode with an empty or non-array `contacts`. |\n| `Batch too large. Maximum 1000 contacts per request` | 400 | Batch `contacts` has more than 1000 rows. |\n| `Invalid contact object` | (per-row) | A `contacts[]` row is not an object — reported in `results[].error`, not at the top level (HTTP stays 200). |\n\n#### Notes\n- Custom fields have no per-account schema; any key you send under `meta` is stored as-is.\n- Create is not deduplicated — re-sending the same contact creates a duplicate.\n- A batch targets a single list; mixing lists in one call is not supported.",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\",\n  \"list_id\": \"683554c596937cc4b90f5cf7\",\n  \"number\": \"0412333555\",\n  \"first_name\": \"Jane\",\n  \"last_name\": \"Smith\",\n  \"email\": \"jane@example.com\",\n  \"meta\": {\n    \"company\": \"Acme\",\n    \"plan\": \"gold\"\n  }\n}"
            }
          },
          "response": [
            {
              "name": "200 OK (single)",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"contact\": {\n    \"id\": \"683554c596937cc4b90f5cf8\",\n    \"list_id\": \"683554c596937cc4b90f5cf7\",\n    \"number\": \"61412333555\",\n    \"first_name\": \"Jane\",\n    \"last_name\": \"Smith\",\n    \"email\": \"jane@example.com\",\n    \"meta\": {\n      \"company\": \"Acme\",\n      \"plan\": \"gold\"\n    }\n  },\n  \"message\": \"Contact created\"\n}"
            },
            {
              "name": "200 OK (batch)",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"created\": 2,\n  \"failed\": 1,\n  \"results\": [\n    { \"index\": 0, \"id\": \"683554c596937cc4b90f5cf8\" },\n    { \"index\": 1, \"id\": \"683554c596937cc4b90f5cf9\" },\n    { \"index\": 2, \"error\": \"Invalid contact object\" }\n  ],\n  \"message\": \"Batch processed\"\n}"
            },
            {
              "name": "400 Bad Request",
              "code": 400,
              "status": "Bad Request",
              "body": "{\n  \"error\": \"Missing required field: list_id\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "List Contacts",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/contacts",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "contacts"
              ],
              "query": [
                {
                  "key": "list_id",
                  "value": "",
                  "description": "Optional. Restrict to one list (24-hex).",
                  "disabled": true
                },
                {
                  "key": "search",
                  "value": "",
                  "description": "Optional. Case-insensitive substring match on first name, last name, number, or email.",
                  "disabled": true
                },
                {
                  "key": "after",
                  "value": "",
                  "description": "24-hex cursor — the `id` of the last contact on the previous page.",
                  "disabled": true
                },
                {
                  "key": "limit",
                  "value": "",
                  "description": "Page size. Plain list: 1–1000 (default 100). Search: 1–500 (default 500).",
                  "disabled": true
                }
              ]
            },
            "description": "List contacts, newest first, or search them.\n\n- **Without `search`:** a keyset-paginated list, optionally filtered by `list_id`.\n- **With `search`:** a substring match over name / number / email, also keyset-paginated.\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `contacts` | array | Contacts on this page. |\n| `contacts[].id` | string | Contact id. |\n| `contacts[].list_id` | string | Owning list id. |\n| `contacts[].number` | string | Phone number (614… format). |\n| `contacts[].first_name` | string | First name. |\n| `contacts[].last_name` | string | Last name. |\n| `contacts[].email` | string | Email address. |\n| `contacts[].meta` | object | Custom fields (empty object when none). |\n| `count` | integer | Contacts on this page. |\n| `next_page` | string | Relative path for the next page; empty when there are no further results. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Invalid list ID format` | 400 | `list_id` is not a 24-hex value. |\n| `Failed (Invalid Page)` | 400 | `after` is not a valid 24-hex cursor. |\n\n#### Notes\n- Plain-list page size defaults to 100 (`limit` 1–1000). Search page size defaults to 500 (`limit` 1–500).\n- `next_page` is present only when a full page was returned; follow it until it is empty. The paging examples below use `limit=2` to stay short.\n- Search is inherently a substring scan — for a rare term over a very large contact book a page may take longer to fill.",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"contacts\": [\n    {\n      \"id\": \"683554c596937cc4b90f5cf8\",\n      \"list_id\": \"683554c596937cc4b90f5cf7\",\n      \"number\": \"61412333555\",\n      \"first_name\": \"Jane\",\n      \"last_name\": \"Smith\",\n      \"email\": \"jane@example.com\",\n      \"meta\": {\n        \"company\": \"Acme\"\n      }\n    },\n    {\n      \"id\": \"683554c596937cc4b90f5cf6\",\n      \"list_id\": \"683554c596937cc4b90f5cf7\",\n      \"number\": \"61412333777\",\n      \"first_name\": \"Tom\",\n      \"last_name\": \"Jones\",\n      \"email\": \"tom@example.com\",\n      \"meta\": {}\n    }\n  ],\n  \"count\": 2,\n  \"next_page\": \"/api/v5/contacts?after=683554c596937cc4b90f5cf6\"\n}"
            },
            {
              "name": "200 OK (Last Page)",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"contacts\": [\n    {\n      \"id\": \"683554c596937cc4b90f5cf4\",\n      \"list_id\": \"683554c596937cc4b90f5cf7\",\n      \"number\": \"61412333999\",\n      \"first_name\": \"Amy\",\n      \"last_name\": \"Lee\",\n      \"email\": \"amy@example.com\",\n      \"meta\": {}\n    }\n  ],\n  \"count\": 1,\n  \"next_page\": \"\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Get Contact",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/contacts/:id",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "contacts",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "Contact id (24-hex)."
                }
              ]
            },
            "description": "Get a single contact by id.\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `contact` | object | The contact: `id`, `list_id`, `number`, `first_name`, `last_name`, `email`, `meta`. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Invalid contact ID format` | 400 | The id is not a 24-hex value. |\n| `Contact not found` | 400 | No such contact on this account. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"contact\": {\n    \"id\": \"683554c596937cc4b90f5cf8\",\n    \"list_id\": \"683554c596937cc4b90f5cf7\",\n    \"number\": \"61412333555\",\n    \"first_name\": \"Jane\",\n    \"last_name\": \"Smith\",\n    \"email\": \"jane@example.com\",\n    \"meta\": {\n      \"company\": \"Acme\"\n    }\n  }\n}"
            },
            {
              "name": "400 Bad Request",
              "code": 400,
              "status": "Bad Request",
              "body": "{\n  \"error\": \"Contact not found\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Update Contact",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/contacts/:id",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "contacts",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "Contact id (24-hex)."
                }
              ]
            },
            "description": "Update fields on a contact. Only the fields you send change.\n\n#### Request body parameters\n| Field | Type | Required | Description |\n| --- | --- | --- | --- |\n| `number` | string | No | Phone number; normalised to 614… on save. |\n| `first_name` | string | No | First name. |\n| `last_name` | string | No | Last name. |\n| `email` | string | No | Email address. |\n| `meta` | object | No | Custom fields. **Replaces** the entire meta object — send the full desired set, not a partial patch. |\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `contact` | object | The updated contact. |\n| `message` | string | Confirmation message. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Invalid contact ID format` | 400 | The id is not a 24-hex value. |\n| `Contact not found` | 400 | No such contact on this account. |\n| `Nothing to update` | 400 | No recognised field supplied. |\n\n#### Notes\n- `meta` is replaced wholesale, not merged; omit it to leave custom fields untouched.",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\",\n  \"email\": \"jane.smith@example.com\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"contact\": {\n    \"id\": \"683554c596937cc4b90f5cf8\",\n    \"list_id\": \"683554c596937cc4b90f5cf7\",\n    \"number\": \"61412333555\",\n    \"first_name\": \"Jane\",\n    \"last_name\": \"Smith\",\n    \"email\": \"jane.smith@example.com\",\n    \"meta\": {\n      \"company\": \"Acme\"\n    }\n  },\n  \"message\": \"Contact updated\"\n}"
            },
            {
              "name": "400 Bad Request",
              "code": 400,
              "status": "Bad Request",
              "body": "{\n  \"error\": \"Nothing to update\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Delete Contact",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/contacts/:id",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "contacts",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "Contact id (24-hex)."
                }
              ]
            },
            "description": "Delete a contact.\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `message` | string | Confirmation message. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Invalid contact ID format` | 400 | The id is not a 24-hex value. |\n| `Contact not found` | 400 | No such contact on this account. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"message\": \"Contact deleted\"\n}"
            },
            {
              "name": "400 Bad Request",
              "code": 400,
              "status": "Bad Request",
              "body": "{\n  \"error\": \"Contact not found\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "Webhooks",
      "description": "Manage outbound webhook configurations programmatically. Webhooks POST a payload to your URL when a platform event fires. The six event types are `sms_events`, `sms_inbound`, `sms_optout`, `email_inbound`, `email_events`, `account_balance`. `email_events` webhooks additionally subscribe to a subset of `delivery`, `engagement`, and `unsubscribe` sub-events via the `events` array. All endpoints use `{{base_url_sms}}`.",
      "item": [
        {
          "name": "List Webhooks",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/webhooks",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "webhooks"
              ],
              "query": [
                {
                  "key": "type",
                  "value": "",
                  "description": "Optional. Restrict to one type: `sms_events`, `sms_inbound`, `sms_optout`, `email_inbound`, `email_events`, `account_balance`.",
                  "disabled": true
                }
              ]
            },
            "description": "List every outbound webhook configuration on your account, including disabled and temporarily-suppressed ones (this is a management view). Optionally filter by `type`.\n\n#### Query parameters\n| Field | Type | Required | Description |\n| --- | --- | --- | --- |\n| `type` | string | No | Restrict to one type. One of `sms_events`, `sms_inbound`, `sms_optout`, `email_inbound`, `email_events`, `account_balance`. |\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `webhooks` | array | Webhook configurations on the account. |\n| `webhooks[].id` | string | Webhook id (24-hex). |\n| `webhooks[].type` | string | One of `sms_events`, `sms_inbound`, `sms_optout`, `email_inbound`, `email_events`, `account_balance`. |\n| `webhooks[].url` | string | Destination URL the payload is POSTed to. |\n| `webhooks[].version` | integer | Payload format version. Currently always `1` (form-encoded); v2/JSON creation is disabled. |\n| `webhooks[].threshold` | integer\\|null | Balance threshold; set only for `account_balance` webhooks, otherwise `null`. |\n| `webhooks[].events` | array | Subscribed events; populated only for `email_events` webhooks (subset of `delivery`, `engagement`, `unsubscribe`), otherwise `[]`. |\n| `webhooks[].enabled` | boolean | Whether the webhook is currently enabled. |\n| `count` | integer | Number of webhooks returned. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Invalid type. Must be one of: sms_events, sms_inbound, sms_optout, email_inbound, email_events, account_balance` | 400 | `type` filter is not an allowed value. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"webhooks\": [\n    {\n      \"id\": \"683554c596937cc4b90f5cf7\",\n      \"type\": \"sms_events\",\n      \"url\": \"https://example.com/dlr\",\n      \"version\": 1,\n      \"threshold\": null,\n      \"events\": [],\n      \"enabled\": true\n    },\n    {\n      \"id\": \"7a1b2c3d4e5f60718293a4b5\",\n      \"type\": \"email_events\",\n      \"url\": \"https://example.com/email-events\",\n      \"version\": 1,\n      \"threshold\": null,\n      \"events\": [\n        \"delivery\",\n        \"engagement\"\n      ],\n      \"enabled\": true\n    }\n  ],\n  \"count\": 2\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Create Webhook",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/webhooks",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "webhooks"
              ]
            },
            "description": "Create an outbound webhook configuration. The `type` determines which platform event fires it. Duplicate configurations of the same type are allowed.\n\n#### Request body parameters\n| Field | Type | Required | Description |\n| --- | --- | --- | --- |\n| `type` | string | Yes | One of `sms_events`, `sms_inbound`, `sms_optout`, `email_inbound`, `email_events`, `account_balance`. |\n| `url` | string | Yes | Destination URL. `http://` is prepended if no scheme is given. Loopback and the cloud metadata address are rejected. |\n| `threshold` | integer | Conditional | Required for `type=account_balance`: fires when the account balance is at or below this value (must be > 0). Ignored for other types. |\n| `events` | array | Conditional | Required for `type=email_events`: a non-empty subset of `delivery`, `engagement`, `unsubscribe`. Duplicates are collapsed. Ignored for other types. |\n\nWebhooks are created as v1 (form-encoded payloads); v2/JSON creation is currently disabled.\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `webhook` | object | The created webhook (`id`, `type`, `url`, `version`, `threshold`, `events`, `enabled`). |\n| `message` | string | Confirmation message. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Missing required field: type` | 400 | `type` not supplied. |\n| `Invalid type. Must be one of: sms_events, sms_inbound, sms_optout, email_inbound, email_events, account_balance` | 400 | `type` is not an allowed value. |\n| `Missing required field: url` | 400 | `url` not supplied. |\n| `Balance webhooks require a numeric threshold greater than 0` | 400 | `type=account_balance` with a missing or non-positive `threshold`. |\n| `Email webhooks require a non-empty events array (subset of: delivery, engagement, unsubscribe)` | 400 | `type=email_events` with `events` absent, not an array, or empty. |\n| `Invalid event. Must be one of: delivery, engagement, unsubscribe` | 400 | `type=email_events` with an `events` value outside the allowlist. |\n| `Invalid URL format` | 400 | `url` fails URL validation. |\n| `URL must use http or https protocol` | 400 | `url` scheme is not http/https. |\n| `URL must have a valid domain` | 400 | `url` has no host. |\n| `Invalid URL protocol` | 400 | `url` contains a `file://` protocol. |\n| `Cannot use localhost URLs` | 400 | `url` host is localhost/127.0.0.1/::1. |\n| `Invalid URL` | 400 | `url` targets the cloud metadata address (169.254.169.254). |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\",\n  \"type\": \"email_events\",\n  \"url\": \"https://example.com/email-events\",\n  \"events\": [\n    \"delivery\",\n    \"engagement\"\n  ]\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"webhook\": {\n    \"id\": \"7a1b2c3d4e5f60718293a4b5\",\n    \"type\": \"email_events\",\n    \"url\": \"https://example.com/email-events\",\n    \"version\": 1,\n    \"threshold\": null,\n    \"events\": [\n      \"delivery\",\n      \"engagement\"\n    ],\n    \"enabled\": true\n  },\n  \"message\": \"Webhook created\"\n}"
            },
            {
              "name": "400 Bad Request",
              "code": 400,
              "status": "Bad Request",
              "body": "{\n  \"error\": \"Email webhooks require a non-empty events array (subset of: delivery, engagement, unsubscribe)\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Enable / Disable Webhook",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/webhooks/:id",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "webhooks",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "Webhook id (24-hex)."
                }
              ]
            },
            "description": "Enable or disable an existing webhook by id. This is the only mutation on an existing webhook; `url`, `type`, and `events` are immutable (delete and recreate to change them).\n\n#### Path parameters\n| Field | Type | Required | Description |\n| --- | --- | --- | --- |\n| `id` | string | Yes | Webhook id (24-hex). |\n\n#### Request body parameters\n| Field | Type | Required | Description |\n| --- | --- | --- | --- |\n| `enabled` | boolean | Yes | `true` to enable, `false` to disable. |\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `webhook` | object | The updated webhook. |\n| `message` | string | Confirmation message. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Invalid webhook ID format` | 400 | `id` is not a valid 24-hex id. |\n| `Missing required field: enabled` | 400 | `enabled` not supplied. |\n| `Webhook not found` | 400 | No such webhook on this account. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\",\n  \"enabled\": false\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"webhook\": {\n    \"id\": \"683554c596937cc4b90f5cf7\",\n    \"type\": \"sms_events\",\n    \"url\": \"https://example.com/dlr\",\n    \"version\": 1,\n    \"threshold\": null,\n    \"events\": [],\n    \"enabled\": false\n  },\n  \"message\": \"Webhook disabled\"\n}"
            },
            {
              "name": "400 Bad Request",
              "code": 400,
              "status": "Bad Request",
              "body": "{\n  \"error\": \"Webhook not found\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        },
        {
          "name": "Delete Webhook",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url_sms}}/webhooks/:id",
              "host": [
                "{{base_url_sms}}"
              ],
              "path": [
                "webhooks",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "Webhook id (24-hex)."
                }
              ]
            },
            "description": "Delete a webhook configuration by id.\n\n#### Path parameters\n| Field | Type | Required | Description |\n| --- | --- | --- | --- |\n| `id` | string | Yes | Webhook id (24-hex). |\n\n#### Response fields\n| Field | Type | Description |\n| --- | --- | --- |\n| `error` | string | Empty on success. |\n| `message` | string | Confirmation message. |\n\n#### Errors\n| Error | HTTP | Cause |\n| --- | --- | --- |\n| `Invalid webhook ID format` | 400 | `id` is not a valid 24-hex id. |\n| `Webhook not found` | 400 | No such webhook on this account. |",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"key-id\": \"{{key_id}}\",\n  \"key-secret\": \"{{key_secret}}\"\n}"
            }
          },
          "response": [
            {
              "name": "200 OK",
              "code": 200,
              "status": "OK",
              "body": "{\n  \"error\": \"\",\n  \"message\": \"Webhook deleted\"\n}"
            },
            {
              "name": "401 Unauthorized",
              "code": 401,
              "status": "Unauthorized",
              "body": "{\n  \"error\": \"Failed (Invalid API ID or Key)\"\n}"
            }
          ]
        }
      ]
    }
  ]
}
