{
  "openapi": "3.1.0",
  "info": {
    "title": "WbizTool API",
    "version": "2.0.0-beta",
    "description": "WbizTool API v2 (public beta). Docs: https://wbiztool.com/docs/api/v2/. Send your key as `Authorization: Bearer wbz_live_…`. JSON in and out; real HTTP status codes; one error shape with a stable `code`; `Idempotency-Key` on POST; cursor pages; per-key limits (60 requests a minute, 10,000 a day). Each operation lists the key scope it needs in `x-wbiztool-scope`. Test keys (wbz_test_…) check every send like a live key but send nothing: the message is kept as a test message whose status moves from queued to sent after 5 s (sent is final, as for a live message); a test key lists and cancels only its test messages, reads the rest of the workspace read-only, and gets test_mode_unsupported for other writes.",
    "contact": {
      "name": "WbizTool",
      "url": "https://wbiztool.com/contact/",
      "email": "info@wbiztool.com"
    }
  },
  "servers": [
    {
      "url": "https://wbiztool.com/api/v2"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/me": {
      "get": {
        "operationId": "getMe",
        "summary": "The key, its workspace, plan and limits",
        "tags": [
          "Account"
        ],
        "x-wbiztool-scope": null,
        "responses": {
          "200": {
            "description": "The key and its workspace.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Me"
                },
                "example": {
                  "workspace": {
                    "id": 5012,
                    "name": "Acme Bakery"
                  },
                  "key": {
                    "id": 7,
                    "name": "Order system",
                    "mode": "live",
                    "scopes": [
                      "messages:send",
                      "messages:read"
                    ],
                    "number_ids": [],
                    "expires_at": null,
                    "limited_to_numbers": false,
                    "read_only": false
                  },
                  "plan": {
                    "name": "Starter",
                    "paid": true
                  },
                  "limits": {
                    "requests_per_minute": 60,
                    "requests_per_day": 10000,
                    "messages_per_number_per_day": 1000,
                    "messages_remaining": null
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/messages": {
      "get": {
        "operationId": "listMessages",
        "summary": "Message history, newest first",
        "tags": [
          "Messages"
        ],
        "x-wbiztool-scope": "messages:read",
        "description": "Messages of the last 90 days (older ones are archived). Filters combine; `next_cursor` gives the next page.",
        "parameters": [
          {
            "name": "number",
            "in": "query",
            "required": false,
            "description": "Only messages from this number id.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "$ref": "#/components/schemas/MessageStatus"
            }
          },
          {
            "name": "since",
            "in": "query",
            "required": false,
            "description": "Created at or after (ISO 8601 with offset).",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "until",
            "in": "query",
            "required": false,
            "description": "Created before (ISO 8601 with offset).",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of messages.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "operationId": "sendMessage",
        "summary": "Send a message now or at a time",
        "tags": [
          "Messages"
        ],
        "x-wbiztool-scope": "messages:send",
        "description": "Queues one message; the answer comes before it goes out (status queued or scheduled). Watch its status with GET /messages/{id}. Without `from`, the only connected number sends it. A message with `media` is an image or a file message; `text` is then its caption.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MessageCreate"
              },
              "example": {
                "to": {
                  "phone": "+919876543210"
                },
                "text": "Hi Asha, your order #1042 is ready."
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Queued.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                },
                "example": {
                  "id": 5168961,
                  "status": "queued",
                  "from": {
                    "id": 101,
                    "phone": "919876500001",
                    "platform": "whatsapp"
                  },
                  "to": {
                    "phone": "+919876543210",
                    "contact_id": 991
                  },
                  "text": "Hi Asha, your order #1042 is ready.",
                  "media": null,
                  "send_at": null,
                  "expires_at": null,
                  "created_at": "2026-10-18T10:15:02.311Z",
                  "sent_at": null,
                  "error": null
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "402": {
            "description": "No messages left on the plan (no_credits).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "This Idempotency-Key is still being processed.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "A value is not usable: invalid_phone, contact_not_found, media_not_found, invalid_media_url, time_in_past, number_not_found, from_required, message_rejected, idempotency_key_reused, recipient_opted_out.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "200": {
            "description": "A dry run (dry_run: true): what would be sent or made; nothing was kept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DryRun"
                }
              }
            }
          }
        }
      }
    },
    "/messages/batch": {
      "post": {
        "operationId": "sendMessageBatch",
        "summary": "Send one message to many recipients",
        "tags": [
          "Messages"
        ],
        "x-wbiztool-scope": "messages:send",
        "description": "Up to 1,000 recipients, all or nothing: every recipient is checked first, and the whole list must fit in the plan and the number's daily room. The same recipient twice gets one message.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MessageBatchCreate"
              },
              "example": {
                "from": 101,
                "to": [
                  {
                    "phone": "+919876543210"
                  },
                  {
                    "group": "Bakery staff"
                  }
                ],
                "text": "We open at 10 tomorrow."
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Queued.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageBatch"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "402": {
            "description": "No messages left on the plan (no_credits).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "This Idempotency-Key is still being processed.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "A value is not usable: invalid_phone, contact_not_found, media_not_found, invalid_media_url, time_in_past, number_not_found, from_required, message_rejected, idempotency_key_reused, recipient_opted_out.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "200": {
            "description": "A dry run (dry_run: true): what would be sent or made; nothing was kept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DryRun"
                }
              }
            }
          }
        }
      }
    },
    "/messages/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ],
      "get": {
        "operationId": "getMessage",
        "summary": "One message and its status",
        "tags": [
          "Messages"
        ],
        "x-wbiztool-scope": "messages:read",
        "responses": {
          "200": {
            "description": "The message.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                },
                "example": {
                  "id": 5168961,
                  "status": "queued",
                  "from": {
                    "id": 101,
                    "phone": "919876500001",
                    "platform": "whatsapp"
                  },
                  "to": {
                    "phone": "+919876543210",
                    "contact_id": 991
                  },
                  "text": "Hi Asha, your order #1042 is ready.",
                  "media": null,
                  "send_at": null,
                  "expires_at": null,
                  "created_at": "2026-10-18T10:15:02.311Z",
                  "sent_at": null,
                  "error": null
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "cancelMessage",
        "summary": "Cancel a waiting message",
        "tags": [
          "Messages"
        ],
        "x-wbiztool-scope": "messages:send",
        "description": "Only a queued or scheduled message can be cancelled; it then has status cancelled.",
        "responses": {
          "200": {
            "description": "Cancelled.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                }
              }
            }
          },
          "409": {
            "description": "Not waiting any more (not_cancellable; `status` says what it is).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/numbers": {
      "get": {
        "operationId": "listNumbers",
        "summary": "The workspace's numbers",
        "tags": [
          "Numbers"
        ],
        "x-wbiztool-scope": "numbers",
        "description": "WhatsApp numbers and Telegram accounts, oldest first. A key limited to some numbers lists only those. `connect` is filled only by GET /numbers/{id}.",
        "responses": {
          "200": {
            "description": "The numbers.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NumberList"
                },
                "example": {
                  "data": [
                    {
                      "id": 101,
                      "phone": "919876500001",
                      "label": "Shop",
                      "platform": "whatsapp",
                      "status": "connected",
                      "login_method": "qr",
                      "connect": null
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "operationId": "connectNumber",
        "summary": "Connect a WhatsApp number",
        "tags": [
          "Numbers"
        ],
        "x-wbiztool-scope": "numbers",
        "description": "Asks a WbizTool server to open WhatsApp for the number. Then poll GET /numbers/{id} every few seconds: while `status` is connecting, `connect` has a QR code link to scan in WhatsApp → Linked devices, or a pairing code to type there; `status` becomes connected. Needs a plan with the QR code API. With `number_id`, an existing number is reconnected.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NumberConnect"
              },
              "example": {
                "login_method": "phone_code",
                "phone": "+919876543210"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Connect requested.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Number"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "409": {
            "description": "already_connected, connect_in_progress (number_id says which), or the Idempotency-Key is still being processed.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "invalid_phone, number_not_found, idempotency_key_reused.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/numbers/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ],
      "get": {
        "operationId": "getNumber",
        "summary": "One number and its connect progress",
        "tags": [
          "Numbers"
        ],
        "x-wbiztool-scope": "numbers",
        "responses": {
          "200": {
            "description": "The number.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Number"
                },
                "example": {
                  "id": 101,
                  "phone": "919876500001",
                  "label": "Shop",
                  "platform": "whatsapp",
                  "status": "connected",
                  "login_method": "qr",
                  "connect": null
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/reminders": {
      "get": {
        "operationId": "listReminders",
        "summary": "Reminders, newest first",
        "tags": [
          "Reminders"
        ],
        "x-wbiztool-scope": "reminders",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of reminders.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReminderList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "operationId": "createReminder",
        "summary": "Create a repeating reminder",
        "tags": [
          "Reminders"
        ],
        "x-wbiztool-scope": "reminders",
        "description": "Sends `text` (and `media`) to one recipient on a 5-part cron schedule in a time zone. Without `from`, any connected number sends it. Needs a plan with reminders. media.file_name is ignored for reminders (the file keeps its own name).",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReminderCreate"
              },
              "example": {
                "name": "Weekly rent",
                "to": {
                  "phone": "+919876543210"
                },
                "text": "Hi, a reminder that the rent is due on Friday.",
                "schedule": {
                  "cron": "0 9 * * 1",
                  "timezone": "Asia/Kolkata"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Reminder"
                },
                "example": {
                  "id": 812,
                  "name": "Weekly rent",
                  "active": true,
                  "to": {
                    "phone": "+919876543210"
                  },
                  "from": 101,
                  "text": "Hi, a reminder that the rent is due on Friday.",
                  "media": null,
                  "schedule": {
                    "cron": "0 9 * * 1",
                    "timezone": "Asia/Kolkata"
                  },
                  "next_run_at": "2026-10-19T09:00:00+05:30",
                  "created_at": "2026-10-18T10:20:11.004Z"
                }
              }
            }
          },
          "409": {
            "description": "The Idempotency-Key is still being processed.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "422": {
            "description": "invalid_phone, invalid_media_url, media_not_found, number_not_connected (from), idempotency_key_reused.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "200": {
            "description": "A dry run (dry_run: true): what would be sent or made; nothing was kept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DryRun"
                }
              }
            }
          }
        }
      }
    },
    "/reminders/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ],
      "get": {
        "operationId": "getReminder",
        "summary": "One reminder",
        "tags": [
          "Reminders"
        ],
        "x-wbiztool-scope": "reminders",
        "responses": {
          "200": {
            "description": "The reminder.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Reminder"
                },
                "example": {
                  "id": 812,
                  "name": "Weekly rent",
                  "active": true,
                  "to": {
                    "phone": "+919876543210"
                  },
                  "from": 101,
                  "text": "Hi, a reminder that the rent is due on Friday.",
                  "media": null,
                  "schedule": {
                    "cron": "0 9 * * 1",
                    "timezone": "Asia/Kolkata"
                  },
                  "next_run_at": "2026-10-19T09:00:00+05:30",
                  "created_at": "2026-10-18T10:20:11.004Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "operationId": "updateReminder",
        "summary": "Change a reminder, pause or resume it",
        "tags": [
          "Reminders"
        ],
        "x-wbiztool-scope": "reminders",
        "description": "Send only what changes; `active: false` pauses it, `true` resumes it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReminderPatch"
              },
              "example": {
                "active": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The reminder now.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Reminder"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "422": {
            "description": "invalid_phone, invalid_media_url, media_not_found, number_not_connected (from).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "deleteReminder",
        "summary": "Delete a reminder",
        "tags": [
          "Reminders"
        ],
        "x-wbiztool-scope": "reminders",
        "responses": {
          "200": {
            "description": "Deleted.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deleted"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/media": {
      "get": {
        "operationId": "listMedia",
        "summary": "Files in the Media directory, newest first",
        "tags": [
          "Media"
        ],
        "x-wbiztool-scope": "media",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of files.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MediaList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "operationId": "uploadMedia",
        "summary": "Upload a file",
        "tags": [
          "Media"
        ],
        "x-wbiztool-scope": "media",
        "description": "One file per request (multipart/form-data, field `file`, at most 64 MB, a type WhatsApp can send). Send it later with media: {\"media_id\": …}.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Stored.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Media"
                },
                "example": {
                  "id": 3301,
                  "name": "price list.pdf",
                  "type": "file",
                  "mime_type": "application/pdf",
                  "size": 182344,
                  "url": "https://media.wbiztool.com/media/org_5012/media_5012_17608_0.pdf",
                  "created_at": "2026-10-18T10:31:40.120Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "409": {
            "description": "The Idempotency-Key is still being processed.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "file_too_large.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "unsupported_file_type, or not multipart.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "storage_full (used, limit), idempotency_key_reused.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "upload_failed: try again.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/media/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ],
      "get": {
        "operationId": "getMedia",
        "summary": "One file",
        "tags": [
          "Media"
        ],
        "x-wbiztool-scope": "media",
        "responses": {
          "200": {
            "description": "The file.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Media"
                },
                "example": {
                  "id": 3301,
                  "name": "price list.pdf",
                  "type": "file",
                  "mime_type": "application/pdf",
                  "size": 182344,
                  "url": "https://media.wbiztool.com/media/org_5012/media_5012_17608_0.pdf",
                  "created_at": "2026-10-18T10:31:40.120Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "deleteMedia",
        "summary": "Delete a file",
        "tags": [
          "Media"
        ],
        "x-wbiztool-scope": "media",
        "description": "Its link stops working. Refused while a waiting message, a reminder or a campaign still uses it.",
        "responses": {
          "200": {
            "description": "Deleted.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deleted"
                }
              }
            }
          },
          "409": {
            "description": "media_in_use (count).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/number-checks": {
      "get": {
        "operationId": "listNumberChecks",
        "summary": "Number checks, newest first",
        "tags": [
          "Number checks"
        ],
        "x-wbiztool-scope": "contacts",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of checks.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NumberCheckList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "operationId": "createNumberCheck",
        "summary": "Check which numbers are on WhatsApp",
        "tags": [
          "Number checks"
        ],
        "x-wbiztool-scope": "contacts",
        "description": "Queues up to 10,000 numbers (the plan may allow fewer per list and per billing period). A number without its country code gets the workspace's. Poll GET /number-checks/{id} until status is completed, then read the results.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NumberCheckCreate"
              },
              "example": {
                "name": "October leads",
                "numbers": [
                  "+919876543210",
                  "+447700900123"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Queued.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NumberCheckCreated"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "409": {
            "description": "The Idempotency-Key is still being processed.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "no_valid_numbers (skipped), too_many_numbers (count, limit), number_check_quota_reached (requested, used, limit), idempotency_key_reused.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/number-checks/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ],
      "get": {
        "operationId": "getNumberCheck",
        "summary": "A check and its progress",
        "tags": [
          "Number checks"
        ],
        "x-wbiztool-scope": "contacts",
        "responses": {
          "200": {
            "description": "The check.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NumberCheck"
                },
                "example": {
                  "id": 51207,
                  "name": "October leads",
                  "status": "processing",
                  "counts": {
                    "total": 3,
                    "pending": 1,
                    "on_whatsapp": 1,
                    "not_on_whatsapp": 1,
                    "cancelled": 0
                  },
                  "created_at": "2026-10-18T10:40:02.511Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/number-checks/{id}/results": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ],
      "get": {
        "operationId": "listNumberCheckResults",
        "summary": "A check's numbers and their result",
        "tags": [
          "Number checks"
        ],
        "x-wbiztool-scope": "contacts",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of results.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NumberCheckResults"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/webhooks": {
      "get": {
        "operationId": "listWebhooks",
        "summary": "Webhook endpoints",
        "tags": [
          "Webhooks"
        ],
        "x-wbiztool-scope": "messages:read",
        "description": "Endpoints get the workspace's events as JSON POSTs: {\"id\": \"evt_…\", \"type\": \"message.sent\", \"created_at\", \"data\": {\"message\": Message}}. Each request carries WbizTool-Event-Id, WbizTool-Timestamp and WbizTool-Signature: v1=<hex HMAC-SHA256(secret, \"<timestamp>.<raw body>\")> – check it and refuse requests older than 5 minutes. Answer 2xx within 10 seconds; anything else is tried again after 1, 5, 15 minutes, 1, 2, 4, 8 and 8 hours. The same event can arrive twice: use its id to ignore repeats. Keys limited to some numbers can't manage webhooks.",
        "responses": {
          "200": {
            "description": "The endpoints.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "operationId": "createWebhook",
        "summary": "Add a webhook endpoint",
        "tags": [
          "Webhooks"
        ],
        "x-wbiztool-scope": "messages:read",
        "description": "At most 5 per workspace, public https:// addresses only. The answer has the endpoint's signing `secret` – shown this once.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookCreate"
              },
              "example": {
                "url": "https://example.com/wbiztool/events",
                "events": [
                  "message.sent",
                  "message.failed"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Added; `secret` is in this answer only.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Webhook"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "409": {
            "description": "The Idempotency-Key is still being processed.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "invalid_webhook_url, idempotency_key_reused.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/webhooks/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ],
      "get": {
        "operationId": "getWebhook",
        "summary": "One webhook endpoint",
        "tags": [
          "Webhooks"
        ],
        "x-wbiztool-scope": "messages:read",
        "responses": {
          "200": {
            "description": "The endpoint.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Webhook"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "deleteWebhook",
        "summary": "Remove a webhook endpoint",
        "tags": [
          "Webhooks"
        ],
        "x-wbiztool-scope": "messages:read",
        "responses": {
          "200": {
            "description": "Removed; nothing more is sent to it.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deleted"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/webhooks/{id}/rotate-secret": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ],
      "post": {
        "operationId": "rotateWebhookSecret",
        "summary": "Replace the signing secret",
        "tags": [
          "Webhooks"
        ],
        "x-wbiztool-scope": "messages:read",
        "description": "The old secret stops at once; the answer has the new `secret` – shown this once.",
        "responses": {
          "200": {
            "description": "The endpoint with its new secret.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Webhook"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/webhooks/{id}/test": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ],
      "post": {
        "operationId": "testWebhook",
        "summary": "Send a ping event",
        "tags": [
          "Webhooks"
        ],
        "x-wbiztool-scope": "messages:read",
        "description": "Queues {\"type\": \"ping\", \"data\": {\"webhook_id\": …}} to this endpoint, signed like every event.",
        "responses": {
          "202": {
            "description": "Queued.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookTest"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/opt-outs": {
      "get": {
        "operationId": "listOptOuts",
        "summary": "Recipients who opted out, newest first",
        "tags": [
          "Opt-outs"
        ],
        "x-wbiztool-scope": "contacts",
        "description": "Every send to them fails: POST /messages answers 422 recipient_opted_out, and messages queued earlier fail with the error \"Opted out\". A reply that is only STOP (or Unsubscribe, Baja, Stopp … in 12 languages) on a number with Unibox adds the sender (source reply); START removes them.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "name": "phone",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Only this number (international format)."
          }
        ],
        "responses": {
          "200": {
            "description": "A page of opt-outs.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OptOutList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "422": {
            "description": "invalid_phone.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "operationId": "createOptOut",
        "summary": "Opt a recipient out",
        "tags": [
          "Opt-outs"
        ],
        "x-wbiztool-scope": "contacts",
        "description": "From now on every send to this number fails. Already opted out: 200 with the existing entry.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OptOutCreate"
              },
              "example": {
                "phone": "+919876543210",
                "note": "Asked by email"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Opted out.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OptOut"
                },
                "example": {
                  "recipient": "+919876543210",
                  "source": "api",
                  "keyword": null,
                  "note": "Asked by email",
                  "created_at": "2026-10-26T09:12:03.120Z"
                }
              }
            }
          },
          "200": {
            "description": "Was opted out already.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OptOut"
                },
                "example": {
                  "recipient": "+919876543210",
                  "source": "reply",
                  "keyword": "STOP",
                  "note": null,
                  "created_at": "2026-10-26T09:12:03.120Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "409": {
            "description": "The Idempotency-Key is still being processed.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "invalid_phone.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/opt-outs/{phone}": {
      "parameters": [
        {
          "name": "phone",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The number in international format without '+' (or a Telegram @username)."
        }
      ],
      "get": {
        "operationId": "getOptOut",
        "summary": "One recipient's opt-out",
        "tags": [
          "Opt-outs"
        ],
        "x-wbiztool-scope": "contacts",
        "description": "404 opt_out_not_found when the recipient hasn't opted out.",
        "responses": {
          "200": {
            "description": "The opt-out.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OptOut"
                },
                "example": {
                  "recipient": "+919876543210",
                  "source": "reply",
                  "keyword": "STOP",
                  "note": null,
                  "created_at": "2026-10-26T09:12:03.120Z"
                }
              }
            }
          },
          "422": {
            "description": "invalid_phone.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "deleteOptOut",
        "summary": "Opt a recipient back in",
        "tags": [
          "Opt-outs"
        ],
        "x-wbiztool-scope": "contacts",
        "description": "Sends to the number work again. 404 opt_out_not_found when it wasn't opted out.",
        "responses": {
          "200": {
            "description": "Opted back in.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OptOutDeleted"
                },
                "example": {
                  "recipient": "+919876543210",
                  "deleted": true
                }
              }
            }
          },
          "422": {
            "description": "invalid_phone.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "wbz_live_…",
        "description": "A v2 key from Settings → API keys. v1 keys are refused (code v1_key_on_v2)."
      }
    },
    "headers": {
      "X-Request-Id": {
        "description": "Our id for this request; quote it when you contact us.",
        "schema": {
          "type": "string",
          "pattern": "^req_[0-9a-f]{24}$"
        }
      },
      "RateLimit-Limit": {
        "description": "Requests allowed in the current window.",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimit-Remaining": {
        "description": "Requests left in the current window.",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimit-Reset": {
        "description": "Seconds until the window resets.",
        "schema": {
          "type": "integer"
        }
      },
      "Retry-After": {
        "description": "Seconds to wait before retrying.",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimit-Policy": {
        "description": "The limits: \"60;w=60, 10000;w=86400\" (requests per window in seconds).",
        "schema": {
          "type": "string"
        }
      },
      "X-Client-Request-Id": {
        "description": "Your own X-Request-Id header, echoed back (up to 100 printable characters).",
        "schema": {
          "type": "string"
        }
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "Any unique string (1–255 printable characters). A retry with the same key and body within 24 hours gets the first answer back (header Idempotent-Replayed: true) and never does the work twice.",
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 255
        }
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 25
        }
      },
      "Cursor": {
        "name": "cursor",
        "in": "query",
        "required": false,
        "description": "The previous page's next_cursor.",
        "schema": {
          "type": "string"
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "No key, a wrong, revoked or expired key, or a v1 key.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Forbidden": {
        "description": "The key lacks the scope, the workspace has no API v2 yet, or a demo workspace.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "Nothing at this address.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ValidationFailed": {
        "description": "The request is not valid; `field` names the first problem.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Too many requests for this key (or wrong keys from this address).",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          },
          "Retry-After": {
            "$ref": "#/components/headers/Retry-After"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "ErrorCode": {
        "type": "string",
        "enum": [
          "account_disabled",
          "already_connected",
          "api_key_expired",
          "api_key_revoked",
          "api_v2_not_enabled",
          "connect_in_progress",
          "contact_not_found",
          "demo_account",
          "file_too_large",
          "from_required",
          "idempotency_in_progress",
          "idempotency_key_reused",
          "internal_error",
          "invalid_api_key",
          "invalid_cron",
          "invalid_cursor",
          "invalid_datetime",
          "invalid_idempotency_key",
          "invalid_json",
          "invalid_media_url",
          "invalid_phone",
          "invalid_timezone",
          "invalid_username",
          "invalid_webhook_url",
          "media_in_use",
          "media_not_found",
          "message_not_found",
          "message_rejected",
          "method_not_allowed",
          "missing_api_key",
          "missing_scope",
          "no_credits",
          "no_valid_numbers",
          "not_cancellable",
          "not_found",
          "number_check_not_found",
          "number_check_quota_reached",
          "number_daily_cap_reached",
          "number_limit_reached",
          "number_not_allowed",
          "number_not_connected",
          "number_not_found",
          "opt_out_not_found",
          "plan_required",
          "rate_limited",
          "read_only_key",
          "recipient_opted_out",
          "reminder_not_found",
          "request_too_large",
          "storage_full",
          "test_mode_unsupported",
          "text_required",
          "text_too_long",
          "time_in_past",
          "too_many_auth_failures",
          "too_many_numbers",
          "too_many_recipients",
          "unsupported_content_type",
          "unsupported_file_type",
          "upload_failed",
          "username_needs_telegram",
          "v1_key_on_v2",
          "validation_failed",
          "webhook_limit_reached",
          "webhook_not_found"
        ]
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "additionalProperties": false,
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message",
              "field",
              "docs",
              "request_id"
            ],
            "additionalProperties": true,
            "properties": {
              "code": {
                "$ref": "#/components/schemas/ErrorCode"
              },
              "message": {
                "type": "string"
              },
              "field": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The field the error is about, e.g. to.phone or to[3].phone."
              },
              "docs": {
                "type": "string",
                "format": "uri"
              },
              "request_id": {
                "type": "string"
              },
              "errors": {
                "type": "array",
                "description": "Further problems, when there are several.",
                "items": {
                  "type": "object",
                  "properties": {
                    "field": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "Scope": {
        "type": "string",
        "enum": [
          "messages:send",
          "messages:read",
          "reminders",
          "numbers",
          "media",
          "contacts"
        ]
      },
      "Me": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "workspace",
          "key",
          "plan",
          "limits"
        ],
        "properties": {
          "workspace": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name"
            ],
            "properties": {
              "id": {
                "type": "integer"
              },
              "name": {
                "type": "string"
              }
            }
          },
          "key": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "mode",
              "scopes",
              "limited_to_numbers",
              "number_ids",
              "expires_at",
              "read_only"
            ],
            "properties": {
              "id": {
                "type": "integer"
              },
              "name": {
                "type": "string"
              },
              "mode": {
                "type": "string",
                "enum": [
                  "live",
                  "test"
                ]
              },
              "scopes": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Scope"
                }
              },
              "number_ids": {
                "type": "array",
                "items": {
                  "type": "integer"
                },
                "description": "The numbers the key is limited to (empty when it is not limited)."
              },
              "expires_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "limited_to_numbers": {
                "type": "boolean",
                "description": "true: the key may use only number_ids (possibly none left)."
              },
              "read_only": {
                "type": "boolean",
                "description": "true: every write answers 403 read_only_key."
              }
            }
          },
          "plan": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "required": [
              "name",
              "paid"
            ],
            "properties": {
              "name": {
                "type": "string"
              },
              "paid": {
                "type": "boolean",
                "description": "An active paid plan (not a free trial)."
              }
            }
          },
          "limits": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "requests_per_minute",
              "requests_per_day",
              "messages_per_number_per_day",
              "messages_remaining"
            ],
            "properties": {
              "requests_per_minute": {
                "type": "integer"
              },
              "requests_per_day": {
                "type": "integer"
              },
              "messages_per_number_per_day": {
                "type": "integer"
              },
              "messages_remaining": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "null: unlimited (a paid plan)."
              }
            }
          }
        }
      },
      "MessageStatus": {
        "type": "string",
        "enum": [
          "queued",
          "scheduled",
          "sent",
          "delivered",
          "read",
          "failed",
          "cancelled",
          "expired"
        ],
        "description": "queued: waiting to go out; scheduled: waiting for send_at; sent: sent from your number, the last status a sent message gets today (delivered / read are reserved: WhatsApp's ticks aren't recorded, so no message has them); failed; cancelled; expired (not sent before expires_at)."
      },
      "Recipient": {
        "type": "object",
        "additionalProperties": false,
        "minProperties": 1,
        "maxProperties": 1,
        "description": "Exactly one of phone (international format with +), group (the exact name of a WhatsApp group the number is in), contact_id, or username (Telegram: @name, a t.me link or tg:<id>; only from a Telegram account).",
        "properties": {
          "phone": {
            "type": "string",
            "example": "+919876543210"
          },
          "group": {
            "type": "string",
            "maxLength": 200
          },
          "contact_id": {
            "type": "integer"
          },
          "username": {
            "type": "string",
            "maxLength": 100,
            "example": "@asha_bakes"
          }
        }
      },
      "MediaRef": {
        "type": "object",
        "additionalProperties": false,
        "description": "Exactly one of url (a public http(s) link) or media_id (a file in the Media directory). An image link (jpg, png, gif, webp) is sent as an image, anything else as a file; `type` decides otherwise.",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "maxLength": 1000
          },
          "media_id": {
            "type": "integer"
          },
          "type": {
            "type": "string",
            "enum": [
              "image",
              "file"
            ]
          },
          "file_name": {
            "type": "string",
            "maxLength": 100
          }
        }
      },
      "MessageCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "to"
        ],
        "properties": {
          "to": {
            "$ref": "#/components/schemas/Recipient"
          },
          "from": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The sending number id (GET /me, the dashboard). Optional when the workspace has one connected number."
          },
          "text": {
            "type": "string",
            "maxLength": 3000,
            "description": "The message, or the caption of media. *bold*, _italic_ as in WhatsApp."
          },
          "media": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/MediaRef"
              },
              {
                "type": "null"
              }
            ]
          },
          "send_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Send at this time (ISO 8601 with offset); otherwise now."
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Not sent after this time: it becomes expired (e.g. a one-time code)."
          },
          "dry_run": {
            "type": "boolean",
            "default": false,
            "description": "true: check everything and answer what would happen (200, ids null), but keep and send nothing."
          }
        }
      },
      "MessageBatchCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "to"
        ],
        "properties": {
          "to": {
            "type": "array",
            "minItems": 1,
            "maxItems": 1000,
            "items": {
              "$ref": "#/components/schemas/Recipient"
            }
          },
          "from": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The sending number id (GET /me, the dashboard). Optional when the workspace has one connected number."
          },
          "text": {
            "type": "string",
            "maxLength": 3000,
            "description": "The message, or the caption of media. *bold*, _italic_ as in WhatsApp."
          },
          "media": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/MediaRef"
              },
              {
                "type": "null"
              }
            ]
          },
          "send_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Send at this time (ISO 8601 with offset); otherwise now."
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Not sent after this time: it becomes expired (e.g. a one-time code)."
          },
          "dry_run": {
            "type": "boolean",
            "default": false,
            "description": "true: check everything and answer what would happen (200, ids null), but keep and send nothing."
          }
        }
      },
      "Message": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "status",
          "from",
          "to",
          "text",
          "media",
          "send_at",
          "expires_at",
          "created_at",
          "sent_at",
          "error"
        ],
        "properties": {
          "id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "null only in a dry run."
          },
          "status": {
            "$ref": "#/components/schemas/MessageStatus"
          },
          "from": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "phone",
              "platform"
            ],
            "properties": {
              "id": {
                "type": "integer"
              },
              "phone": {
                "type": "string"
              },
              "platform": {
                "type": "string",
                "enum": [
                  "whatsapp",
                  "telegram"
                ]
              }
            }
          },
          "to": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "contact_id"
            ],
            "properties": {
              "phone": {
                "type": "string"
              },
              "group": {
                "type": "string"
              },
              "username": {
                "type": "string",
                "description": "A Telegram @username."
              },
              "contact_id": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "null only for a test-mode message to a number with no contact."
              }
            }
          },
          "text": {
            "type": "string"
          },
          "media": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "required": [
              "type",
              "url",
              "file_name"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "image",
                  "file"
                ]
              },
              "url": {
                "type": "string"
              },
              "file_name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "send_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "sent_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "error": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "required": [
              "message"
            ],
            "properties": {
              "message": {
                "type": "string"
              }
            },
            "description": "Why a failed or expired message did not go out."
          }
        }
      },
      "MessageList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Message"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "MessageBatch": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Message"
            }
          }
        }
      },
      "Number": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "phone",
          "label",
          "platform",
          "status",
          "login_method",
          "connect"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "description": "Digits with the country code; null until a QR login is scanned."
          },
          "label": {
            "type": "string"
          },
          "platform": {
            "type": "string",
            "enum": [
              "whatsapp",
              "telegram"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "connected",
              "connecting",
              "disconnected"
            ]
          },
          "login_method": {
            "type": "string",
            "enum": [
              "qr",
              "phone_code"
            ]
          },
          "connect": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "required": [
              "qr_code_url",
              "pairing_code",
              "pairing_code_expires_at",
              "linking",
              "requested_at"
            ],
            "description": "While connecting (GET /numbers/{id} only).",
            "properties": {
              "qr_code_url": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The QR to scan: an image link valid 10 minutes, new on every call; null until the server has it."
              },
              "pairing_code": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "phone_code login: type it in WhatsApp → Linked devices → Link with phone number."
              },
              "pairing_code_expires_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "linking": {
                "type": "boolean",
                "description": "Scanned / accepted; WhatsApp is still linking."
              },
              "requested_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              }
            }
          }
        }
      },
      "NumberList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Number"
            }
          }
        }
      },
      "NumberConnect": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "login_method": {
            "type": "string",
            "enum": [
              "qr",
              "phone_code"
            ],
            "default": "qr"
          },
          "phone": {
            "type": "string",
            "description": "phone_code: the number to link, international format with +."
          },
          "number_id": {
            "type": "integer",
            "description": "Reconnect this number instead of adding one."
          }
        }
      },
      "Schedule": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "cron"
        ],
        "properties": {
          "cron": {
            "type": "string",
            "description": "minute hour day-of-month month day-of-week, e.g. \"0 9 * * 1\" = Mondays 09:00."
          },
          "timezone": {
            "type": "string",
            "default": "UTC",
            "description": "IANA name, e.g. Asia/Kolkata."
          }
        }
      },
      "ReminderCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name",
          "to",
          "text",
          "schedule"
        ],
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 100
          },
          "to": {
            "$ref": "#/components/schemas/Recipient"
          },
          "from": {
            "type": [
              "integer",
              "null"
            ]
          },
          "text": {
            "type": "string",
            "maxLength": 3000
          },
          "media": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/MediaRef"
              },
              {
                "type": "null"
              }
            ]
          },
          "schedule": {
            "$ref": "#/components/schemas/Schedule"
          },
          "active": {
            "type": "boolean",
            "default": true
          },
          "dry_run": {
            "type": "boolean",
            "default": false,
            "description": "true: check everything and answer what would happen (200, ids null), but keep and send nothing."
          }
        }
      },
      "ReminderPatch": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 100
          },
          "to": {
            "$ref": "#/components/schemas/Recipient"
          },
          "from": {
            "type": [
              "integer",
              "null"
            ]
          },
          "text": {
            "type": "string",
            "maxLength": 3000
          },
          "media": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/MediaRef"
              },
              {
                "type": "null"
              }
            ]
          },
          "schedule": {
            "type": "object",
            "additionalProperties": false,
            "description": "cron, timezone or both; a part left out keeps its value.",
            "properties": {
              "cron": {
                "type": "string"
              },
              "timezone": {
                "type": "string"
              }
            }
          },
          "active": {
            "type": "boolean",
            "description": "false pauses it, true resumes it; left out: unchanged."
          }
        }
      },
      "Reminder": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "name",
          "active",
          "to",
          "from",
          "text",
          "media",
          "schedule",
          "next_run_at",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "null only in a dry run."
          },
          "name": {
            "type": "string"
          },
          "active": {
            "type": "boolean"
          },
          "to": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "phone": {
                "type": "string"
              },
              "group": {
                "type": "string"
              },
              "username": {
                "type": "string"
              }
            }
          },
          "from": {
            "type": [
              "integer",
              "null"
            ],
            "description": "null: any connected number."
          },
          "text": {
            "type": "string"
          },
          "media": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "required": [
              "type",
              "url",
              "file_name"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "image",
                  "file"
                ]
              },
              "url": {
                "type": "string"
              },
              "file_name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "schedule": {
            "$ref": "#/components/schemas/Schedule"
          },
          "next_run_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "In the reminder's time zone; null while paused."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ReminderList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Reminder"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Deleted": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "deleted"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "deleted": {
            "const": true
          }
        }
      },
      "Media": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "name",
          "type",
          "mime_type",
          "size",
          "url",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "image",
              "file"
            ]
          },
          "mime_type": {
            "type": "string"
          },
          "size": {
            "type": "integer",
            "description": "Bytes."
          },
          "url": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "MediaList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Media"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "NumberCheck": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "name",
          "status",
          "counts",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "processing",
              "completed"
            ]
          },
          "counts": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "total",
              "pending",
              "on_whatsapp",
              "not_on_whatsapp",
              "cancelled"
            ],
            "properties": {
              "total": {
                "type": "integer"
              },
              "pending": {
                "type": "integer"
              },
              "on_whatsapp": {
                "type": "integer"
              },
              "not_on_whatsapp": {
                "type": "integer"
              },
              "cancelled": {
                "type": "integer"
              }
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "NumberCheckCreated": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "name",
          "status",
          "counts",
          "created_at",
          "skipped"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "processing",
              "completed"
            ]
          },
          "counts": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "total",
              "pending",
              "on_whatsapp",
              "not_on_whatsapp",
              "cancelled"
            ],
            "properties": {
              "total": {
                "type": "integer"
              },
              "pending": {
                "type": "integer"
              },
              "on_whatsapp": {
                "type": "integer"
              },
              "not_on_whatsapp": {
                "type": "integer"
              },
              "cancelled": {
                "type": "integer"
              }
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "skipped": {
            "type": "integer",
            "description": "How many entries were not a phone number (not checked, not counted)."
          }
        }
      },
      "NumberCheckCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "numbers"
        ],
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 500
          },
          "numbers": {
            "type": "array",
            "minItems": 1,
            "maxItems": 10000,
            "items": {
              "type": "string"
            }
          }
        }
      },
      "NumberCheckList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/NumberCheck"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "NumberCheckResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "number",
          "on_whatsapp",
          "status",
          "checked_at"
        ],
        "properties": {
          "number": {
            "type": "string"
          },
          "on_whatsapp": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "null until checked."
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "on_whatsapp",
              "not_on_whatsapp",
              "cancelled"
            ]
          },
          "checked_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "NumberCheckResults": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/NumberCheckResult"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "WebhookEventType": {
        "type": "string",
        "enum": [
          "message.sent",
          "message.failed",
          "message.cancelled",
          "message.expired"
        ]
      },
      "WebhookCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "url"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "maxLength": 500
          },
          "events": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/WebhookEventType"
            },
            "description": "Default: all of them."
          },
          "description": {
            "type": "string",
            "maxLength": 100
          }
        }
      },
      "Webhook": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "url",
          "description",
          "events",
          "secret_last4",
          "created_at",
          "last_delivery",
          "active",
          "disabled_reason"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "url": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEventType"
            }
          },
          "secret": {
            "type": "string",
            "description": "Only when made or rotated."
          },
          "secret_last4": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_delivery": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "required": [
              "state",
              "status_code",
              "created_at"
            ],
            "properties": {
              "state": {
                "type": "string",
                "enum": [
                  "pending",
                  "delivered",
                  "failed"
                ]
              },
              "status_code": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "active": {
            "type": "boolean",
            "description": "false: it gets no events any more (see disabled_reason)."
          },
          "disabled_reason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why it stopped: failing for 24 hours, or the API key that made it was revoked or has expired."
          }
        }
      },
      "WebhookList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Webhook"
            }
          }
        }
      },
      "WebhookTest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "event_id",
          "type",
          "queued"
        ],
        "properties": {
          "event_id": {
            "type": "string"
          },
          "type": {
            "const": "ping"
          },
          "queued": {
            "type": "boolean"
          }
        }
      },
      "WebhookEvent": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "type",
          "created_at",
          "data"
        ],
        "description": "What an endpoint receives.",
        "properties": {
          "id": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "message.sent",
              "message.failed",
              "message.cancelled",
              "message.expired",
              "ping"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "data": {
            "type": "object",
            "properties": {
              "message": {
                "$ref": "#/components/schemas/Message"
              },
              "webhook_id": {
                "type": "integer"
              }
            }
          }
        }
      },
      "DryRun": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "dry_run",
          "data"
        ],
        "properties": {
          "dry_run": {
            "const": true
          },
          "data": {
            "type": "array",
            "items": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/Message"
                },
                {
                  "$ref": "#/components/schemas/Reminder"
                }
              ]
            }
          }
        }
      },
      "OptOut": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "recipient",
          "source",
          "keyword",
          "note",
          "created_at"
        ],
        "properties": {
          "recipient": {
            "type": "string",
            "description": "'+' and the number, or a Telegram @username / tg:<id>."
          },
          "source": {
            "type": "string",
            "enum": [
              "reply",
              "dashboard",
              "import",
              "api"
            ],
            "description": "reply: they sent STOP to an inbox number; dashboard / import: the team on Contacts; api: this API."
          },
          "keyword": {
            "type": [
              "string",
              "null"
            ],
            "description": "The reply that opted them out."
          },
          "note": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "OptOutCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "phone"
        ],
        "properties": {
          "phone": {
            "type": "string",
            "description": "International format, e.g. +919876543210."
          },
          "note": {
            "type": "string",
            "maxLength": 200
          }
        }
      },
      "OptOutList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OptOut"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "OptOutDeleted": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "recipient",
          "deleted"
        ],
        "properties": {
          "recipient": {
            "type": "string"
          },
          "deleted": {
            "const": true
          }
        }
      }
    }
  }
}
