{
  "openapi": "3.1.0",
  "info": {
    "title": "WAgate public API",
    "version": "1",
    "description": "Channel-scoped HTTP API. Every operation under /v1 needs the channel's token as a bearer token, and opens only that channel. Errors are JSON with a stable `code` and a human-readable `error`. The product name and the origin are filled in when the document is served."
  },
  "servers": [
    {
      "url": "{origin}/v1",
      "variables": {
        "origin": {
          "default": "https://api-wagate.socialground.co"
        }
      }
    }
  ],
  "security": [
    {
      "bearer": []
    }
  ],
  "tags": [
    {
      "name": "Messages"
    },
    {
      "name": "Chats"
    },
    {
      "name": "Groups"
    },
    {
      "name": "Communities"
    },
    {
      "name": "Contacts"
    },
    {
      "name": "Channel"
    },
    {
      "name": "Webhooks"
    },
    {
      "name": "Warming"
    }
  ],
  "paths": {
    "/health": {
      "servers": [
        {
          "url": "{origin}",
          "variables": {
            "origin": {
              "default": "https://api-wagate.socialground.co"
            }
          }
        }
      ],
      "get": {
        "operationId": "health",
        "summary": "Liveness",
        "tags": [
          "Channel"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "const": "ok"
                    }
                  },
                  "required": [
                    "status"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/sessions/{name}": {
      "get": {
        "operationId": "getChannel",
        "summary": "Read this channel's state",
        "tags": [
          "Channel"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Session"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      },
      "patch": {
        "operationId": "updateChannel",
        "summary": "Change this channel's label or registration date",
        "tags": [
          "Channel"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "label": {
                    "type": "string",
                    "maxLength": 40,
                    "description": "An empty string resets it."
                  },
                  "registration_date": {
                    "type": "string",
                    "pattern": "^(\\d{4}-\\d{2}-\\d{2})?$",
                    "description": "YYYY-MM-DD, not in the future. An empty string resets it."
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Session"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sessions/{name}/history-sync": {
      "get": {
        "operationId": "getHistorySync",
        "summary": "Read whether this channel stores synced history",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "store_history_sync": {
                      "type": "boolean",
                      "description": "Whether history the paired phone sends is stored and readable through the conversation read (marked `source: history_sync`). Off by default."
                    }
                  },
                  "required": [
                    "store_history_sync"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      },
      "put": {
        "operationId": "setHistorySync",
        "summary": "Turn storing synced history on or off for this channel",
        "description": "Off by default. When on, messages the paired phone sends as history are kept in this channel's message log, under the same retention as every other message, and returned by the conversation read with `source: history_sync`. That history arrives when a number is linked (turn this on before linking to receive up to 200 recent messages per chat, from within the message retention period) and when you ask for it with a history request. Turning it off stops storing; what was stored stays until retention removes it. The gateway does not read or use this content; it only stores it for your channel.",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "store_history_sync": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "store_history_sync"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "store_history_sync": {
                      "type": "boolean",
                      "description": "Whether history the paired phone sends is stored and readable through the conversation read (marked `source: history_sync`). Off by default."
                    }
                  },
                  "required": [
                    "store_history_sync"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sessions/{name}/chats/{chat}/history-requests": {
      "post": {
        "operationId": "requestChatHistory",
        "summary": "Ask the paired phone for older messages of one chat",
        "description": "Asks the phone linked to this channel for up to `count` messages of this chat older than the oldest one this channel holds, so a number linked earlier can fill in a conversation without linking again. The phone answers later; the messages then appear in the conversation read with `source: history_sync`. The channel must have `store_history_sync` on (otherwise 409 `history_sync_off`) and must hold at least one message of the chat (otherwise 404 `no_history_anchor`). At most one request per channel every 30 seconds and per chat every 5 minutes (429 `history_request_too_soon`, with Retry-After). Each request is recorded in your API access records. Whether the phone answers, and how much, is up to the phone.",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          },
          {
            "$ref": "#/components/parameters/chat"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "count": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 50,
                    "default": 50,
                    "description": "How many older messages to ask for."
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Requested",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "requested"
                      ]
                    },
                    "chat_jid": {
                      "type": "string",
                      "description": "The chat the phone was asked about."
                    },
                    "before_message_id": {
                      "type": "string",
                      "description": "The answer will be older than this message."
                    },
                    "before": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "count": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "status",
                    "chat_jid",
                    "before_message_id",
                    "before",
                    "count"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sessions/{name}/chats": {
      "get": {
        "operationId": "listChats",
        "summary": "List chats, up to 100",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "chats": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Chat"
                      }
                    },
                    "source": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "chats"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sessions/{name}/chats/{chat}/messages": {
      "get": {
        "operationId": "listChatMessages",
        "summary": "Read a conversation (a person or a group), newest first, one page at a time",
        "description": "Reads this channel's own message log for one chat. For a person, on a channel holding the `lid_lookup` grant, both of their addresses (phone JID and LID) are read and merged into one timeline; `merge` says whether that happened and, if not, why. In a group, each sender is presented as one identity: `sender_jid` is their phone JID where known, `sender_lid` the LID they were stored under. Each message says which chat it is filed under (`source_chat`). Page with `before`: pass the previous page's `next_before` until it is absent. Every page returned is recorded in this channel's API access records (who read it, which chats, how many messages; never the content).",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          },
          {
            "$ref": "#/components/parameters/chat"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Messages per page, 1 to 500. Default 50.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 50
            }
          },
          {
            "name": "before",
            "in": "query",
            "required": false,
            "description": "Continue from a previous page: its `next_before`, exactly as returned. Any other value is a 400.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{1,256}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "messages": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Message"
                      }
                    },
                    "chats": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "The chat addresses this page was read from."
                    },
                    "merge": {
                      "type": "string",
                      "enum": [
                        "merged",
                        "none_known",
                        "not_granted",
                        "not_a_person"
                      ],
                      "description": "`merged`: both of the person's addresses were read. `none_known`: no other address is known for them. `not_granted`: this channel does not hold the `lid_lookup` grant, so only the named address was read. `not_a_person`: a group (one chat)."
                    },
                    "next_before": {
                      "type": "string",
                      "description": "Present when older messages exist: pass it as `before` for the next page."
                    },
                    "source": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "messages",
                    "chats",
                    "merge"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sessions/{name}/communities": {
      "post": {
        "operationId": "createCommunity",
        "summary": "Create a community",
        "tags": [
          "Communities"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "subject": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 25
                  }
                },
                "required": [
                  "subject"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Group"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sessions/{name}/communities/{community}": {
      "get": {
        "operationId": "getCommunity",
        "summary": "Get a community",
        "tags": [
          "Communities"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          },
          {
            "$ref": "#/components/parameters/community"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Group"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sessions/{name}/communities/{community}/invite-link": {
      "get": {
        "operationId": "getCommunityInviteLink",
        "summary": "Get a community's invite link",
        "tags": [
          "Communities"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          },
          {
            "$ref": "#/components/parameters/community"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "invite_link": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "invite_link"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sessions/{name}/communities/{community}/links": {
      "post": {
        "operationId": "linkGroup",
        "summary": "Link a group to a community",
        "tags": [
          "Communities"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          },
          {
            "$ref": "#/components/parameters/community"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "group_jid": {
                    "type": "string"
                  },
                  "hidden": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "group_jid"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Group"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sessions/{name}/communities/{community}/links/{group}": {
      "delete": {
        "operationId": "unlinkGroup",
        "summary": "Unlink a group from a community",
        "tags": [
          "Communities"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          },
          {
            "$ref": "#/components/parameters/community"
          },
          {
            "$ref": "#/components/parameters/group"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Group"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sessions/{name}/communities/{community}/participants": {
      "get": {
        "operationId": "listCommunityParticipants",
        "summary": "List a community's participants",
        "tags": [
          "Communities"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          },
          {
            "$ref": "#/components/parameters/community"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "participants": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/GroupParticipant"
                      }
                    },
                    "count": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "participants",
                    "count"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sessions/{name}/communities/{community}/subgroups": {
      "get": {
        "operationId": "listSubGroups",
        "summary": "List a community's subgroups",
        "tags": [
          "Communities"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          },
          {
            "$ref": "#/components/parameters/community"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "subgroups": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SubGroup"
                      }
                    }
                  },
                  "required": [
                    "subgroups"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      },
      "post": {
        "operationId": "createSubGroup",
        "summary": "Create a subgroup in a community",
        "tags": [
          "Communities"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          },
          {
            "$ref": "#/components/parameters/community"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "subject": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 25
                  },
                  "participants": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "maxItems": 50
                  },
                  "hidden": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "subject"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Group"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The channel's state does not allow this, or the subgroup was created and verification definitively failed.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/Error"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "code": {
                              "not": {
                                "const": "verification_failed"
                              }
                            }
                          }
                        }
                      ]
                    },
                    {
                      "$ref": "#/components/schemas/VerificationFailedError"
                    }
                  ]
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sessions/{name}/contacts/check": {
      "post": {
        "operationId": "checkContacts",
        "summary": "Check which phone numbers are on WhatsApp",
        "tags": [
          "Contacts"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "phones": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1,
                    "maxItems": 50
                  }
                },
                "required": [
                  "phones"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ContactCheck"
                      }
                    }
                  },
                  "required": [
                    "results"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sessions/{name}/contacts/{who}": {
      "get": {
        "operationId": "getContact",
        "summary": "Get a contact",
        "tags": [
          "Contacts"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          },
          {
            "$ref": "#/components/parameters/who"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactInfo"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      },
      "put": {
        "operationId": "saveContact",
        "summary": "Save or update a contact",
        "tags": [
          "Contacts"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          },
          {
            "$ref": "#/components/parameters/who"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "full_name": {
                    "type": "string"
                  },
                  "first_name": {
                    "type": "string"
                  }
                },
                "required": [
                  "full_name"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactInfo"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      },
      "delete": {
        "operationId": "removeContact",
        "summary": "Remove a contact",
        "description": "Clears the contact's stored names and, where phone writes are enabled, removes the contact from the linked phone's contact list through the same queue as a save. Idempotent: removing a contact that is not stored, or already removed, answers 200. handset is removed once the phone's app state confirmed the removal. Without phone writes enabled it answers off and only the stored names are cleared. While the channel's phone writes are halted it behaves exactly as a save: the names are cleared, nothing is sent to the phone, and handset is failed. A removal that was queued waits until an operator clears the halt; one that could not be queued is not made, so send it again once the halt is cleared.",
        "tags": [
          "Contacts"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          },
          {
            "$ref": "#/components/parameters/who"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactInfo"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sessions/{name}/groups": {
      "get": {
        "operationId": "listGroups",
        "summary": "List groups",
        "tags": [
          "Groups"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "groups": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Group"
                      }
                    }
                  },
                  "required": [
                    "groups"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      },
      "post": {
        "operationId": "createGroup",
        "summary": "Create a group",
        "tags": [
          "Groups"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "subject": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 25
                  },
                  "participants": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "maxItems": 50
                  },
                  "parent_jid": {
                    "type": "string",
                    "description": "Create the group inside this community."
                  }
                },
                "required": [
                  "subject"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Group"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sessions/{name}/groups/{group}": {
      "get": {
        "operationId": "getGroup",
        "summary": "Get a group",
        "tags": [
          "Groups"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          },
          {
            "$ref": "#/components/parameters/group"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Group"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sessions/{name}/groups/{group}/description": {
      "put": {
        "operationId": "setGroupDescription",
        "summary": "Set or clear a group's description",
        "tags": [
          "Groups"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          },
          {
            "$ref": "#/components/parameters/group"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "description": {
                    "type": "string",
                    "maxLength": 2048,
                    "description": "An empty string removes the description."
                  }
                },
                "required": [
                  "description"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Group"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sessions/{name}/groups/{group}/invite-link": {
      "get": {
        "operationId": "getGroupInviteLink",
        "summary": "Get a group's invite link",
        "tags": [
          "Groups"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          },
          {
            "$ref": "#/components/parameters/group"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "invite_link": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "invite_link"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sessions/{name}/groups/{group}/participants": {
      "post": {
        "operationId": "changeParticipants",
        "summary": "Add, remove, promote or demote participants",
        "tags": [
          "Groups"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          },
          {
            "$ref": "#/components/parameters/group"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "add",
                      "remove",
                      "promote",
                      "demote"
                    ]
                  },
                  "participants": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1,
                    "maxItems": 50
                  }
                },
                "required": [
                  "action",
                  "participants"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ParticipantResult"
                      }
                    }
                  },
                  "required": [
                    "results"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sessions/{name}/groups/{group}/settings": {
      "put": {
        "operationId": "setGroupSettings",
        "summary": "Change a group's settings",
        "tags": [
          "Groups"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          },
          {
            "$ref": "#/components/parameters/group"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "announce": {
                    "type": "boolean"
                  },
                  "locked": {
                    "type": "boolean"
                  },
                  "member_add_mode": {
                    "type": "string",
                    "enum": [
                      "admin_add",
                      "all_member_add"
                    ]
                  }
                },
                "additionalProperties": false,
                "minProperties": 1
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GroupSettingsResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sessions/{name}/groups/{group}/subject": {
      "put": {
        "operationId": "setGroupSubject",
        "summary": "Set a group's subject",
        "tags": [
          "Groups"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          },
          {
            "$ref": "#/components/parameters/group"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "subject": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 25
                  }
                },
                "required": [
                  "subject"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "const": "renamed"
                    }
                  },
                  "required": [
                    "status"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sessions/{name}/lids/pn/{phone}": {
      "get": {
        "operationId": "getLidForPhone",
        "summary": "Look up the LID for a phone number",
        "description": "Reads the gateway's local LID map. It makes no call to WhatsApp. A channel can use it only when the gateway operator has granted it `lid_lookup`; otherwise the answer is 403 `lid_lookup_not_granted`. A number with no known LID is a 200 without `lid`.",
        "tags": [
          "Contacts"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          },
          {
            "$ref": "#/components/parameters/phone"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LIDMapping"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sessions/{name}/lids/{lid}": {
      "get": {
        "operationId": "getPhoneForLid",
        "summary": "Look up the phone number for a LID",
        "description": "Reads the gateway's local LID map. It makes no call to WhatsApp. A channel can use it only when the gateway operator has granted it `lid_lookup`; otherwise the answer is 403 `lid_lookup_not_granted`. A LID with no known phone number is a 200 without `pn`.",
        "tags": [
          "Contacts"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          },
          {
            "$ref": "#/components/parameters/lid"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LIDMapping"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sessions/{name}/media/{id}": {
      "get": {
        "operationId": "getMedia",
        "summary": "Download media by ID",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          },
          {
            "$ref": "#/components/parameters/id"
          }
        ],
        "responses": {
          "200": {
            "description": "The media bytes. Always served as application/octet-stream, as an attachment.",
            "headers": {
              "Content-Disposition": {
                "schema": {
                  "type": "string"
                }
              },
              "Content-Length": {
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "contentEncoding": "binary"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "One answer for media that belongs to another channel, to nobody, or does not exist (media_forbidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sessions/{name}/messages/contact": {
      "post": {
        "operationId": "sendContact",
        "summary": "Send a contact card",
        "tags": [
          "Messages"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "to": {
                    "type": "string"
                  },
                  "display_name": {
                    "type": "string"
                  },
                  "vcard": {
                    "type": "string"
                  }
                },
                "required": [
                  "to",
                  "display_name",
                  "vcard"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "504": {
            "$ref": "#/components/responses/SendUnconfirmed"
          }
        }
      }
    },
    "/sessions/{name}/messages/image": {
      "post": {
        "operationId": "sendImage",
        "summary": "Send an image",
        "tags": [
          "Messages"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "to": {
                    "type": "string",
                    "description": "Phone number in international form, or a JID."
                  },
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "description": "Where the gateway fetches the media."
                  },
                  "caption": {
                    "type": "string",
                    "maxLength": 4096,
                    "description": "Shown under the image."
                  }
                },
                "required": [
                  "to",
                  "url"
                ],
                "additionalProperties": false
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "to": {
                    "type": "string",
                    "description": "Phone number in international form, or a JID."
                  },
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "The file itself. Send the file in this part instead of a `url`; at most 5 MiB. Upload example: `curl -F to=... -F file=@file`."
                  },
                  "caption": {
                    "type": "string",
                    "description": "Shown under the image. At most 4096 bytes."
                  }
                },
                "required": [
                  "to",
                  "file"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "504": {
            "$ref": "#/components/responses/SendUnconfirmed"
          }
        },
        "description": "JPEG or PNG, at most 5 MiB. The media is fetched by the gateway from `url`. The URL must be http or https, carry no credentials, and resolve to a public internet address; it may redirect at most 3 times, and every hop is held to the same rule. It must answer 2xx within 30 seconds. The message type WhatsApp is told comes from the bytes, not from the URL's Content-Type. A media message counts as one message against the daily send cap and the sandbox allowance, the same as a text, whatever its size. Errors: `media_type_unsupported` and `media_fetch_failed` (400) are about the media; `media_too_large` is 400 when the media is fetched from a URL and 413 when the uploaded file (multipart) is over the limit; `media_upload_failed` (502) means the upload to WhatsApp failed and nothing was sent, so retrying is safe. A media send can also answer 429 `gateway_busy` with `bound: \"media\"` when the gateway is holding as many media payloads as it can; retry after the Retry-After header (10 seconds)."
      }
    },
    "/sessions/{name}/messages/video": {
      "post": {
        "operationId": "sendVideo",
        "summary": "Send a video",
        "tags": [
          "Messages"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "to": {
                    "type": "string",
                    "description": "Phone number in international form, or a JID."
                  },
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "description": "Where the gateway fetches the media."
                  },
                  "caption": {
                    "type": "string",
                    "maxLength": 4096,
                    "description": "Shown under the video."
                  }
                },
                "required": [
                  "to",
                  "url"
                ],
                "additionalProperties": false
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "to": {
                    "type": "string",
                    "description": "Phone number in international form, or a JID."
                  },
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "The file itself. Send the file in this part instead of a `url`; at most 16 MiB. Upload example: `curl -F to=... -F file=@file`."
                  },
                  "caption": {
                    "type": "string",
                    "description": "Shown under the video. At most 4096 bytes."
                  }
                },
                "required": [
                  "to",
                  "file"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "504": {
            "$ref": "#/components/responses/SendUnconfirmed"
          }
        },
        "description": "MP4 (H.264) or 3GPP, at most 16 MiB. The media is fetched by the gateway from `url`. The URL must be http or https, carry no credentials, and resolve to a public internet address; it may redirect at most 3 times, and every hop is held to the same rule. It must answer 2xx within 30 seconds. The message type WhatsApp is told comes from the bytes, not from the URL's Content-Type. A media message counts as one message against the daily send cap and the sandbox allowance, the same as a text, whatever its size. Errors: `media_type_unsupported` and `media_fetch_failed` (400) are about the media; `media_too_large` is 400 when the media is fetched from a URL and 413 when the uploaded file (multipart) is over the limit; `media_upload_failed` (502) means the upload to WhatsApp failed and nothing was sent, so retrying is safe. A media send can also answer 429 `gateway_busy` with `bound: \"media\"` when the gateway is holding as many media payloads as it can; retry after the Retry-After header (10 seconds)."
      }
    },
    "/sessions/{name}/messages/audio": {
      "post": {
        "operationId": "sendAudio",
        "summary": "Send an audio file",
        "tags": [
          "Messages"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "to": {
                    "type": "string",
                    "description": "Phone number in international form, or a JID."
                  },
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "description": "Where the gateway fetches the media."
                  }
                },
                "required": [
                  "to",
                  "url"
                ],
                "additionalProperties": false
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "to": {
                    "type": "string",
                    "description": "Phone number in international form, or a JID."
                  },
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "The file itself. Send the file in this part instead of a `url`; at most 16 MiB. Upload example: `curl -F to=... -F file=@file`."
                  }
                },
                "required": [
                  "to",
                  "file"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "504": {
            "$ref": "#/components/responses/SendUnconfirmed"
          }
        },
        "description": "AAC, MP4 audio, MP3, AMR or Ogg, at most 16 MiB. Sent as a file to play, not a voice note. The media is fetched by the gateway from `url`. The URL must be http or https, carry no credentials, and resolve to a public internet address; it may redirect at most 3 times, and every hop is held to the same rule. It must answer 2xx within 30 seconds. The message type WhatsApp is told comes from the bytes, not from the URL's Content-Type. A media message counts as one message against the daily send cap and the sandbox allowance, the same as a text, whatever its size. Errors: `media_type_unsupported` and `media_fetch_failed` (400) are about the media; `media_too_large` is 400 when the media is fetched from a URL and 413 when the uploaded file (multipart) is over the limit; `media_upload_failed` (502) means the upload to WhatsApp failed and nothing was sent, so retrying is safe. A media send can also answer 429 `gateway_busy` with `bound: \"media\"` when the gateway is holding as many media payloads as it can; retry after the Retry-After header (10 seconds)."
      }
    },
    "/sessions/{name}/messages/voice": {
      "post": {
        "operationId": "sendVoice",
        "summary": "Send a voice note",
        "tags": [
          "Messages"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "to": {
                    "type": "string",
                    "description": "Phone number in international form, or a JID."
                  },
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "description": "Where the gateway fetches the media."
                  }
                },
                "required": [
                  "to",
                  "url"
                ],
                "additionalProperties": false
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "to": {
                    "type": "string",
                    "description": "Phone number in international form, or a JID."
                  },
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "The file itself. Send the file in this part instead of a `url`; at most 16 MiB. Upload example: `curl -F to=... -F file=@file`."
                  }
                },
                "required": [
                  "to",
                  "file"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "504": {
            "$ref": "#/components/responses/SendUnconfirmed"
          }
        },
        "description": "Ogg Opus only, at most 16 MiB. Shown as a voice note with its duration. The media is fetched by the gateway from `url`. The URL must be http or https, carry no credentials, and resolve to a public internet address; it may redirect at most 3 times, and every hop is held to the same rule. It must answer 2xx within 30 seconds. The message type WhatsApp is told comes from the bytes, not from the URL's Content-Type. A media message counts as one message against the daily send cap and the sandbox allowance, the same as a text, whatever its size. Errors: `media_type_unsupported` and `media_fetch_failed` (400) are about the media; `media_too_large` is 400 when the media is fetched from a URL and 413 when the uploaded file (multipart) is over the limit; `media_upload_failed` (502) means the upload to WhatsApp failed and nothing was sent, so retrying is safe. A media send can also answer 429 `gateway_busy` with `bound: \"media\"` when the gateway is holding as many media payloads as it can; retry after the Retry-After header (10 seconds)."
      }
    },
    "/sessions/{name}/messages/document": {
      "post": {
        "operationId": "sendDocument",
        "summary": "Send a document",
        "tags": [
          "Messages"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "to": {
                    "type": "string",
                    "description": "Phone number in international form, or a JID."
                  },
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "description": "Where the gateway fetches the media."
                  },
                  "caption": {
                    "type": "string",
                    "maxLength": 4096,
                    "description": "Shown under the document."
                  },
                  "filename": {
                    "type": "string",
                    "maxLength": 255,
                    "description": "The name the recipient sees. Defaults to the URL's last path segment, then `document` plus an extension for its type."
                  },
                  "mime_type": {
                    "type": "string",
                    "description": "The document's media type, e.g. `application/pdf`."
                  }
                },
                "required": [
                  "to",
                  "url"
                ],
                "additionalProperties": false
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "to": {
                    "type": "string",
                    "description": "Phone number in international form, or a JID."
                  },
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "The file itself. Send the file in this part instead of a `url`; at most 32 MiB. Upload example: `curl -F to=... -F file=@file`."
                  },
                  "caption": {
                    "type": "string",
                    "description": "Shown under the document. At most 4096 bytes."
                  },
                  "filename": {
                    "type": "string",
                    "description": "The name the recipient sees. When omitted, the file part's own filename is used (last path element only, at most 255 bytes), then `document` plus an extension for its type."
                  },
                  "mime_type": {
                    "type": "string",
                    "description": "The document's media type, e.g. `application/pdf`. When omitted, the file part's Content-Type is used."
                  }
                },
                "required": [
                  "to",
                  "file"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "504": {
            "$ref": "#/components/responses/SendUnconfirmed"
          }
        },
        "description": "Any file, at most 32 MiB. Its type is `mime_type` if given, else the URL's Content-Type, else detected from the bytes. The media is fetched by the gateway from `url`. The URL must be http or https, carry no credentials, and resolve to a public internet address; it may redirect at most 3 times, and every hop is held to the same rule. It must answer 2xx within 30 seconds. The message type WhatsApp is told comes from the bytes, not from the URL's Content-Type. A media message counts as one message against the daily send cap and the sandbox allowance, the same as a text, whatever its size. Errors: `media_type_unsupported` and `media_fetch_failed` (400) are about the media; `media_too_large` is 400 when the media is fetched from a URL and 413 when the uploaded file (multipart) is over the limit; `media_upload_failed` (502) means the upload to WhatsApp failed and nothing was sent, so retrying is safe. A media send can also answer 429 `gateway_busy` with `bound: \"media\"` when the gateway is holding as many media payloads as it can; retry after the Retry-After header (10 seconds)."
      }
    },
    "/sessions/{name}/messages/poll": {
      "post": {
        "operationId": "sendPoll",
        "summary": "Send a poll",
        "tags": [
          "Messages"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "to": {
                    "type": "string",
                    "description": "Phone number in international form, or a JID."
                  },
                  "question": {
                    "type": "string",
                    "maxLength": 255,
                    "description": "The question."
                  },
                  "options": {
                    "type": "array",
                    "minItems": 2,
                    "maxItems": 12,
                    "items": {
                      "type": "string",
                      "maxLength": 100
                    },
                    "description": "The options, each different."
                  },
                  "multiple": {
                    "type": "boolean",
                    "description": "Let a voter choose more than one option. Default false."
                  }
                },
                "required": [
                  "to",
                  "question",
                  "options"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "504": {
            "$ref": "#/components/responses/SendUnconfirmed"
          }
        },
        "description": "2 to 12 different options; one answer unless `multiple` is true. Votes arrive as `message` webhook events of type `poll_vote`, with the chosen options by name. A poll counts as one message against the daily send cap and the sandbox allowance."
      }
    },
    "/sessions/{name}/messages/{message}": {
      "delete": {
        "operationId": "deleteMessage",
        "summary": "Delete a message for everyone",
        "tags": [
          "Messages"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          },
          {
            "$ref": "#/components/parameters/message"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RevokeResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "504": {
            "$ref": "#/components/responses/SendUnconfirmed"
          }
        },
        "description": "Deletes for everyone, in a chat or a group, a message this channel's number sent (through the API or on the paired phone) and this channel has in its message log. Only your own messages: someone else's answers 403 `not_own_message` (deleting others' messages as a group admin is not offered). A message more than 48 hours old answers 409 `revoke_window_passed`, and one not in the log 404 `message_not_found`; neither sends anything. IDEMPOTENT: deleting a message the log already records as deleted answers 200 with `status: \"already_revoked\"` and sends nothing; a delete of the same message while one is still running answers 409 `revoke_in_progress`. A delete counts as one message against the daily send cap and the sandbox allowance, as a reaction does. The log keeps the message and marks it with `revoked_at`. A delete made on the paired phone, or by the sender of a message you received, arrives as a `message` webhook event of type `protocol` carrying `revoke.message_id`."
      }
    },
    "/sessions/{name}/messages/{message}/reaction": {
      "put": {
        "operationId": "setReaction",
        "summary": "React to a message",
        "tags": [
          "Messages"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          },
          {
            "$ref": "#/components/parameters/message"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "emoji": {
                    "type": "string",
                    "description": "One emoji."
                  }
                },
                "required": [
                  "emoji"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "504": {
            "$ref": "#/components/responses/SendUnconfirmed"
          }
        },
        "description": "Reacts with one emoji to a message in this channel's log; a new reaction replaces this channel's earlier one. Counts as one message against the daily send cap and the sandbox allowance. A message this channel has no record of answers 404 `message_not_found`."
      },
      "delete": {
        "operationId": "removeReaction",
        "summary": "Remove a reaction",
        "tags": [
          "Messages"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          },
          {
            "$ref": "#/components/parameters/message"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "504": {
            "$ref": "#/components/responses/SendUnconfirmed"
          }
        },
        "description": "Removes this channel's reaction to a message. Counts as one message against the daily send cap and the sandbox allowance, as WhatsApp sends it as one."
      }
    },
    "/sessions/{name}/messages/read": {
      "post": {
        "operationId": "markRead",
        "summary": "Send read receipts",
        "tags": [
          "Messages"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "chat": {
                    "type": "string"
                  },
                  "sender": {
                    "type": "string"
                  },
                  "message_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "maxItems": 500
                  },
                  "up_to": {
                    "type": "boolean",
                    "description": "Group chats only, and only with message_ids. When true, every unread message in the group at or before the newest id you pass is also marked read, each to its own sender, so everyone you have read up to sees it. receipted then counts those messages too. Without it (the default) only the ids you pass are marked. A request that would mark 500 or more messages this way, or send receipts to more than 5 senders, is refused up front with 400 up_to_range_too_large and nothing is sent; mark an older id first, or send it without up_to. A request that fails after a receipt went out is never a 4xx: it is 502 receipt_partial with receipted, the count already marked."
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "const": "read"
                    },
                    "receipted": {
                      "type": "integer"
                    },
                    "skipped": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "status",
                    "receipted"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/ReadBadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sessions/{name}/messages/text": {
      "post": {
        "operationId": "sendText",
        "summary": "Send a text",
        "tags": [
          "Messages"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "to": {
                    "type": "string",
                    "description": "Phone number in international form, or a JID."
                  },
                  "text": {
                    "type": "string",
                    "description": "The message."
                  },
                  "mentions": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Phone numbers or JIDs to mention."
                  },
                  "quoted": {
                    "type": "string",
                    "pattern": "^[A-Za-z0-9._-]{1,128}$",
                    "description": "ID of a message in the same chat to reply to."
                  }
                },
                "required": [
                  "to",
                  "text"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "504": {
            "$ref": "#/components/responses/SendUnconfirmed"
          }
        }
      }
    },
    "/sessions/{name}/presence": {
      "post": {
        "operationId": "setPresence",
        "summary": "Set presence",
        "tags": [
          "Messages"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "presence": {
                    "type": "string",
                    "enum": [
                      "available",
                      "online",
                      "unavailable",
                      "offline"
                    ]
                  }
                },
                "required": [
                  "presence"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StatusOK"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sessions/{name}/safety": {
      "get": {
        "operationId": "getSafety",
        "summary": "Read this channel's health readings",
        "tags": [
          "Channel"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SafetyReport"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sessions/{name}/typing": {
      "post": {
        "operationId": "setTyping",
        "summary": "Show or clear the typing indicator",
        "tags": [
          "Messages"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "chat": {
                    "type": "string"
                  },
                  "state": {
                    "type": "string",
                    "enum": [
                      "typing",
                      "composing",
                      "recording",
                      "paused",
                      "stop"
                    ]
                  }
                },
                "required": [
                  "chat",
                  "state"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StatusOK"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sessions/{name}/warming": {
      "get": {
        "operationId": "getWarming",
        "summary": "Read this channel's warming state",
        "description": "Warming is available only to accounts the gateway has enabled it for. For any other channel this route answers `not_found`.",
        "tags": [
          "Warming"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerWarming"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      },
      "put": {
        "operationId": "setWarming",
        "summary": "Turn warming on or off for this channel",
        "description": "The first switch-on of an account must carry acknowledge, which records the account's acceptance of this text (version 2026-10-13):\n\n> Warming sends short, pre-written chat messages between your own channels, automatically, from your numbers. You do not write these messages. WAgate does, and sends them on your behalf. They are ordinary WhatsApp messages from your numbers: you will see the chats on your phones, and they count toward your sandbox allowances.\n>\n> WhatsApp decides what it allows. Warming is meant to lower the risk of a restriction, but WhatsApp can still restrict or ban any number, including one that is warming, for reasons we cannot see and cannot reverse. A restriction or ban alone does not entitle you to a refund or compensation. Refund rights required by law and the liability exceptions in Terms section 13 still apply.\n>\n> You can turn warming off for any number at any time. It stops before the next message.\n>\n> I understand that WAgate will send pre-written messages from my numbers on my behalf, and that WhatsApp may still restrict or ban a number that is warming. I own these numbers or am authorized to operate them, and I accept that risk.\n\nTurning it on needs the account owner's acknowledgement of the warming terms: either recorded earlier (`warming_ack_required` until it is) or sent here as `acknowledge`. After a safety stop the number cools down first (`warming_cooldown`, with `restart_after`); a restart after that stop also needs `restart_ack: true` (`warming_restart_ack_required`). Only `enabled`, `restart_ack` and `acknowledge` are accepted; any other field is refused. A successful call returns the channel (`Session`); its `warming` field holds the same object `GET` returns. Available only to accounts the gateway has enabled warming for; otherwise `not_found`.",
        "tags": [
          "Warming"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "restart_ack": {
                    "type": "boolean",
                    "description": "Confirms a restart after a safety stop."
                  },
                  "acknowledge": {
                    "type": "object",
                    "properties": {
                      "text_version": {
                        "type": "string",
                        "description": "The version of the warming terms being acknowledged. Checked only while the account has not yet acknowledged the current version: then an unknown version is `warming_ack_version_unknown`. Once the account has acknowledged the current version, a supplied value is ignored and enabling can succeed."
                      }
                    },
                    "required": [
                      "text_version"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "enabled"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Session"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sessions/{name}/warming/webhooks": {
      "put": {
        "operationId": "setWarmingWebhooks",
        "summary": "Choose whether this channel's receivers get warming traffic",
        "description": "Off by default. When on, this channel's webhook receivers also receive the events its warming traffic produces, marked `warming: true`. Available only to accounts the gateway has enabled warming for; otherwise `not_found`.",
        "tags": [
          "Warming"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "include_warming": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "include_warming"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerWarming"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sessions/{name}/webhooks": {
      "get": {
        "operationId": "listWebhooks",
        "summary": "List this channel's webhook receivers",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "receivers": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookReceiverView"
                      }
                    },
                    "persistent": {
                      "type": "boolean"
                    },
                    "persistence_note": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "receivers"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      },
      "put": {
        "operationId": "setWebhooks",
        "summary": "Replace this channel's webhook receivers",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "receivers": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/WebhookReceiver"
                    },
                    "maxItems": 8,
                    "description": "Replaces the whole list. An empty list removes every receiver."
                  }
                },
                "required": [
                  "receivers"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "receivers": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookReceiverView"
                      }
                    },
                    "persistent": {
                      "type": "boolean"
                    },
                    "persistence_note": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "receivers"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sessions/{name}/webhooks/test": {
      "post": {
        "operationId": "testWebhook",
        "summary": "Send one signed test event to a configured receiver",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/name"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string"
                  }
                },
                "required": [
                  "url"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "const": "delivered"
                    },
                    "detail": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "status"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "The channel token."
      }
    },
    "parameters": {
      "name": {
        "name": "name",
        "in": "path",
        "required": true,
        "description": "Channel ID.",
        "schema": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_-]+$"
        },
        "example": "my-channel"
      },
      "group": {
        "name": "group",
        "in": "path",
        "required": true,
        "description": "Group JID.",
        "schema": {
          "type": "string",
          "pattern": "^[^/]+$"
        },
        "example": "120363000000000001@g.us"
      },
      "community": {
        "name": "community",
        "in": "path",
        "required": true,
        "description": "Community JID.",
        "schema": {
          "type": "string",
          "pattern": "^[^/]+$"
        },
        "example": "120363000000000002@g.us"
      },
      "chat": {
        "name": "chat",
        "in": "path",
        "required": true,
        "description": "Chat JID.",
        "schema": {
          "type": "string",
          "pattern": "^[^/]+$"
        },
        "example": "12025550123@s.whatsapp.net"
      },
      "who": {
        "name": "who",
        "in": "path",
        "required": true,
        "description": "Contact address: a user JID or a LID.",
        "schema": {
          "type": "string",
          "pattern": "^[^/]+$"
        },
        "example": "12025550123@s.whatsapp.net"
      },
      "id": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "Media ID, as delivered in a message's media_id.",
        "schema": {
          "type": "string",
          "pattern": "^[0-9a-f]{64}\\.[a-z0-9]{1,8}$"
        },
        "example": "0000000000000000000000000000000000000000000000000000000000000000.jpg"
      },
      "message": {
        "name": "message",
        "in": "path",
        "required": true,
        "description": "ID of a message this channel has in its message log (sent or received while connected).",
        "schema": {
          "type": "string",
          "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$"
        },
        "example": "3EB0C767D26A1D8E2A41"
      },
      "phone": {
        "name": "phone",
        "in": "path",
        "required": true,
        "description": "Phone number, digits with an optional leading +.",
        "schema": {
          "type": "string",
          "pattern": "^\\+?[0-9]{6,20}$"
        },
        "example": "12025550123"
      },
      "lid": {
        "name": "lid",
        "in": "path",
        "required": true,
        "description": "A LID: digits followed by the literal @lid.",
        "schema": {
          "type": "string",
          "pattern": "^[0-9]+@lid$"
        },
        "example": "12025550123@lid"
      }
    },
    "responses": {
      "BadRequest": {
        "description": "The request was refused as sent.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "PayloadTooLarge": {
        "description": "An uploaded file (multipart/form-data) is larger than its media type allows (media_too_large).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing, invalid or stale token, or a channel this token does not open.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "headers": {
          "WWW-Authenticate": {
            "schema": {
              "type": "string"
            }
          }
        }
      },
      "Forbidden": {
        "description": "The token is valid and may not do this.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "The route or the named object does not exist.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Conflict": {
        "description": "The channel's state does not allow this, for example not_linked or stood_down.",
        "content": {
          "application/json": {
            "schema": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/Error"
                },
                {
                  "$ref": "#/components/schemas/WarmingCooldownError"
                }
              ]
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "A gateway flood guard or the webhook-test cooldown (rate_limited, with a Retry-After header), the gateway being busy with this channel's earlier requests (gateway_busy, Retry-After: 1), a sandbox channel's allowance (sandbox_allowance_exceeded, with Retry-After), an operator stop (daily_send_cap_exceeded), an optional warm-up protection allowance (daily_cap_<class>, only when warm-up protection is on), a history request inside its rate limit (history_request_too_soon, with Retry-After), or WhatsApp throttling the number (whatsapp_rate_limited, no Retry-After). Paid numbers have no gateway allowance. `code` says which: the bodies never share one.",
        "headers": {
          "Retry-After": {
            "description": "Seconds to wait. Sent with rate_limited, gateway_busy and sandbox_allowance_exceeded.",
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/RateLimitError"
                },
                {
                  "$ref": "#/components/schemas/WhatsAppRateLimitError"
                },
                {
                  "$ref": "#/components/schemas/DailySendCapError"
                },
                {
                  "$ref": "#/components/schemas/ActionCapError"
                },
                {
                  "$ref": "#/components/schemas/SandboxAllowanceError"
                },
                {
                  "$ref": "#/components/schemas/GatewayBusyError"
                },
                {
                  "$ref": "#/components/schemas/HistoryRequestTooSoonError"
                }
              ]
            }
          }
        }
      },
      "InternalError": {
        "description": "The gateway could not complete the request.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "BadGateway": {
        "description": "The service behind the gateway could not be reached or answered unusably. Retry.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unavailable": {
        "description": "Temporarily unavailable, for example the token could not be checked, the gateway could not confirm its own account setup (operator_unresolved, with a Retry-After header), or a call made under a phone lane reached the lane's deadline (lane_reclaimed, with a Retry-After header and `ambiguous`). Unless the error explicitly says nothing was sent (for example `ambiguous: false`), treat any write as possibly applied; list or read the state of what you changed before retrying a write; never re-create (e.g. a group) without reading/listing first; reads can be retried after a short back-off. Honor Retry-After when present.",
        "content": {
          "application/json": {
            "schema": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/Error"
                },
                {
                  "$ref": "#/components/schemas/OperatorUnresolvedError"
                },
                {
                  "$ref": "#/components/schemas/LaneReclaimedError"
                }
              ]
            }
          }
        }
      },
      "ReadBadGateway": {
        "description": "Either the service behind the gateway could not be reached (retry), or, with code `receipt_partial`, some read receipts went out before the request failed: `receipted` is how many messages were marked. Do not resend a `receipt_partial` request as is.",
        "content": {
          "application/json": {
            "schema": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/Error"
                },
                {
                  "$ref": "#/components/schemas/ReceiptPartialError"
                }
              ]
            }
          }
        }
      },
      "SendUnconfirmed": {
        "description": "The outcome is unknown. Look up `id` in history before resending.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/SendUnconfirmedError"
            }
          }
        }
      }
    },
    "schemas": {
      "ErrorCode": {
        "type": "string",
        "description": "Stable machine-readable error code. Branch on this, not on `error`. A client should treat an unknown code as a generic failure.",
        "enum": [
          "invalid_body",
          "invalid_request",
          "up_to_range_too_large",
          "field_not_allowed",
          "header_not_allowed",
          "quote_not_found",
          "quote_wrong_chat",
          "media_fetch_failed",
          "media_type_unsupported",
          "media_too_large",
          "unauthorized",
          "credential_stale",
          "slot_ended",
          "admin_only_field",
          "account_forbidden",
          "account_suspended",
          "number_forbidden",
          "blocked",
          "media_forbidden",
          "not_found",
          "message_not_found",
          "not_own_message",
          "revoke_window_passed",
          "revoke_in_progress",
          "not_linked",
          "stood_down",
          "verification_failed",
          "rate_limited",
          "whatsapp_rate_limited",
          "whatsapp_disconnected",
          "whatsapp_timeout",
          "daily_send_cap_exceeded",
          "daily_cap_group_add",
          "daily_cap_contact_write",
          "daily_cap_contact_check",
          "sandbox_allowance_exceeded",
          "gateway_busy",
          "channel_unentitled",
          "internal_error",
          "upstream_unreachable",
          "upstream_redirect",
          "upstream_not_json",
          "upstream_not_relayable",
          "upstream_too_large",
          "upstream_unavailable",
          "media_upload_failed",
          "operator_unresolved",
          "read_unresolvable",
          "history_unresolvable",
          "audit_unavailable",
          "history_sync_off",
          "no_history_anchor",
          "history_request_too_soon",
          "history_unavailable",
          "send_unconfirmed",
          "receipt_partial",
          "lane_reclaimed",
          "warming_peer_refused",
          "warming_cooldown",
          "warming_ack_required",
          "warming_restart_ack_required",
          "warming_state_unreadable",
          "warming_ack_version_unknown",
          "lid_lookup_not_granted"
        ]
      },
      "Error": {
        "type": "object",
        "properties": {
          "code": {
            "$ref": "#/components/schemas/ErrorCode"
          },
          "error": {
            "type": "string",
            "description": "Human-readable message. It can change; do not parse it."
          },
          "details": {
            "type": "string"
          },
          "reason": {
            "type": "string",
            "enum": [
              "proxy_unreachable",
              "proxy_auth_failed",
              "proxy_tls_failed",
              "proxy_timeout",
              "proxy_dns_failed",
              "connect_failed"
            ],
            "description": "Present when the request failed on a connection-level error (a connect, a relink, a proxy change that reconnects, or any call whose socket or proxy failed). `error` is then a fixed sentence for this reason. For a channel or customer caller `error` is always a fixed sentence for its `code`, or the gateway's own validation message; the underlying error text is never returned."
          }
        },
        "required": [
          "code",
          "error"
        ]
      },
      "RateLimitError": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Error"
          },
          {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "const": "rate_limited"
              },
              "window": {
                "type": "string",
                "description": "The ceiling that refused the request, for example token.send. Not a closed list."
              },
              "retry_after": {
                "type": "integer",
                "minimum": 1
              }
            },
            "required": [
              "code",
              "window",
              "retry_after"
            ]
          }
        ]
      },
      "WhatsAppRateLimitError": {
        "description": "WhatsApp itself throttled the linked number (an IQ 429), not a ceiling of ours. No Retry-After is sent: WhatsApp does not say how long. Back off and retry later.",
        "allOf": [
          {
            "$ref": "#/components/schemas/Error"
          },
          {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "const": "whatsapp_rate_limited"
              }
            },
            "required": [
              "code"
            ]
          }
        ]
      },
      "OperatorUnresolvedError": {
        "description": "The gateway could not confirm its own account setup for a moment, so it could not decide whether this channel may send. Nothing about the channel is wrong. Retry after the Retry-After header (5 seconds).",
        "allOf": [
          {
            "$ref": "#/components/schemas/Error"
          },
          {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "const": "operator_unresolved"
              },
              "retry_after": {
                "type": "integer",
                "minimum": 1
              }
            },
            "required": [
              "code",
              "retry_after"
            ]
          }
        ]
      },
      "LaneReclaimedError": {
        "description": "A call made under a phone lane reached the lane's deadline. Retry after the Retry-After header (1 second). `ambiguous` is always present: false means the call was refused before it was sent, so nothing reached WhatsApp and a plain retry is safe; true means the call was sent and its outcome is unknown, so a group add may already have applied: retry at your normal pace and treat \"already a participant\" as success.",
        "allOf": [
          {
            "$ref": "#/components/schemas/Error"
          },
          {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "const": "lane_reclaimed"
              },
              "retry_after": {
                "type": "integer",
                "minimum": 1
              },
              "ambiguous": {
                "type": "boolean",
                "description": "false: refused before it was sent, nothing reached WhatsApp. true: sent, outcome unknown."
              }
            },
            "required": [
              "code",
              "retry_after",
              "ambiguous"
            ]
          }
        ]
      },
      "WarmingCooldownError": {
        "description": "This number was stopped for safety and is cooling down. `restart_after` is when warming may be turned on again, or null when there is no recorded end and the gateway operator clears it.",
        "allOf": [
          {
            "$ref": "#/components/schemas/Error"
          },
          {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "const": "warming_cooldown"
              },
              "restart_after": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              }
            },
            "required": [
              "code",
              "restart_after"
            ]
          }
        ]
      },
      "HistoryRequestTooSoonError": {
        "description": "A history request inside its rate limit: one per channel every 30 seconds, one per chat every 5 minutes. Retry after the Retry-After header.",
        "allOf": [
          {
            "$ref": "#/components/schemas/Error"
          },
          {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "const": "history_request_too_soon"
              },
              "retry_after": {
                "type": "integer",
                "minimum": 1
              }
            },
            "required": [
              "code",
              "retry_after"
            ]
          }
        ]
      },
      "GatewayBusyError": {
        "description": "The gateway has too many requests in flight to take this one, or is holding as many media sends as it can. `bound` names the limit that refused it: this channel's own in-flight requests, all of this account's channels together, the gateway's shared pool, or `media` for media sends. Never queued: retry after the Retry-After header (1 second, or 10 seconds for `media`).",
        "allOf": [
          {
            "$ref": "#/components/schemas/Error"
          },
          {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "const": "gateway_busy"
              },
              "bound": {
                "type": "string",
                "enum": [
                  "channel",
                  "account",
                  "pool",
                  "media"
                ],
                "description": "Which limit refused the request. May be absent."
              },
              "retry_after": {
                "type": "integer",
                "minimum": 1
              }
            },
            "required": [
              "code",
              "retry_after"
            ]
          }
        ]
      },
      "SandboxAllowanceError": {
        "description": "A sandbox channel reached one of its allowances (180 messages a day, 10 new chats a month, 20 number checks a day, 5 group adds a day, 1,500 API calls a month; days and months in Bangkok time, UTC+7). Every request made with the sandbox channel's key counts as an API call, reads included. Sent with a Retry-After header. Paid channels never receive it.",
        "allOf": [
          {
            "$ref": "#/components/schemas/Error"
          },
          {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "const": "sandbox_allowance_exceeded"
              },
              "window": {
                "type": "string",
                "enum": [
                  "sandbox_messages_daily",
                  "sandbox_chats_monthly",
                  "sandbox_number_checks_daily",
                  "sandbox_group_adds_daily",
                  "sandbox_api_calls_monthly"
                ]
              },
              "retry_after": {
                "type": "integer",
                "minimum": 1
              },
              "cap": {
                "type": "integer"
              },
              "used": {
                "type": "integer"
              },
              "reserved": {
                "type": "integer"
              },
              "resets_at": {
                "type": "string",
                "format": "date-time"
              }
            },
            "required": [
              "code",
              "window",
              "retry_after",
              "cap",
              "used",
              "reserved",
              "resets_at"
            ]
          }
        ]
      },
      "DailySendCapError": {
        "description": "An operator stop: the operator has stopped sends on this channel (a ceiling of 0), or the channel is in the operator's warming programme. Paid numbers have no daily send cap, so on a customer channel this means the operator acted.",
        "allOf": [
          {
            "$ref": "#/components/schemas/Error"
          },
          {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "const": "daily_send_cap_exceeded"
              },
              "window": {
                "type": "string",
                "const": "daily_send"
              },
              "retry_after": {
                "type": "integer",
                "minimum": 1
              },
              "cap": {
                "type": "integer"
              },
              "sent": {
                "type": "integer"
              },
              "resets_at": {
                "type": "string",
                "format": "date-time"
              }
            },
            "required": [
              "code",
              "window",
              "retry_after",
              "cap",
              "sent",
              "resets_at"
            ]
          }
        ]
      },
      "ActionCapError": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Error"
          },
          {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "pattern": "^daily_cap_[a-z_]+$",
                "description": "daily_cap_ followed by the class."
              },
              "class": {
                "type": "string",
                "enum": [
                  "group_add",
                  "contact_write",
                  "contact_check"
                ]
              },
              "window": {
                "type": "string",
                "pattern": "^daily_[a-z_]+$",
                "description": "daily_ followed by the class."
              },
              "retry_after": {
                "type": "integer",
                "minimum": 1
              },
              "cap": {
                "type": "integer"
              },
              "sent": {
                "type": "integer"
              },
              "tier": {
                "type": "string"
              },
              "resets_at": {
                "type": "string",
                "format": "date-time"
              }
            },
            "required": [
              "code",
              "class",
              "window",
              "retry_after",
              "cap",
              "sent",
              "resets_at",
              "tier"
            ]
          }
        ]
      },
      "ReceiptPartialError": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Error"
          },
          {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "const": "receipt_partial"
              },
              "receipted": {
                "type": "integer",
                "description": "How many messages were marked read before the request failed. Do not resend the same request."
              }
            },
            "required": [
              "code",
              "receipted"
            ]
          }
        ]
      },
      "SendUnconfirmedError": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Error"
          },
          {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "const": "send_unconfirmed"
              },
              "ambiguous": {
                "const": true
              },
              "id": {
                "type": "string",
                "description": "The logged row of this send. Look it up in history before resending."
              }
            },
            "required": [
              "code",
              "ambiguous"
            ]
          }
        ]
      },
      "VerificationFailedError": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Error"
          },
          {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "const": "verification_failed"
              },
              "verification": {
                "type": "string",
                "const": "failed"
              },
              "definitive": {
                "type": "boolean",
                "const": true
              },
              "group_jid": {
                "type": "string"
              },
              "retry_as_ambiguous": {
                "type": "boolean",
                "const": false
              }
            },
            "required": [
              "code",
              "verification",
              "definitive",
              "group_jid",
              "retry_as_ambiguous"
            ]
          }
        ]
      },
      "SendResult": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "chat_jid": {
            "type": "string"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "type": "string",
            "const": "server_ack",
            "description": "WhatsApp's servers accepted the message. Not a delivery receipt."
          }
        },
        "required": [
          "id",
          "chat_jid",
          "timestamp",
          "status"
        ]
      },
      "RevokeResult": {
        "type": "object",
        "properties": {
          "message_id": {
            "type": "string",
            "description": "The message deleted."
          },
          "chat_jid": {
            "type": "string"
          },
          "id": {
            "type": "string",
            "description": "The delete's own message id. Absent when `status` is `already_revoked`."
          },
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "description": "WhatsApp's time on the delete. Absent when `status` is `already_revoked`."
          },
          "status": {
            "type": "string",
            "enum": [
              "server_ack",
              "already_revoked"
            ],
            "description": "`server_ack`: the delete was sent and WhatsApp's servers accepted it. `already_revoked`: the log already records this message as deleted; nothing was sent and nothing counted."
          }
        },
        "required": [
          "message_id",
          "chat_jid",
          "status"
        ],
        "additionalProperties": false
      },
      "StatusOK": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "const": "ok"
          }
        },
        "required": [
          "status"
        ]
      },
      "GroupParticipant": {
        "type": "object",
        "properties": {
          "jid": {
            "type": "string"
          },
          "phone_number": {
            "type": "string"
          },
          "lid": {
            "type": "string"
          },
          "phone_resolved": {
            "type": "boolean"
          },
          "is_admin": {
            "type": "boolean"
          },
          "is_super_admin": {
            "type": "boolean"
          }
        },
        "required": [
          "jid"
        ]
      },
      "Group": {
        "type": "object",
        "properties": {
          "jid": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "member_add_mode": {
            "type": "string",
            "enum": [
              "admin_add",
              "all_member_add"
            ]
          },
          "owner_jid": {
            "type": "string"
          },
          "created": {
            "type": "string",
            "format": "date-time"
          },
          "participants": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/GroupParticipant"
            }
          },
          "participant_count": {
            "type": "integer"
          },
          "announce": {
            "type": "boolean"
          },
          "locked": {
            "type": "boolean"
          },
          "is_community": {
            "type": "boolean"
          },
          "linked_parent_jid": {
            "type": "string"
          },
          "is_default_subgroup": {
            "type": "boolean"
          },
          "is_incognito": {
            "type": "boolean"
          },
          "owner_resolved": {
            "type": "boolean"
          }
        },
        "required": [
          "jid"
        ]
      },
      "SubGroup": {
        "type": "object",
        "properties": {
          "jid": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "is_default_subgroup": {
            "type": "boolean"
          },
          "is_incognito": {
            "type": "boolean",
            "description": "Absent on this API: it is filled only by a hydrate read, and the public API relays no query string."
          }
        },
        "required": [
          "jid"
        ]
      },
      "ParticipantResult": {
        "type": "object",
        "properties": {
          "jid": {
            "type": "string",
            "description": "The address as you sent it."
          },
          "status": {
            "type": "string",
            "description": "`ok`, `error_<status>`, or `unknown` when the change may have applied; read the group before retrying."
          },
          "error": {
            "type": "string"
          },
          "lid": {
            "type": "string"
          },
          "matched_by": {
            "type": "string",
            "enum": [
              "a",
              "c",
              "single",
              "unknown"
            ]
          }
        },
        "required": [
          "jid",
          "status"
        ]
      },
      "GroupSettingsResult": {
        "type": "object",
        "properties": {
          "jid": {
            "type": "string"
          },
          "applied": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "observed": {
            "type": "object",
            "properties": {
              "announce": {
                "type": "boolean"
              },
              "locked": {
                "type": "boolean"
              },
              "member_add_mode": {
                "type": "string"
              }
            }
          },
          "mismatched": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "jid"
        ]
      },
      "ContactCheck": {
        "type": "object",
        "properties": {
          "query": {
            "type": "string"
          },
          "registered": {
            "type": "boolean"
          },
          "jid": {
            "type": "string"
          },
          "is_business": {
            "type": "boolean"
          },
          "verified_name": {
            "type": "string"
          }
        },
        "required": [
          "query",
          "registered"
        ]
      },
      "ContactInfo": {
        "type": "object",
        "properties": {
          "jid": {
            "type": "string"
          },
          "push_name": {
            "type": "string"
          },
          "business_name": {
            "type": "string"
          },
          "full_name": {
            "type": "string"
          },
          "first_name": {
            "type": "string"
          },
          "found": {
            "type": "boolean"
          },
          "handset": {
            "type": "string",
            "enum": [
              "saved",
              "pending",
              "unknown",
              "failed",
              "off",
              "removed"
            ],
            "description": "Whether the linked phone holds this contact under the names above. saved: the phone confirmed these names (including when it already held them). pending: the write to the phone is queued. unknown: a write may or may not have reached the phone. failed: the write was not made by this request: no phone number is known for this contact yet or the queue is full (it will not be made), or the channel's phone writes are halted (a write that was queued waits and is made once an operator clears the halt; one that could not be queued is not made, so send it again once the halt is cleared). off: phone writes are not enabled on this channel, so the contact is saved here only (or, after a remove, removed here only). removed: the phone confirmed the contact is not in its contact list (after a remove, or deleted on the phone)."
          }
        },
        "required": [
          "jid",
          "full_name",
          "first_name",
          "found",
          "handset"
        ]
      },
      "LIDMapping": {
        "type": "object",
        "properties": {
          "pn": {
            "type": "string",
            "description": "Phone-number address (user JID). Absent when no phone number is known for the LID."
          },
          "lid": {
            "type": "string",
            "description": "The LID address. Absent when no LID is known for the phone number."
          }
        },
        "description": "One entry of the gateway's LID map. Both keys are omitted when empty, so an unknown mapping is a 200 with the looked-up key only."
      },
      "CustomerWarming": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": "boolean"
          },
          "since": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "state": {
            "type": "string",
            "enum": [
              "off",
              "stopped",
              "paused",
              "waiting",
              "warming"
            ],
            "description": "Where warming stands."
          },
          "reason_code": {
            "type": "string",
            "enum": [
              "off",
              "waiting_for_partner",
              "waiting_for_voice",
              "warming",
              "paused_by_you",
              "paused_disconnected",
              "paused_allowance",
              "waiting_for_capacity",
              "stopped_safety",
              "pool_halted",
              "stopped_operator"
            ],
            "description": "Why warming is in this state."
          },
          "restart_after": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "stage": {
            "type": [
              "integer",
              "null"
            ]
          },
          "stage_count": {
            "type": "integer"
          },
          "clean_days_at_stage": {
            "type": [
              "integer",
              "null"
            ]
          },
          "clean_days_needed": {
            "type": [
              "integer",
              "null"
            ]
          },
          "conversations_today": {
            "type": [
              "integer",
              "null"
            ]
          },
          "target_today": {
            "type": [
              "integer",
              "null"
            ]
          },
          "partners": {
            "type": "integer"
          },
          "voice": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_activity_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "next_window": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "from": {
                "type": "string",
                "format": "date-time"
              },
              "to": {
                "type": "string",
                "format": "date-time"
              }
            },
            "required": [
              "from",
              "to"
            ]
          },
          "stale": {
            "type": "boolean",
            "description": "True when the progress figures could not be read fresh, so they are the last known rather than current."
          },
          "webhook_include_warming": {
            "type": "boolean",
            "description": "Whether this channel's webhook receivers get warming traffic (see PUT warming/webhooks)."
          }
        },
        "required": [
          "enabled",
          "since",
          "state",
          "reason_code",
          "restart_after",
          "stage",
          "stage_count",
          "clean_days_at_stage",
          "clean_days_needed",
          "conversations_today",
          "target_today",
          "partners",
          "voice",
          "last_activity_at",
          "next_window",
          "stale",
          "webhook_include_warming"
        ]
      },
      "Session": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "phone": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "registration_date": {
            "type": "string"
          },
          "unlinked": {
            "type": "boolean",
            "description": "true while this channel has held a number and an unlink released it. Link the same channel again; do not create a new one."
          },
          "state": {
            "type": "string",
            "enum": [
              "NEW",
              "PAIRING",
              "CONNECTED",
              "DISCONNECTED",
              "BLOCKED"
            ]
          },
          "daily_send_cap": {
            "type": [
              "integer",
              "null"
            ],
            "description": "null: no daily send cap applies to this channel. A number here is an operator stop (0) or a warming ceiling."
          },
          "daily_send_cap_source": {
            "type": "string",
            "enum": [
              "operator",
              "config",
              "default",
              "none"
            ]
          },
          "plan": {
            "type": "string",
            "enum": [
              "paid",
              "sandbox",
              "comp",
              "internal"
            ],
            "description": "sandbox: the account's free sandbox channel, which has daily allowances (SandboxAllowanceError). paid: no gateway allowances. comp: a channel covered by the account's channel grant rather than a subscription. internal: a channel owned by the gateway operator's own account."
          },
          "comp_granted": {
            "type": "boolean",
            "description": "true for a comp channel the account's channel grant covers now. Absent otherwise."
          },
          "warmup_protection": {
            "type": "boolean"
          },
          "lid_lookup": {
            "type": "boolean"
          },
          "token_set": {
            "type": "boolean"
          },
          "jid": {
            "type": "string"
          },
          "push_name": {
            "type": "string"
          },
          "last_error": {
            "type": "string",
            "description": "Why the last connection attempt or state change failed, as one of a fixed set of sentences. It never quotes the underlying error."
          },
          "qr_pending": {
            "type": "boolean"
          },
          "connected": {
            "type": "boolean"
          },
          "logged_in": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_message_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_event_at": {
            "type": "string",
            "format": "date-time"
          },
          "connected_observed_at": {
            "type": "string",
            "format": "date-time",
            "description": "When `connected` was observed."
          },
          "connected_stale": {
            "type": "boolean",
            "description": "True when `connected` could not be observed by this read, so it is the last known state rather than a fresh one. Absent when the read is fresh."
          },
          "warming": {
            "description": "The channel's warming state. A channel token or a customer account receives the customer shape (`CustomerWarming`), present only when customer warming is on for the account and the channel is admitted, and it is the `warming` of the `PUT /sessions/{name}/warming` response. The operator receives the enrollment form below.",
            "anyOf": [
              {
                "$ref": "#/components/schemas/CustomerWarming"
              },
              {
                "type": "object",
                "description": "The operator's enrollment form.",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "role": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "since": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date-time"
                  },
                  "disabled_reason": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              }
            ]
          }
        },
        "required": [
          "name",
          "state"
        ],
        "description": "The channel's state. This is every field the public API returns for a session. The fields listed under x-operator-only (the operator's phone lane and the legacy send_cap_exempt row kept for rollback) are operator-only: they are never returned to a channel token or to a customer account, only to the operator.",
        "x-operator-only": [
          "lane",
          "send_cap_exempt"
        ]
      },
      "SafetyIndicator": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "level": {
            "type": "string",
            "enum": [
              "good",
              "watch",
              "bad",
              "too_early"
            ],
            "description": "too_early is neutral in the overall: good plus too_early reads good when at least one reading is measured good; all too_early reads watch ('Not enough data yet'); a reading that could not be measured reads watch."
          },
          "value": {
            "type": "string"
          },
          "reason": {
            "type": "string"
          },
          "basis": {
            "type": "string"
          },
          "measured_at": {
            "type": "string",
            "format": "date-time",
            "description": "Time this reading was computed. A replayed reading keeps its original compute time."
          },
          "evidence_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Time the underlying evidence was measured, or null when there is no recorded measurement."
          },
          "live": {
            "type": "boolean",
            "description": "Whether this reading uses live state rather than a recorded measurement."
          },
          "data": {
            "type": "object",
            "description": "Reading-specific fields; other keys may appear. A computed `lifetime` carries `source`, `days`, `is_lower_bound` and `linked_at`; `stood_down` carries `refused` and the fields below.",
            "properties": {
              "source": {
                "type": "string",
                "enum": [
                  "registration_date",
                  "paired",
                  "earliest message"
                ],
                "description": "`lifetime` only: where the age was measured from."
              },
              "days": {
                "type": "integer",
                "minimum": 0,
                "description": "`lifetime` only: whole days from `linked_at` to `measured_at`, floored. A lower bound when `is_lower_bound` is true."
              },
              "is_lower_bound": {
                "type": "boolean",
                "description": "`lifetime` only: true when the age comes from the earliest message, so the real age is at least this."
              },
              "linked_at": {
                "type": "string",
                "format": "date-time",
                "description": "`lifetime` only: the instant the age is measured from. A `registration_date` source is a calendar date encoded as UTC midnight."
              },
              "refused": {
                "type": "boolean",
                "description": "`stood_down` only: the policy-only refusal the warmer reads, independent of `level`."
              },
              "quarantined": {
                "type": "boolean",
                "description": "`stood_down` only: the channel is quarantined."
              },
              "stood_down": {
                "type": "boolean",
                "description": "`stood_down` only: an unlink record exists."
              },
              "blocked": {
                "type": "boolean",
                "description": "`stood_down` only: the identity guard is blocking the channel."
              },
              "refusal_kind": {
                "type": "string",
                "description": "`stood_down` only: why a send would be refused, empty when none."
              },
              "cap_source": {
                "type": "string",
                "description": "`stood_down` only: where the daily ceiling comes from (`none` when no ceiling applies)."
              },
              "since": {
                "type": "string",
                "format": "date-time",
                "description": "`stood_down` only: when the unlink was recorded."
              },
              "restricted_at": {
                "type": "string",
                "format": "date-time",
                "description": "`stood_down` only: present for a severe WhatsApp logout."
              },
              "unlink": {
                "type": "object",
                "description": "`stood_down` only: the retained unlink record.",
                "properties": {
                  "at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "who": {
                    "type": "string"
                  }
                }
              },
              "unreadable": {
                "type": "boolean",
                "description": "Present and true when the reading could not be taken."
              }
            }
          }
        },
        "required": [
          "key",
          "level",
          "evidence_at",
          "live"
        ]
      },
      "SafetyOverall": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "level": {
            "type": "string",
            "enum": [
              "good",
              "watch",
              "bad"
            ],
            "description": "Always exactly good, watch or bad, never none or too_early (changed 2026-10-20). Bad if any reading is bad; else watch if any reading is watch or could not be measured, or the report is waiting or stale; else good. too_early is neutral: good plus too_early reads good when at least one reading is measured good; all too_early reads watch ('Not enough data yet'); a reading that could not be measured reads watch."
          },
          "value": {
            "type": "string"
          },
          "reason": {
            "type": "string"
          },
          "basis": {
            "type": "string"
          },
          "measured_at": {
            "type": "string",
            "format": "date-time",
            "description": "Time this reading was computed. A replayed reading keeps its original compute time."
          },
          "evidence_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Time the underlying evidence was measured, or null when there is no recorded measurement."
          },
          "live": {
            "type": "boolean",
            "description": "Whether this reading uses live state rather than a recorded measurement."
          },
          "data": {
            "type": "object",
            "description": "Reading-specific fields; other keys may appear. A computed `lifetime` carries `source`, `days`, `is_lower_bound` and `linked_at`; `stood_down` carries `refused` and the fields below.",
            "properties": {
              "source": {
                "type": "string",
                "enum": [
                  "registration_date",
                  "paired",
                  "earliest message"
                ],
                "description": "`lifetime` only: where the age was measured from."
              },
              "days": {
                "type": "integer",
                "minimum": 0,
                "description": "`lifetime` only: whole days from `linked_at` to `measured_at`, floored. A lower bound when `is_lower_bound` is true."
              },
              "is_lower_bound": {
                "type": "boolean",
                "description": "`lifetime` only: true when the age comes from the earliest message, so the real age is at least this."
              },
              "linked_at": {
                "type": "string",
                "format": "date-time",
                "description": "`lifetime` only: the instant the age is measured from. A `registration_date` source is a calendar date encoded as UTC midnight."
              },
              "refused": {
                "type": "boolean",
                "description": "`stood_down` only: the policy-only refusal the warmer reads, independent of `level`."
              },
              "quarantined": {
                "type": "boolean",
                "description": "`stood_down` only: the channel is quarantined."
              },
              "stood_down": {
                "type": "boolean",
                "description": "`stood_down` only: an unlink record exists."
              },
              "blocked": {
                "type": "boolean",
                "description": "`stood_down` only: the identity guard is blocking the channel."
              },
              "refusal_kind": {
                "type": "string",
                "description": "`stood_down` only: why a send would be refused, empty when none."
              },
              "cap_source": {
                "type": "string",
                "description": "`stood_down` only: where the daily ceiling comes from (`none` when no ceiling applies)."
              },
              "since": {
                "type": "string",
                "format": "date-time",
                "description": "`stood_down` only: when the unlink was recorded."
              },
              "restricted_at": {
                "type": "string",
                "format": "date-time",
                "description": "`stood_down` only: present for a severe WhatsApp logout."
              },
              "unlink": {
                "type": "object",
                "description": "`stood_down` only: the retained unlink record.",
                "properties": {
                  "at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "who": {
                    "type": "string"
                  }
                }
              },
              "unreadable": {
                "type": "boolean",
                "description": "Present and true when the reading could not be taken."
              }
            }
          }
        },
        "required": [
          "key",
          "level",
          "evidence_at",
          "live"
        ]
      },
      "SafetyReport": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "current",
              "waiting",
              "stale"
            ]
          },
          "session": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "measured_at": {
            "type": "string",
            "format": "date-time"
          },
          "overall": {
            "$ref": "#/components/schemas/SafetyOverall",
            "description": "Changed 2026-10-20: overall.level is always good, watch or bad and no longer returns none. A waiting or stale report reads watch (or bad when a measured reading is bad)."
          },
          "indicators": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SafetyIndicator"
            }
          }
        },
        "required": [
          "status",
          "overall",
          "indicators"
        ]
      },
      "Chat": {
        "type": "object",
        "properties": {
          "chat_jid": {
            "type": "string"
          },
          "last_at": {
            "type": "string",
            "format": "date-time"
          },
          "message_count": {
            "type": "integer"
          },
          "unread": {
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "required": [
          "chat_jid"
        ]
      },
      "Message": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "session": {
            "type": "string"
          },
          "chat_jid": {
            "type": "string"
          },
          "sender_jid": {
            "type": "string"
          },
          "direction": {
            "type": "string",
            "enum": [
              "in",
              "out"
            ]
          },
          "type": {
            "type": "string",
            "description": "The kind of message. Values include text, image, video, document, audio, sticker, contact, contact_list, location, live_location, poll, poll_vote, reaction, protocol, short (a video note), group_invite and unknown (a message that cannot be described). New values can be added, so handle a value you do not recognize the way you handle unknown."
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "from_me": {
            "type": "boolean"
          },
          "text": {
            "type": "string"
          },
          "media_id": {
            "type": "string"
          },
          "push_name": {
            "type": "string"
          },
          "ambiguous": {
            "type": "boolean"
          },
          "mentions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "quoted_id": {
            "type": "string"
          },
          "quoted_author": {
            "type": "string"
          },
          "chat_lid": {
            "type": "string"
          },
          "sender_lid": {
            "type": "string"
          },
          "poll": {
            "type": "object",
            "description": "A poll's structure (type `poll`), or a vote's (type `poll_vote`).",
            "properties": {
              "options": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "type `poll`: the options, in order."
              },
              "multiple": {
                "type": "boolean",
                "description": "type `poll`: more than one option may be chosen."
              },
              "poll_id": {
                "type": "string",
                "description": "type `poll_vote`: the poll voted on."
              },
              "selected": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "type `poll_vote`: the chosen options, by name. A readable vote with no `selected` and no `unresolved` is an un-vote: the voter withdrew their choice."
              },
              "unresolved": {
                "type": "integer",
                "description": "type `poll_vote`: chosen options that could not be named (the poll is not in this channel's log)."
              },
              "unreadable": {
                "type": "boolean",
                "description": "type `poll_vote`: the vote could not be decrypted; `selected` means nothing."
              }
            },
            "additionalProperties": false
          },
          "revoked_at": {
            "type": "string",
            "format": "date-time",
            "description": "When this message was deleted for everyone (by this number or by its sender). Absent if it was not. The log keeps the message's content."
          },
          "sender_pn_resolved": {
            "type": "boolean",
            "description": "Present, and false, only when the sender was stored under a LID whose phone number is not known; `sender_jid` then holds the LID."
          },
          "quoted_author_lid": {
            "type": "string",
            "description": "The LID the quoted message's author was named by, when it was; `quoted_author` is then their phone JID where known."
          },
          "source": {
            "type": "string",
            "enum": [
              "live",
              "history_sync"
            ],
            "description": "Conversation reads: `live` for a message captured while the channel was connected, `history_sync` for one the paired phone sent as history (stored because `store_history_sync` is on)."
          },
          "source_chat": {
            "type": "string",
            "description": "Conversation reads: the chat address this message is filed under in the log (a person's phone JID or LID, or the group)."
          },
          "reaction": {
            "type": "object",
            "description": "type `reaction`: the message reacted to. `text` is the emoji; `removed` marks a removal. Absent on reactions logged before 2026-10-05.",
            "properties": {
              "message_id": {
                "type": "string"
              },
              "removed": {
                "type": "boolean"
              }
            },
            "required": [
              "message_id"
            ],
            "additionalProperties": false
          },
          "revoke": {
            "type": "object",
            "description": "A delete for everyone (type `revoke`, sent by this number; type `protocol`, observed): the message it names. Whether the delete took effect is that message's `revoked_at`. Absent on deletes logged before 2026-10-05.",
            "properties": {
              "message_id": {
                "type": "string"
              }
            },
            "required": [
              "message_id"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "id",
          "chat_jid",
          "sender_jid",
          "direction",
          "type",
          "timestamp",
          "from_me"
        ]
      },
      "WebhookReceiver": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Event types to receive. Omit for every type."
          },
          "secret": {
            "type": "string",
            "anyOf": [
              {
                "maxLength": 0
              },
              {
                "minLength": 24
              }
            ],
            "writeOnly": true,
            "description": "Signs this receiver's deliveries. Required for a new receiver. On a receiver you already saved, omit it (or send an empty string) to keep the stored secret; send a new value of at least 24 characters to replace it. A secret cannot be removed: delete the receiver and add it again with a new one."
          }
        },
        "required": [
          "url"
        ],
        "additionalProperties": false
      },
      "WebhookReceiverView": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sessions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "media_policy": {
            "type": "string",
            "enum": [
              "reference",
              "metadata"
            ]
          },
          "secret_set": {
            "type": "boolean"
          }
        },
        "required": [
          "url",
          "secret_set"
        ]
      }
    }
  }
}