{
  "openapi": "3.1.0",
  "info": {
    "title": "VoidFix SMS API",
    "version": "1.0.0",
    "summary": "Send and receive SMS and MMS through your own Android phones.",
    "description": "The **VoidFix SMS API** lets your apps and AI agents send and receive SMS and MMS through the Android phones connected to your VoidFix account: send single, bulk or scheduled messages, follow delivery status, read conversations and incoming messages, manage contact lists and opt-outs, receive events by webhook and run USSD codes.\n\n## Get your API key\n\n1. Sign in at **https://sms.voidfix.com**.\n2. In the left menu, open **API & Settings**.\n3. In the **API key** section, click **Copy key** (or the eye icon to see it).\n\nDirect link: **https://sms.voidfix.com/configuration.php#api**\n\nKeep the key secret, like a password. Regenerating it on that page stops the old key working straight away.\n\n## Authentication\n\nAdd your API key as the **`key`** parameter on every request: in the query string for `GET` requests, or as a form field for `POST` requests.\n\n```bash\ncurl \"https://sms.voidfix.com/services/v2/get-account.php?key=YOUR_API_KEY\"\n```\n\n> In this reference, click **Authentication** and paste your key once: every \"Try it\" request will include it.\n\n## Quick Start\n\n**1. Check your key and see your phones**\n\n```bash\ncurl \"https://sms.voidfix.com/services/get-devices.php?key=YOUR_API_KEY\"\n```\n\n**2. Send a message**\n\n```bash\ncurl -X POST https://sms.voidfix.com/services/send.php \\\n  -d key=YOUR_API_KEY \\\n  -d number=+15551234567 \\\n  -d message=\"Hello from VoidFix\"\n```\n\n**3. Follow its status** with the `ID` from the response:\n\n```bash\ncurl \"https://sms.voidfix.com/services/read-messages.php?key=YOUR_API_KEY&id=1234\"\n```\n\n**4. Read replies** as they arrive, either by webhook (see **Webhooks**) or by polling:\n\n```bash\ncurl \"https://sms.voidfix.com/services/v2/list-messages.php?key=YOUR_API_KEY&direction=inbound&afterId=1234\"\n```\n\n## Requests\n\nEndpoints take `GET` query parameters or `POST` form fields (`application/x-www-form-urlencoded` or `multipart/form-data`), not a JSON body. Include the country code in phone numbers, e.g. `+15551234567`. SIM slots are `0` for SIM 1 and `1` for SIM 2. Timestamps are Unix seconds; dates in responses are ISO 8601.\n\n## Response format\n\nEvery response is JSON with the same envelope, returned with **HTTP 200 even when the request fails**. Always check `success`:\n\n```json\n{ \"success\": true,  \"data\": { ... }, \"error\": null }\n{ \"success\": false, \"data\": null, \"error\": { \"code\": 401, \"message\": \"This API key is invalid.\" } }\n```\n\n| `error.code` | Meaning |\n|---|---|\n| 400 | A required parameter is missing or invalid |\n| 401 | The API key is missing or wrong |\n| 403 | That resource isn't yours (e.g. another account's contact list) |\n| 404 | Nothing matched |\n| 500 | Something went wrong on our side |\n\n## Pagination\n\nList endpoints return `page`, `limit`, `total` and `hasMore`. **List messages** pages by message ID instead: pass the returned `next.beforeId` (older) or `next.afterId` (newer).\n\n## Choosing which phone sends\n\nUse `devices` on **Send messages**:\n\n- `devices=12` sends from device 12, SIM 1.\n- `devices=12&simSlot=1` sends from device 12, **SIM 2**.\n- `devices=[\"12|0\",\"15|1\"]` (JSON array) spreads messages across device 12 SIM 1 and device 15 SIM 2.\n- `option=1` uses every connected device; `option=2` every SIM of every device.\n- Leave it out to use your primary device.\n\n## For AI agents\n\n- Plain-text summary: `https://sms.voidfix.com/llms.txt`\n- This OpenAPI document: `https://sms.voidfix.com/openapi.json`\n",
    "contact": {
      "name": "VoidFix Support",
      "email": "support@voidfix.com",
      "url": "https://sms.voidfix.com"
    }
  },
  "servers": [
    {
      "url": "https://sms.voidfix.com",
      "description": "VoidFix SMS Gateway"
    }
  ],
  "security": [
    {
      "ApiKey": []
    }
  ],
  "tags": [
    {
      "name": "Messages",
      "description": "Send SMS and MMS, follow their status, list history, resend and cancel."
    },
    {
      "name": "Conversations",
      "description": "Threads with each person you text, with unread counts, like an inbox."
    },
    {
      "name": "Devices",
      "description": "The Android phones (and their SIMs) connected to your account."
    },
    {
      "name": "Contacts",
      "description": "Contact lists and the people in them."
    },
    {
      "name": "Opt-outs",
      "description": "Numbers you never send to (blacklist)."
    },
    {
      "name": "Templates",
      "description": "Saved message templates."
    },
    {
      "name": "Webhook settings",
      "description": "Choose the URL VoidFix sends events to. The events themselves (what we send and how to verify it) are listed under **Webhooks** at the bottom."
    },
    {
      "name": "USSD",
      "description": "Run USSD codes (like `*150#`) on a connected phone and read the responses."
    },
    {
      "name": "Account",
      "description": "Your account and balance."
    }
  ],
  "paths": {
    "/services/send.php": {
      "post": {
        "tags": [
          "Messages"
        ],
        "operationId": "sendMessages",
        "summary": "Send messages",
        "description": "Send one message to one or more numbers, a different message to each number, or one message to every subscriber of a contact list.\n\nProvide **one** of:\n- `number` + `message` — the same message to one number, a comma-separated list of numbers, or several `number[]` fields.\n- `messages` — a JSON array of `{\"number\", \"message\"}` objects, each with its own text.\n- `listID` + `message` — everyone subscribed to a contact list. Use `%name%` in the message to insert each contact's name.\n\nMessages are queued and sent by your phone. The response returns them with status `Pending` (or `Scheduled`); use **Get messages** or a webhook to follow their status.\n\nBefore sending a campaign, check your opt-outs with **List opt-outs**: numbers on it are skipped.",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/SendRequest"
              },
              "examples": {
                "single": {
                  "summary": "One message to one number",
                  "value": {
                    "number": "+15551234567",
                    "message": "Your order has shipped!"
                  }
                },
                "sim2": {
                  "summary": "Send from a specific phone and SIM 2",
                  "value": {
                    "number": "+15551234567",
                    "message": "Hi there",
                    "devices": "12",
                    "simSlot": 1
                  }
                },
                "many": {
                  "summary": "Same message to several numbers",
                  "value": {
                    "number": "+15551234567,+15557654321",
                    "message": "Reminder: meeting at 3pm"
                  }
                },
                "bulk": {
                  "summary": "A different message to each number",
                  "value": {
                    "messages": "[{\"number\":\"+15551234567\",\"message\":\"Hi Anna\"},{\"number\":\"+15557654321\",\"message\":\"Hi Ben\"}]",
                    "option": 1
                  }
                },
                "list": {
                  "summary": "Everyone in a contact list",
                  "value": {
                    "listID": 4,
                    "message": "Hi %name%, our sale starts today."
                  }
                },
                "mms": {
                  "summary": "MMS with an image",
                  "value": {
                    "number": "+15551234567",
                    "message": "Here is your ticket",
                    "type": "mms",
                    "attachments": "https://example.com/ticket.png"
                  }
                },
                "scheduled": {
                  "summary": "Schedule for later",
                  "value": {
                    "number": "+15551234567",
                    "message": "Happy birthday!",
                    "schedule": 1767225600
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The queued messages, or an error. Check `success`.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/MessagesResponse"
                    },
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ]
                },
                "examples": {
                  "success": {
                    "summary": "Queued",
                    "value": {
                      "success": true,
                      "data": {
                        "messages": [
                          {
                            "ID": 1234,
                            "number": "+15551234567",
                            "message": "Your order has shipped!",
                            "deviceID": 12,
                            "simSlot": 0,
                            "userID": 7,
                            "status": "Pending",
                            "type": "sms",
                            "attachments": null,
                            "sentDate": "2026-10-03T09:15:00+0000",
                            "deliveredDate": null,
                            "groupID": "kX9QpL2vR7tY0wZb3cDe67a1b2c3d4e5f6.12345678"
                          }
                        ]
                      },
                      "error": null
                    }
                  },
                  "badKey": {
                    "summary": "Wrong API key",
                    "value": {
                      "success": false,
                      "data": null,
                      "error": {
                        "code": 401,
                        "message": "Incorrect API key."
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Account"
        ],
        "operationId": "getBalance",
        "summary": "Get balance",
        "description": "Returns how many message credits you have left. `credits` is `null` when your plan is unlimited.\n\n(This is the Send endpoint called with only your API key.)",
        "responses": {
          "200": {
            "description": "Your credits, or an error.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "required": [
                        "success",
                        "data"
                      ],
                      "properties": {
                        "success": {
                          "const": true
                        },
                        "data": {
                          "type": "object",
                          "properties": {
                            "credits": {
                              "type": [
                                "integer",
                                "string",
                                "null"
                              ],
                              "description": "Credits left. `null` means unlimited.",
                              "examples": [
                                4870
                              ]
                            }
                          }
                        },
                        "error": {
                          "type": "null"
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ]
                },
                "example": {
                  "success": true,
                  "data": {
                    "credits": 4870
                  },
                  "error": null
                }
              }
            }
          }
        }
      }
    },
    "/services/read-messages.php": {
      "get": {
        "tags": [
          "Messages"
        ],
        "operationId": "getMessages",
        "summary": "Get messages",
        "description": "Look up sent and received messages. Combine any of the filters. Use `id` for one message, `groupId` for everything from one send request, or `status` (optionally with a device, SIM and time range) to find messages in a given state.\n\nReturns `error.code` 404 when nothing matches.\n\nFor paging through large histories, use **List messages** instead.",
        "parameters": [
          {
            "$ref": "#/components/parameters/MessageId"
          },
          {
            "$ref": "#/components/parameters/GroupId"
          },
          {
            "$ref": "#/components/parameters/Status"
          },
          {
            "$ref": "#/components/parameters/DeviceIdFilter"
          },
          {
            "$ref": "#/components/parameters/SimSlotFilter"
          },
          {
            "$ref": "#/components/parameters/StartTimestamp"
          },
          {
            "$ref": "#/components/parameters/EndTimestamp"
          }
        ],
        "responses": {
          "200": {
            "description": "Matching messages, or an error.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/MessagesResponse"
                    },
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/services/v2/list-messages.php": {
      "get": {
        "tags": [
          "Messages"
        ],
        "operationId": "listMessages",
        "summary": "List messages",
        "description": "Message history with filters and paging, newest first. Use it to:\n\n- **Show one conversation:** `number=+15551234567`\n- **Poll for new replies:** `direction=inbound&afterId=<highest ID you've seen>` (oldest first, so you can process them in order)\n- **Follow a send request:** `groupId=...`\n\nPage through results with the returned `next.beforeId` / `next.afterId`.",
        "parameters": [
          {
            "name": "number",
            "in": "query",
            "description": "Only messages with this number (or comma-separated numbers).",
            "schema": {
              "type": "string"
            },
            "example": "+15551234567"
          },
          {
            "name": "direction",
            "in": "query",
            "description": "`inbound` = received, `outbound` = sent by you.",
            "schema": {
              "type": "string",
              "enum": [
                "inbound",
                "outbound"
              ]
            }
          },
          {
            "name": "status",
            "in": "query",
            "description": "One status or a comma-separated list, e.g. `Failed` or `Sent,Delivered`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "groupId",
            "in": "query",
            "description": "Only messages from this send request.",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/DeviceIdFilter"
          },
          {
            "$ref": "#/components/parameters/SimSlotFilter"
          },
          {
            "$ref": "#/components/parameters/StartTimestamp"
          },
          {
            "$ref": "#/components/parameters/EndTimestamp"
          },
          {
            "name": "afterId",
            "in": "query",
            "description": "Only messages with a higher ID (newer). Results come oldest first.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "beforeId",
            "in": "query",
            "description": "Only messages with a lower ID (older).",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Messages per page (1-200).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Check `success`. On failure, `error` explains why.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "required": [
                        "success",
                        "data"
                      ],
                      "properties": {
                        "success": {
                          "const": true
                        },
                        "data": {
                          "type": "object",
                          "properties": {
                            "messages": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/ApiMessage"
                              }
                            },
                            "hasMore": {
                              "type": "boolean"
                            },
                            "next": {
                              "type": [
                                "object",
                                "null"
                              ],
                              "description": "Parameters for the next page, e.g. `{\"beforeId\": 1180}`."
                            }
                          }
                        },
                        "error": {
                          "type": "null"
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ]
                },
                "example": {
                  "success": true,
                  "data": {
                    "messages": [
                      {
                        "ID": 1240,
                        "number": "+15551234567",
                        "message": "Yes, see you then",
                        "direction": "inbound",
                        "status": "Received",
                        "type": "sms",
                        "attachments": null,
                        "deviceID": 12,
                        "simSlot": 0,
                        "groupID": null,
                        "sentDate": "2026-10-03T09:20:11+00:00",
                        "deliveredDate": null
                      }
                    ],
                    "hasMore": true,
                    "next": {
                      "beforeId": 1240
                    }
                  },
                  "error": null
                }
              }
            }
          }
        }
      }
    },
    "/services/resend.php": {
      "post": {
        "tags": [
          "Messages"
        ],
        "operationId": "resendMessages",
        "summary": "Resend messages",
        "description": "Send messages again, for example the ones that failed. Pick them with the same filters as **Get messages**: `id` for one message, `groupId` (optionally with `status`) for a send request, or `status` with an optional device, SIM and time range.\n\nReturns `error.code` 404 when nothing matches.",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/MessageFilter"
              },
              "examples": {
                "one": {
                  "summary": "One message",
                  "value": {
                    "id": 1234
                  }
                },
                "failedInGroup": {
                  "summary": "Failed messages from one send",
                  "value": {
                    "groupId": "kX9QpL2vR7tY0wZb3cDe67a1b2c3d4e5f6.12345678",
                    "status": "Failed"
                  }
                },
                "failedToday": {
                  "summary": "Everything that failed since a time",
                  "value": {
                    "status": "Failed",
                    "startTimestamp": 1767139200
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The messages that were queued again, or an error.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/MessagesResponse"
                    },
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/services/v2/cancel-messages.php": {
      "post": {
        "tags": [
          "Messages"
        ],
        "operationId": "cancelMessages",
        "summary": "Cancel messages",
        "description": "Cancel messages that haven't gone out yet (status `Pending`, `Queued` or `Scheduled`). Messages that were already sent, delivered or failed are left alone.\n\n`Queued` means the phone has already picked the message up, so on rare occasions it may still go out.",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "integer",
                    "description": "One message."
                  },
                  "groupId": {
                    "type": "string",
                    "description": "Every message from one send request."
                  }
                }
              },
              "examples": {
                "one": {
                  "summary": "One message",
                  "value": {
                    "id": 1234
                  }
                },
                "group": {
                  "summary": "A whole send request",
                  "value": {
                    "groupId": "kX9QpL2vR7tY0wZb3cDe67a1b2c3d4e5f6.12345678"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Check `success`. On failure, `error` explains why.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "required": [
                        "success",
                        "data"
                      ],
                      "properties": {
                        "success": {
                          "const": true
                        },
                        "data": {
                          "type": "object",
                          "properties": {
                            "canceled": {
                              "type": "integer"
                            },
                            "ids": {
                              "type": "array",
                              "items": {
                                "type": "integer"
                              }
                            }
                          }
                        },
                        "error": {
                          "type": "null"
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ]
                },
                "example": {
                  "success": true,
                  "data": {
                    "canceled": 2,
                    "ids": [
                      1234,
                      1235
                    ]
                  },
                  "error": null
                }
              }
            }
          }
        }
      }
    },
    "/services/v2/get-message-stats.php": {
      "get": {
        "tags": [
          "Messages"
        ],
        "operationId": "getMessageStats",
        "summary": "Get message statistics",
        "description": "How many messages are in each status over a time range, plus totals and the success rate. Defaults to the last 30 days.",
        "parameters": [
          {
            "$ref": "#/components/parameters/StartTimestamp"
          },
          {
            "$ref": "#/components/parameters/EndTimestamp"
          },
          {
            "$ref": "#/components/parameters/DeviceIdFilter"
          },
          {
            "$ref": "#/components/parameters/SimSlotFilter"
          }
        ],
        "responses": {
          "200": {
            "description": "Check `success`. On failure, `error` explains why.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "required": [
                        "success",
                        "data"
                      ],
                      "properties": {
                        "success": {
                          "const": true
                        },
                        "data": {
                          "type": "object",
                          "properties": {
                            "from": {
                              "type": "string",
                              "format": "date-time"
                            },
                            "to": {
                              "type": "string",
                              "format": "date-time"
                            },
                            "byStatus": {
                              "type": "object",
                              "additionalProperties": {
                                "type": "integer"
                              }
                            },
                            "totals": {
                              "type": "object",
                              "properties": {
                                "outbound": {
                                  "type": "integer"
                                },
                                "inbound": {
                                  "type": "integer"
                                },
                                "sent": {
                                  "type": "integer"
                                },
                                "failed": {
                                  "type": "integer"
                                },
                                "waiting": {
                                  "type": "integer"
                                }
                              }
                            },
                            "successRate": {
                              "type": [
                                "number",
                                "null"
                              ],
                              "description": "% of finished outbound messages that went out (Sent or Delivered)."
                            }
                          }
                        },
                        "error": {
                          "type": "null"
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ]
                },
                "example": {
                  "success": true,
                  "data": {
                    "from": "2026-09-03T00:00:00+00:00",
                    "to": "2026-10-03T00:00:00+00:00",
                    "byStatus": {
                      "Pending": 2,
                      "Queued": 0,
                      "Scheduled": 5,
                      "Sent": 820,
                      "Delivered": 1310,
                      "Failed": 41,
                      "Received": 466,
                      "Canceled": 3
                    },
                    "totals": {
                      "outbound": 2181,
                      "inbound": 466,
                      "sent": 2130,
                      "failed": 41,
                      "waiting": 7
                    },
                    "successRate": 98.1
                  },
                  "error": null
                }
              }
            }
          }
        }
      }
    },
    "/services/v2/get-conversations.php": {
      "get": {
        "tags": [
          "Conversations"
        ],
        "operationId": "getConversations",
        "summary": "List conversations",
        "description": "One row per person you've texted with, most recent activity first, with the last message and the number of unread incoming messages. Read state is shared with the dashboard chat.\n\nOpen a thread with **List messages** (`number=`), then **Mark as read**.",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "Page number, starting at 1.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Items per page (1-100).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "search",
            "in": "query",
            "description": "Part of a phone number.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "unread",
            "in": "query",
            "description": "1 = only conversations with unread messages.",
            "schema": {
              "type": "integer",
              "enum": [
                0,
                1
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Check `success`. On failure, `error` explains why.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "required": [
                        "success",
                        "data"
                      ],
                      "properties": {
                        "success": {
                          "const": true
                        },
                        "data": {
                          "type": "object",
                          "properties": {
                            "conversations": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/Conversation"
                              }
                            },
                            "page": {
                              "type": "integer"
                            },
                            "limit": {
                              "type": "integer"
                            },
                            "total": {
                              "type": "integer"
                            },
                            "hasMore": {
                              "type": "boolean"
                            }
                          }
                        },
                        "error": {
                          "type": "null"
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ]
                },
                "example": {
                  "success": true,
                  "data": {
                    "conversations": [
                      {
                        "number": "+15551234567",
                        "messageCount": 14,
                        "unreadCount": 1,
                        "lastMessage": {
                          "ID": 1240,
                          "number": "+15551234567",
                          "message": "Yes, see you then",
                          "direction": "inbound",
                          "status": "Received",
                          "type": "sms",
                          "attachments": null,
                          "deviceID": 12,
                          "simSlot": 0,
                          "groupID": null,
                          "sentDate": "2026-10-03T09:20:11+00:00",
                          "deliveredDate": null
                        }
                      }
                    ],
                    "page": 1,
                    "limit": 20,
                    "total": 1,
                    "hasMore": false
                  },
                  "error": null
                }
              }
            }
          }
        }
      }
    },
    "/services/v2/mark-read.php": {
      "post": {
        "tags": [
          "Conversations"
        ],
        "operationId": "markConversationRead",
        "summary": "Mark as read",
        "description": "Mark a conversation as read, up to `lastId` or everything so far. The unread count in **List conversations** and the dashboard chat updates accordingly.",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "number": {
                    "type": "string",
                    "description": "The other person's number."
                  },
                  "lastId": {
                    "type": "integer",
                    "description": "Mark messages up to this ID. Omit for all."
                  }
                },
                "required": [
                  "number"
                ]
              },
              "examples": {
                "all": {
                  "summary": "Everything so far",
                  "value": {
                    "number": "+15551234567"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Check `success`. On failure, `error` explains why.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "required": [
                        "success",
                        "data"
                      ],
                      "properties": {
                        "success": {
                          "const": true
                        },
                        "data": {
                          "type": "object",
                          "properties": {
                            "number": {
                              "type": "string"
                            },
                            "lastReadId": {
                              "type": "integer"
                            }
                          }
                        },
                        "error": {
                          "type": "null"
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ]
                },
                "example": {
                  "success": true,
                  "data": {
                    "number": "+15551234567",
                    "lastReadId": 1240
                  },
                  "error": null
                }
              }
            }
          }
        }
      }
    },
    "/services/get-devices.php": {
      "get": {
        "tags": [
          "Devices"
        ],
        "operationId": "listDevices",
        "summary": "List devices",
        "description": "The phones connected to your account, with their SIMs. Use a device's `id` (and a SIM slot from `sims`) in the `devices` / `simSlot` fields of **Send messages**.",
        "responses": {
          "200": {
            "description": "Your devices, or an error.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "required": [
                        "success",
                        "data"
                      ],
                      "properties": {
                        "success": {
                          "const": true
                        },
                        "data": {
                          "type": "object",
                          "properties": {
                            "devices": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/Device"
                              }
                            }
                          }
                        },
                        "error": {
                          "type": "null"
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ]
                },
                "example": {
                  "success": true,
                  "data": {
                    "devices": [
                      {
                        "id": 12,
                        "name": "Office phone",
                        "model": "Pixel 8",
                        "androidVersion": "14",
                        "appVersion": "17",
                        "sims": {
                          "0": "SIM #1 [+15551234567]",
                          "1": "SIM #2 [+15557654321]"
                        }
                      }
                    ]
                  },
                  "error": null
                }
              }
            }
          }
        }
      }
    },
    "/services/v2/get-contacts-lists.php": {
      "get": {
        "tags": [
          "Contacts"
        ],
        "operationId": "listContactsLists",
        "summary": "List contact lists",
        "description": "Your contact lists, with how many contacts each has. Use a list's `ID` as `listID` when sending.",
        "responses": {
          "200": {
            "description": "Check `success`. On failure, `error` explains why.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "required": [
                        "success",
                        "data"
                      ],
                      "properties": {
                        "success": {
                          "const": true
                        },
                        "data": {
                          "type": "object",
                          "properties": {
                            "lists": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/ContactsList"
                              }
                            }
                          }
                        },
                        "error": {
                          "type": "null"
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ]
                },
                "example": {
                  "success": true,
                  "data": {
                    "lists": [
                      {
                        "ID": 4,
                        "name": "Customers",
                        "contacts": 812,
                        "subscribed": 790
                      }
                    ]
                  },
                  "error": null
                }
              }
            }
          }
        }
      }
    },
    "/services/v2/add-contacts-list.php": {
      "post": {
        "tags": [
          "Contacts"
        ],
        "operationId": "createContactsList",
        "summary": "Create a contact list",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "List name, unique in your account."
                  }
                },
                "required": [
                  "name"
                ]
              },
              "examples": {
                "new": {
                  "summary": "New list",
                  "value": {
                    "name": "VIP customers"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Check `success`. On failure, `error` explains why.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "required": [
                        "success",
                        "data"
                      ],
                      "properties": {
                        "success": {
                          "const": true
                        },
                        "data": {
                          "type": "object",
                          "properties": {
                            "list": {
                              "$ref": "#/components/schemas/ContactsList"
                            }
                          }
                        },
                        "error": {
                          "type": "null"
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ]
                },
                "example": {
                  "success": true,
                  "data": {
                    "list": {
                      "ID": 9,
                      "name": "VIP customers",
                      "contacts": 0,
                      "subscribed": 0
                    }
                  },
                  "error": null
                }
              }
            }
          }
        }
      }
    },
    "/services/v2/get-contacts.php": {
      "get": {
        "tags": [
          "Contacts"
        ],
        "operationId": "listContacts",
        "summary": "List contacts",
        "description": "The contacts in one of your lists.",
        "parameters": [
          {
            "name": "listID",
            "in": "query",
            "description": "The contact list.",
            "schema": {
              "type": "integer"
            },
            "required": true,
            "example": 4
          },
          {
            "name": "page",
            "in": "query",
            "description": "Page number, starting at 1.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Items per page (1-500).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          },
          {
            "name": "search",
            "in": "query",
            "description": "Part of a name or number.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "subscribed",
            "in": "query",
            "description": "1 = only subscribed, 0 = only unsubscribed.",
            "schema": {
              "type": "integer",
              "enum": [
                0,
                1
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Check `success`. On failure, `error` explains why.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "required": [
                        "success",
                        "data"
                      ],
                      "properties": {
                        "success": {
                          "const": true
                        },
                        "data": {
                          "type": "object",
                          "properties": {
                            "listID": {
                              "type": "integer"
                            },
                            "contacts": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/ListedContact"
                              }
                            },
                            "page": {
                              "type": "integer"
                            },
                            "limit": {
                              "type": "integer"
                            },
                            "total": {
                              "type": "integer"
                            },
                            "hasMore": {
                              "type": "boolean"
                            }
                          }
                        },
                        "error": {
                          "type": "null"
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ]
                },
                "example": {
                  "success": true,
                  "data": {
                    "listID": 4,
                    "contacts": [
                      {
                        "ID": 5512,
                        "name": "Anna",
                        "number": "+15551234567",
                        "subscribed": true
                      }
                    ],
                    "page": 1,
                    "limit": 100,
                    "total": 1,
                    "hasMore": false
                  },
                  "error": null
                }
              }
            }
          }
        }
      }
    },
    "/services/manage-contacts.php": {
      "post": {
        "tags": [
          "Contacts"
        ],
        "operationId": "manageContact",
        "summary": "Add or unsubscribe a contact",
        "description": "Add a number to one of your contact lists, re-subscribe someone who left, or unsubscribe them.\n\n- Add: `listID` + `number` (+ optional `name`). Fails with 400 if the contact already exists, unless `resubscribe=1`.\n- Unsubscribe: `listID` + `number` + `unsubscribe=1`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/ContactRequest"
              },
              "examples": {
                "add": {
                  "summary": "Add a contact",
                  "value": {
                    "listID": 4,
                    "number": "+15551234567",
                    "name": "Anna"
                  }
                },
                "resubscribe": {
                  "summary": "Re-subscribe",
                  "value": {
                    "listID": 4,
                    "number": "+15551234567",
                    "resubscribe": 1
                  }
                },
                "unsubscribe": {
                  "summary": "Unsubscribe",
                  "value": {
                    "listID": 4,
                    "number": "+15551234567",
                    "unsubscribe": 1
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The saved contact, or an error.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "required": [
                        "success",
                        "data"
                      ],
                      "properties": {
                        "success": {
                          "const": true
                        },
                        "data": {
                          "type": "object",
                          "properties": {
                            "contact": {
                              "$ref": "#/components/schemas/Contact"
                            }
                          }
                        },
                        "error": {
                          "type": "null"
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/services/v2/get-blacklist.php": {
      "get": {
        "tags": [
          "Opt-outs"
        ],
        "operationId": "listOptOuts",
        "summary": "List opt-outs",
        "description": "Numbers you never send to. Messages to these numbers are skipped automatically.",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "Page number, starting at 1.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Items per page (1-500).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          },
          {
            "name": "search",
            "in": "query",
            "description": "Part of a number.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Check `success`. On failure, `error` explains why.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "required": [
                        "success",
                        "data"
                      ],
                      "properties": {
                        "success": {
                          "const": true
                        },
                        "data": {
                          "type": "object",
                          "properties": {
                            "numbers": {
                              "type": "array",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "ID": {
                                    "type": "integer"
                                  },
                                  "number": {
                                    "type": "string"
                                  }
                                }
                              }
                            },
                            "page": {
                              "type": "integer"
                            },
                            "limit": {
                              "type": "integer"
                            },
                            "total": {
                              "type": "integer"
                            },
                            "hasMore": {
                              "type": "boolean"
                            }
                          }
                        },
                        "error": {
                          "type": "null"
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ]
                },
                "example": {
                  "success": true,
                  "data": {
                    "numbers": [
                      {
                        "ID": 31,
                        "number": "+15550001111"
                      }
                    ],
                    "page": 1,
                    "limit": 100,
                    "total": 1,
                    "hasMore": false
                  },
                  "error": null
                }
              }
            }
          }
        }
      }
    },
    "/services/v2/add-to-blacklist.php": {
      "post": {
        "tags": [
          "Opt-outs"
        ],
        "operationId": "addOptOut",
        "summary": "Add opt-outs",
        "description": "Stop sending to one or more numbers, for example when someone replies STOP.",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "number": {
                    "type": "string",
                    "description": "One number, or several separated by commas (or repeat `number[]`)."
                  }
                },
                "required": [
                  "number"
                ]
              },
              "examples": {
                "one": {
                  "summary": "Opt out one number",
                  "value": {
                    "number": "+15550001111"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Check `success`. On failure, `error` explains why.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "required": [
                        "success",
                        "data"
                      ],
                      "properties": {
                        "success": {
                          "const": true
                        },
                        "data": {
                          "type": "object",
                          "properties": {
                            "added": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              }
                            },
                            "alreadyBlacklisted": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              }
                            }
                          }
                        },
                        "error": {
                          "type": "null"
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ]
                },
                "example": {
                  "success": true,
                  "data": {
                    "added": [
                      "+15550001111"
                    ],
                    "alreadyBlacklisted": []
                  },
                  "error": null
                }
              }
            }
          }
        }
      }
    },
    "/services/v2/remove-from-blacklist.php": {
      "post": {
        "tags": [
          "Opt-outs"
        ],
        "operationId": "removeOptOut",
        "summary": "Remove opt-outs",
        "description": "Allow sending to numbers again.",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "number": {
                    "type": "string",
                    "description": "One number, or several separated by commas (or repeat `number[]`)."
                  }
                },
                "required": [
                  "number"
                ]
              },
              "examples": {
                "one": {
                  "summary": "Remove one number",
                  "value": {
                    "number": "+15550001111"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Check `success`. On failure, `error` explains why.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "required": [
                        "success",
                        "data"
                      ],
                      "properties": {
                        "success": {
                          "const": true
                        },
                        "data": {
                          "type": "object",
                          "properties": {
                            "removed": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              }
                            },
                            "notBlacklisted": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              }
                            }
                          }
                        },
                        "error": {
                          "type": "null"
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ]
                },
                "example": {
                  "success": true,
                  "data": {
                    "removed": [
                      "+15550001111"
                    ],
                    "notBlacklisted": []
                  },
                  "error": null
                }
              }
            }
          }
        }
      }
    },
    "/services/v2/get-templates.php": {
      "get": {
        "tags": [
          "Templates"
        ],
        "operationId": "listTemplates",
        "summary": "List templates",
        "description": "Your saved message templates (created in the dashboard under Templates).",
        "responses": {
          "200": {
            "description": "Check `success`. On failure, `error` explains why.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "required": [
                        "success",
                        "data"
                      ],
                      "properties": {
                        "success": {
                          "const": true
                        },
                        "data": {
                          "type": "object",
                          "properties": {
                            "templates": {
                              "type": "array",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "ID": {
                                    "type": "integer"
                                  },
                                  "name": {
                                    "type": "string"
                                  },
                                  "message": {
                                    "type": "string"
                                  }
                                }
                              }
                            }
                          }
                        },
                        "error": {
                          "type": "null"
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ]
                },
                "example": {
                  "success": true,
                  "data": {
                    "templates": [
                      {
                        "ID": 3,
                        "name": "Appointment reminder",
                        "message": "Hi %name%, see you tomorrow at 10am."
                      }
                    ]
                  },
                  "error": null
                }
              }
            }
          }
        }
      }
    },
    "/services/v2/get-webhook.php": {
      "get": {
        "tags": [
          "Webhook settings"
        ],
        "operationId": "getWebhook",
        "summary": "Get webhook URL",
        "description": "The URL VoidFix posts events to, or `null` when none is set.",
        "responses": {
          "200": {
            "description": "Check `success`. On failure, `error` explains why.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "required": [
                        "success",
                        "data"
                      ],
                      "properties": {
                        "success": {
                          "const": true
                        },
                        "data": {
                          "type": "object",
                          "properties": {
                            "webhook": {
                              "type": "object",
                              "properties": {
                                "url": {
                                  "type": [
                                    "string",
                                    "null"
                                  ]
                                }
                              }
                            }
                          }
                        },
                        "error": {
                          "type": "null"
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ]
                },
                "example": {
                  "success": true,
                  "data": {
                    "webhook": {
                      "url": "https://your-app.com/voidfix-webhook"
                    }
                  },
                  "error": null
                }
              }
            }
          }
        }
      }
    },
    "/services/v2/set-webhook.php": {
      "post": {
        "tags": [
          "Webhook settings"
        ],
        "operationId": "setWebhook",
        "summary": "Set webhook URL",
        "description": "Set the URL VoidFix posts events to (the events and their payloads are listed under **Webhooks** at the bottom). Send an empty `url` to remove it. Same setting as **API & Settings → Webhooks**.",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "description": "An http(s) URL. Empty to remove."
                  }
                }
              },
              "examples": {
                "set": {
                  "summary": "Set the URL",
                  "value": {
                    "url": "https://your-app.com/voidfix-webhook"
                  }
                },
                "remove": {
                  "summary": "Remove it",
                  "value": {
                    "url": ""
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Check `success`. On failure, `error` explains why.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "required": [
                        "success",
                        "data"
                      ],
                      "properties": {
                        "success": {
                          "const": true
                        },
                        "data": {
                          "type": "object",
                          "properties": {
                            "webhook": {
                              "type": "object",
                              "properties": {
                                "url": {
                                  "type": [
                                    "string",
                                    "null"
                                  ]
                                }
                              }
                            }
                          }
                        },
                        "error": {
                          "type": "null"
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ]
                },
                "example": {
                  "success": true,
                  "data": {
                    "webhook": {
                      "url": "https://your-app.com/voidfix-webhook"
                    }
                  },
                  "error": null
                }
              }
            }
          }
        }
      }
    },
    "/services/send-ussd-request.php": {
      "post": {
        "tags": [
          "USSD"
        ],
        "operationId": "sendUssdRequest",
        "summary": "Send a USSD request",
        "description": "Ask a connected phone to dial a USSD code, such as `*150#` to check airtime. The response arrives later: read it with **Get USSD requests** or receive it on your webhook (`ussdRequest` event).",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "request",
                  "device"
                ],
                "properties": {
                  "request": {
                    "type": "string",
                    "description": "The USSD code to dial.",
                    "examples": [
                      "*150#"
                    ]
                  },
                  "device": {
                    "type": "integer",
                    "description": "ID of the device that should dial it (from **List devices**).",
                    "examples": [
                      12
                    ]
                  },
                  "sim": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "SIM slot to use: 0 for SIM 1, 1 for SIM 2. Defaults to the phone's default SIM.",
                    "examples": [
                      0
                    ]
                  }
                }
              },
              "example": {
                "request": "*150#",
                "device": 12,
                "sim": 0
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The created request, or an error.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "required": [
                        "success",
                        "data"
                      ],
                      "properties": {
                        "success": {
                          "const": true
                        },
                        "data": {
                          "type": "object",
                          "properties": {
                            "request": {
                              "$ref": "#/components/schemas/UssdRequest"
                            }
                          }
                        },
                        "error": {
                          "type": "null"
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/services/read-ussd-requests.php": {
      "get": {
        "tags": [
          "USSD"
        ],
        "operationId": "getUssdRequests",
        "summary": "Get USSD requests",
        "description": "Look up USSD requests and their responses. Returns `error.code` 404 when nothing matches.",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "description": "ID of one USSD request.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "request",
            "in": "query",
            "description": "Only requests with this exact code, e.g. `*150#`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/DeviceIdFilter"
          },
          {
            "$ref": "#/components/parameters/SimSlotFilter"
          },
          {
            "$ref": "#/components/parameters/StartTimestamp"
          },
          {
            "$ref": "#/components/parameters/EndTimestamp"
          }
        ],
        "responses": {
          "200": {
            "description": "Matching USSD requests, or an error.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "required": [
                        "success",
                        "data"
                      ],
                      "properties": {
                        "success": {
                          "const": true
                        },
                        "data": {
                          "type": "object",
                          "properties": {
                            "requests": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/UssdRequest"
                              }
                            }
                          }
                        },
                        "error": {
                          "type": "null"
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/services/v2/get-account.php": {
      "get": {
        "tags": [
          "Account"
        ],
        "operationId": "getAccount",
        "summary": "Get account",
        "description": "Who the API key belongs to: name, email, timezone, credits, plan expiry, connected phones and webhook status. A good first call to check a key.",
        "responses": {
          "200": {
            "description": "Check `success`. On failure, `error` explains why.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "required": [
                        "success",
                        "data"
                      ],
                      "properties": {
                        "success": {
                          "const": true
                        },
                        "data": {
                          "type": "object",
                          "properties": {
                            "account": {
                              "$ref": "#/components/schemas/Account"
                            }
                          }
                        },
                        "error": {
                          "type": "null"
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ]
                },
                "example": {
                  "success": true,
                  "data": {
                    "account": {
                      "ID": 7,
                      "name": "Anna Smith",
                      "email": "anna@example.com",
                      "timeZone": "America/New_York",
                      "credits": null,
                      "unlimitedCredits": true,
                      "expiryDate": null,
                      "connectedDevices": 2,
                      "webhookEnabled": true
                    }
                  },
                  "error": null
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {
    "messages": {
      "post": {
        "summary": "Event: messages (received or status changed)",
        "description": "Sent to your webhook URL when you receive a message, or when a message you sent changes status (sent, delivered, failed).\n\nThe body is a form post with one field, `messages`, whose value is a **JSON string** containing an array of Message objects.\n\n**Verify it came from VoidFix:** compute `base64(HMAC-SHA256(<value of the messages field>, YOUR_API_KEY))` and compare it with the `X-SG-Signature` header.\n\n```php\n$payload   = $_POST['messages'];\n$expected  = base64_encode(hash_hmac('sha256', $payload, 'YOUR_API_KEY', true));\nif (hash_equals($expected, $_SERVER['HTTP_X_SG_SIGNATURE'] ?? '')) {\n    $messages = json_decode($payload, true);\n}\n```\n\n```javascript\n// Node.js (Express with express.urlencoded())\nconst crypto = require('crypto');\nconst expected = crypto.createHmac('sha256', process.env.VOIDFIX_API_KEY).update(req.body.messages).digest('base64');\nif (expected === req.get('X-SG-Signature')) { const messages = JSON.parse(req.body.messages); }\n```\n\nReply with any 2xx status.\n\nSet the URL in **API & Settings → Webhooks** or with **Set webhook URL**.",
        "parameters": [
          {
            "name": "X-SG-Signature",
            "in": "header",
            "required": true,
            "description": "base64 HMAC-SHA256 of the `messages` field, keyed with your API key.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "messages"
                ],
                "properties": {
                  "messages": {
                    "type": "string",
                    "contentMediaType": "application/json",
                    "description": "JSON array of Message objects."
                  }
                }
              },
              "example": {
                "messages": "[{\"ID\":1240,\"number\":\"+15551234567\",\"message\":\"Yes, see you then\",\"deviceID\":12,\"simSlot\":0,\"status\":\"Received\",\"type\":\"sms\",\"sentDate\":\"2026-10-03T09:20:11+0000\"}]"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Return any 2xx status to acknowledge."
          }
        }
      }
    },
    "ussdRequest": {
      "post": {
        "summary": "Event: ussdRequest (USSD reply)",
        "description": "Sent when a USSD request gets its response from the network. The form field `ussdRequest` holds a JSON-encoded UssdRequest. It is signed the same way as `messages` (`X-SG-Signature` over the `ussdRequest` value).",
        "parameters": [
          {
            "name": "X-SG-Signature",
            "in": "header",
            "required": true,
            "description": "base64 HMAC-SHA256 of the `ussdRequest` field, keyed with your API key.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "ussdRequest"
                ],
                "properties": {
                  "ussdRequest": {
                    "type": "string",
                    "contentMediaType": "application/json",
                    "description": "JSON-encoded UssdRequest."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Return any 2xx status to acknowledge."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKey": {
        "type": "apiKey",
        "in": "query",
        "name": "key",
        "description": "Your API key from **API & Settings → API key** (https://sms.voidfix.com/configuration.php#api). Send it as the `key` query parameter, or as a `key` form field in POST requests."
      }
    },
    "parameters": {
      "MessageId": {
        "name": "id",
        "in": "query",
        "description": "ID of one message.",
        "schema": {
          "type": "integer"
        },
        "example": 1234
      },
      "GroupId": {
        "name": "groupId",
        "in": "query",
        "description": "`groupID` returned by **Send messages**: every message from that request.",
        "schema": {
          "type": "string"
        }
      },
      "Status": {
        "name": "status",
        "in": "query",
        "description": "Only messages with this status.",
        "schema": {
          "$ref": "#/components/schemas/MessageStatus"
        }
      },
      "DeviceIdFilter": {
        "name": "deviceID",
        "in": "query",
        "description": "Only this device.",
        "schema": {
          "type": "integer"
        }
      },
      "SimSlotFilter": {
        "name": "simSlot",
        "in": "query",
        "description": "Only this SIM: 0 for SIM 1, 1 for SIM 2.",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      },
      "StartTimestamp": {
        "name": "startTimestamp",
        "in": "query",
        "description": "Only items sent at or after this Unix timestamp (seconds).",
        "schema": {
          "type": "integer"
        },
        "example": 1767139200
      },
      "EndTimestamp": {
        "name": "endTimestamp",
        "in": "query",
        "description": "Only items sent at or before this Unix timestamp (seconds).",
        "schema": {
          "type": "integer"
        },
        "example": 1767225600
      }
    },
    "schemas": {
      "SendRequest": {
        "type": "object",
        "description": "Provide `number` + `message`, or `messages`, or `listID` + `message`.",
        "properties": {
          "number": {
            "type": "string",
            "description": "Recipient number with country code. Several numbers: separate with commas, or repeat as `number[]`.",
            "examples": [
              "+15551234567"
            ]
          },
          "message": {
            "type": "string",
            "description": "Message text. With `listID`, `%name%` is replaced by each contact's name.",
            "examples": [
              "Your order has shipped!"
            ]
          },
          "messages": {
            "type": "string",
            "contentMediaType": "application/json",
            "description": "JSON array of `{\"number\": \"...\", \"message\": \"...\"}` to send a different text to each number.",
            "examples": [
              "[{\"number\":\"+15551234567\",\"message\":\"Hi Anna\"}]"
            ]
          },
          "listID": {
            "type": "integer",
            "description": "Send `message` to every subscribed contact in this contact list."
          },
          "devices": {
            "type": "string",
            "description": "Which phone sends. A device ID (`12`), or a JSON array of `\"deviceID|simSlot\"` entries (`[\"12|0\",\"15|1\"]`). Omit to use your primary device.",
            "examples": [
              "12"
            ]
          },
          "simSlot": {
            "type": "integer",
            "minimum": 0,
            "description": "With a single device ID in `devices`: 0 = SIM 1, 1 = SIM 2. Defaults to 0.",
            "examples": [
              1
            ]
          },
          "option": {
            "type": "integer",
            "enum": [
              0,
              1,
              2
            ],
            "description": "0 = use `devices` (default). 1 = every connected device. 2 = every SIM of every device. Messages are spread across them."
          },
          "useRandomDevice": {
            "type": "integer",
            "enum": [
              0,
              1
            ],
            "description": "1 = send all messages from one randomly chosen device out of the selected ones."
          },
          "type": {
            "type": "string",
            "enum": [
              "sms",
              "mms"
            ],
            "default": "sms",
            "description": "`mms` to send `attachments`."
          },
          "attachments": {
            "type": "string",
            "description": "MMS only. Comma-separated public URLs of the files to attach.",
            "examples": [
              "https://example.com/ticket.png"
            ]
          },
          "schedule": {
            "type": "integer",
            "description": "Unix timestamp (seconds) to send at, instead of now.",
            "examples": [
              1767225600
            ]
          },
          "prioritize": {
            "type": "integer",
            "enum": [
              0,
              1
            ],
            "default": 0,
            "description": "1 = send ahead of queued messages, and even during your quiet hours."
          }
        }
      },
      "MessageFilter": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "ID of one message."
          },
          "groupId": {
            "type": "string",
            "description": "All messages from one send request."
          },
          "status": {
            "$ref": "#/components/schemas/MessageStatus"
          },
          "deviceID": {
            "type": "integer",
            "description": "Only this device."
          },
          "simSlot": {
            "type": "integer",
            "minimum": 0,
            "description": "Only this SIM: 0 = SIM 1, 1 = SIM 2."
          },
          "startTimestamp": {
            "type": "integer",
            "description": "Unix timestamp (seconds): sent at or after."
          },
          "endTimestamp": {
            "type": "integer",
            "description": "Unix timestamp (seconds): sent at or before."
          }
        }
      },
      "ContactRequest": {
        "type": "object",
        "required": [
          "listID",
          "number"
        ],
        "properties": {
          "listID": {
            "type": "integer",
            "description": "ID of one of your contact lists."
          },
          "number": {
            "type": "string",
            "description": "The contact's number with country code.",
            "examples": [
              "+15551234567"
            ]
          },
          "name": {
            "type": "string",
            "description": "The contact's name (used for `%name%`)."
          },
          "resubscribe": {
            "type": "integer",
            "enum": [
              0,
              1
            ],
            "description": "1 = subscribe again if the contact already exists."
          },
          "unsubscribe": {
            "type": "integer",
            "enum": [
              0,
              1
            ],
            "description": "1 = unsubscribe this contact."
          }
        }
      },
      "MessageStatus": {
        "type": "string",
        "enum": [
          "Pending",
          "Queued",
          "Scheduled",
          "Sent",
          "Delivered",
          "Failed",
          "Received",
          "Canceled"
        ],
        "description": "`Pending`/`Queued`: waiting for the phone. `Scheduled`: waiting for its send time. `Sent`: the phone sent it. `Delivered`: the carrier confirmed delivery (needs delivery reports turned on). `Failed`: it couldn't be sent. `Received`: an incoming message. `Canceled`: cancelled before sending."
      },
      "Message": {
        "type": "object",
        "properties": {
          "ID": {
            "type": "integer",
            "description": "Message ID."
          },
          "number": {
            "type": "string",
            "description": "The other party's number."
          },
          "message": {
            "type": "string",
            "description": "Message text."
          },
          "deviceID": {
            "type": "integer",
            "description": "Device that sent or received it."
          },
          "simSlot": {
            "type": [
              "integer",
              "null"
            ],
            "description": "0 = SIM 1, 1 = SIM 2."
          },
          "userID": {
            "type": "integer"
          },
          "status": {
            "$ref": "#/components/schemas/MessageStatus"
          },
          "type": {
            "type": "string",
            "enum": [
              "sms",
              "mms"
            ]
          },
          "attachments": {
            "type": [
              "string",
              "null"
            ],
            "description": "Comma-separated attachment URLs (MMS)."
          },
          "sentDate": {
            "type": "string",
            "format": "date-time"
          },
          "deliveredDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "groupID": {
            "type": "string",
            "description": "Shared by all messages from one send request."
          }
        },
        "additionalProperties": true
      },
      "Device": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Device ID, used in `devices` when sending."
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "The name you gave the device."
          },
          "model": {
            "type": "string"
          },
          "androidVersion": {
            "type": [
              "string",
              "null"
            ]
          },
          "appVersion": {
            "type": [
              "string",
              "null"
            ]
          },
          "sims": {
            "type": "object",
            "description": "SIM slot (`\"0\"` = SIM 1, `\"1\"` = SIM 2) to a label with its number.",
            "additionalProperties": {
              "type": "string"
            }
          }
        }
      },
      "Contact": {
        "type": "object",
        "properties": {
          "ID": {
            "type": "integer"
          },
          "number": {
            "type": "string"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "contactsListID": {
            "type": "integer"
          },
          "subscribed": {
            "type": [
              "boolean",
              "integer"
            ]
          }
        },
        "additionalProperties": true
      },
      "UssdRequest": {
        "type": "object",
        "properties": {
          "ID": {
            "type": "integer"
          },
          "request": {
            "type": "string",
            "description": "The USSD code that was dialled."
          },
          "response": {
            "type": [
              "string",
              "null"
            ],
            "description": "The network's reply, once it arrives."
          },
          "deviceID": {
            "type": "integer"
          },
          "simSlot": {
            "type": [
              "integer",
              "null"
            ]
          },
          "userID": {
            "type": "integer"
          },
          "sentDate": {
            "type": "string"
          },
          "responseDate": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "additionalProperties": true
      },
      "MessagesResponse": {
        "type": "object",
        "required": [
          "success",
          "data"
        ],
        "properties": {
          "success": {
            "const": true
          },
          "data": {
            "type": "object",
            "properties": {
              "messages": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Message"
                }
              }
            }
          },
          "error": {
            "type": "null"
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "required": [
          "success",
          "error"
        ],
        "properties": {
          "success": {
            "const": false
          },
          "data": {
            "type": "null"
          },
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "integer",
                "enum": [
                  204,
                  400,
                  401,
                  403,
                  404,
                  500
                ],
                "description": "What went wrong (see Response format above)."
              },
              "message": {
                "type": "string",
                "description": "Human-readable explanation."
              }
            }
          }
        }
      },
      "ApiMessage": {
        "type": "object",
        "description": "A message as returned by the newer endpoints (List messages, Get conversations).",
        "properties": {
          "ID": {
            "type": "integer"
          },
          "number": {
            "type": "string",
            "description": "The other person's number."
          },
          "message": {
            "type": "string"
          },
          "direction": {
            "type": "string",
            "enum": [
              "inbound",
              "outbound"
            ],
            "description": "`inbound` = you received it, `outbound` = you sent it."
          },
          "status": {
            "$ref": "#/components/schemas/MessageStatus"
          },
          "type": {
            "type": "string",
            "enum": [
              "sms",
              "mms"
            ]
          },
          "attachments": {
            "type": [
              "string",
              "null"
            ],
            "description": "Comma-separated URLs (MMS)."
          },
          "deviceID": {
            "type": [
              "integer",
              "null"
            ]
          },
          "simSlot": {
            "type": [
              "integer",
              "null"
            ],
            "description": "0 = SIM 1, 1 = SIM 2."
          },
          "groupID": {
            "type": [
              "string",
              "null"
            ],
            "description": "Shared by every message from one send request."
          },
          "sentDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "deliveredDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "Conversation": {
        "type": "object",
        "properties": {
          "number": {
            "type": "string"
          },
          "messageCount": {
            "type": "integer"
          },
          "unreadCount": {
            "type": "integer",
            "description": "Incoming messages you haven't marked as read."
          },
          "lastMessage": {
            "$ref": "#/components/schemas/ApiMessage"
          }
        }
      },
      "ContactsList": {
        "type": "object",
        "properties": {
          "ID": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "contacts": {
            "type": "integer",
            "description": "Contacts in the list."
          },
          "subscribed": {
            "type": "integer",
            "description": "Contacts who are still subscribed."
          }
        }
      },
      "ListedContact": {
        "type": "object",
        "properties": {
          "ID": {
            "type": "integer"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "number": {
            "type": "string"
          },
          "subscribed": {
            "type": "boolean"
          }
        }
      },
      "Account": {
        "type": "object",
        "properties": {
          "ID": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "timeZone": {
            "type": "string"
          },
          "credits": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Message credits left. `null` = unlimited."
          },
          "unlimitedCredits": {
            "type": "boolean"
          },
          "expiryDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When your plan ends, if it does."
          },
          "connectedDevices": {
            "type": "integer"
          },
          "webhookEnabled": {
            "type": "boolean"
          }
        }
      }
    }
  }
}
