{
  "openapi": "3.1.0",
  "info": {
    "title": "SMeet Bot API",
    "version": "1.0.0",
    "summary": "HTTP API for programs that act as SMeet bots.",
    "description": "The SMeet Bot API lets a program running on the bot author's own server receive\nupdates (messages, button presses, access changes) and reply in the chats a user\nhas explicitly started with the bot.\n\nThis is SMeet's own API. It is not compatible with the Telegram Bot API and no\nTelegram client library is expected to work with it.\n\n## Conventions\n\n* Every method is `POST https://messenger.scrile.com/bot-api/v1/<method>` with a JSON\n  body (`uploadFile` takes `multipart/form-data`). Read-only methods also accept `GET`\n  with query parameters.\n* Authentication: `Authorization: Bearer <BOT_TOKEN>`. The token is never passed in\n  the URL. A token belongs to exactly one bot, and a bot belongs to exactly one space\n  (the public space or one organisation); the token cannot reach any other space.\n* Every response is JSON. Success: `{\"ok\": true, \"result\": ...}`. Failure:\n  `{\"ok\": false, \"error\": {\"code\": \"...\", \"message\": \"...\", \"retry_after\": 5}, \"request_id\": \"...\"}`.\n  `retry_after` (seconds) is present only when retrying makes sense.\n* All identifiers, including `update_id`, are decimal strings. Do not convert them to\n  floating point numbers: they can exceed 2^53.\n* Unknown JSON fields may appear in any object at any time. Unknown update types must be\n  ignored, not treated as errors.\n* Mutating methods take an `Idempotency-Key` header. Repeating a request with the same key\n  and the same parameters returns the original result instead of performing the action\n  twice; the same key with different parameters fails with `IDEMPOTENCY_CONFLICT`. Keys are\n  kept for 7 days.\n\n## Receiving updates\n\nA bot uses exactly one delivery mode at a time, chosen by its owner in SMeet BotFather /\n\"My bots\": long polling (`getUpdates`, the default) or webhook (SMeet POSTs each update\nto the owner's HTTPS endpoint). A bot program cannot change the mode or the webhook URL\nwith its token.\n\n`update_id` is the delivery position, `event_id` is the identity of the business event.\nA network retry keeps both. A manual replay of a FAILED update gets a new `update_id`,\nkeeps the `event_id` and sets `replay_of_update_id`. Deduplicate business actions by\n`event_id`.\n",
    "contact": {
      "name": "SMeet",
      "url": "https://messenger.scrile.com/docs/bots/en/"
    }
  },
  "servers": [
    {
      "url": "https://messenger.scrile.com/bot-api/v1",
      "description": "Production"
    }
  ],
  "security": [
    {
      "botToken": []
    }
  ],
  "tags": [
    {
      "name": "Bot",
      "description": "The bot's own profile and commands."
    },
    {
      "name": "Updates",
      "description": "Receiving updates with long polling."
    },
    {
      "name": "Messages",
      "description": "Sending and editing messages, answering button presses."
    },
    {
      "name": "Files",
      "description": "Photos and documents."
    },
    {
      "name": "Delivery",
      "description": "Read-only delivery diagnostics."
    }
  ],
  "paths": {
    "/getMe": {
      "get": {
        "operationId": "getMe",
        "tags": [
          "Bot"
        ],
        "summary": "Check the token and return the bot's public profile.",
        "responses": {
          "200": {
            "description": "The bot.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetMeResponse"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "post": {
        "operationId": "getMePost",
        "tags": [
          "Bot"
        ],
        "summary": "Same as GET /getMe.",
        "responses": {
          "200": {
            "description": "The bot.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetMeResponse"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/getUpdates": {
      "post": {
        "operationId": "getUpdates",
        "tags": [
          "Updates"
        ],
        "summary": "Receive pending updates with long polling.",
        "description": "Returns pending updates starting at the confirmed position. Passing `offset` confirms\nevery update with `update_id < offset`; confirmed updates are never returned again.\nUnconfirmed updates are returned again on the next call (after a lost response or a\nrestart), so the program must tolerate repeats.\n\nThe request waits up to `timeout` seconds and returns as soon as an update arrives, not\nat the end of the timeout. An empty result after a timeout changes nothing.\n\nOne active consumer per bot. The first call names a `consumer_id` and receives a lease\nwith an `epoch`; later calls pass the same `consumer_id` and `epoch` and extend the\nlease (TTL 60 seconds). A second concurrent call, even with the same `consumer_id`, is\nrejected with `CONSUMER_CONFLICT`. A call carrying a foreign epoch (another consumer, the\nowner's lease reset or a mode switch took the lease since) gets a new lease but confirms\nnothing: its `offset` is ignored and unconfirmed updates come again. If your own lease\nexpired and nobody took it in between, your next call gets a new lease and its `offset`\nstill confirms.\n\nOffset rules (`confirmed_offset` = position after the confirmed prefix,\n`max_issued_update_id` = highest update_id ever returned):\n* `offset = confirmed_offset` is always accepted (a no-op confirmation).\n* `confirmed_offset < offset <= max_issued_update_id + 1` confirms the prefix.\n* `offset < confirmed_offset` fails with `409 CURSOR_BEHIND`; the error carries the\n  current `confirmed_offset`. Nothing is re-sent.\n* `offset > max_issued_update_id + 1` fails with `400 OFFSET_NOT_ISSUED` unless it\n  equals the `next_offset` the server returned for the current lease.\n* Omitting `offset` continues from the position stored by the server.\n\nExample: after update 42 was returned, `offset=43` confirms it; `offset=44` is\nrejected; once 43 is confirmed, `offset=42` gets `CURSOR_BEHIND`.\n\nUpdates that were cancelled, expired or moved to FAILED before being confirmed are\nreported in `skipped`; `next_offset` then jumps over them. Always confirm with the\nreturned `next_offset` after durably storing the whole batch.\n",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GetUpdatesRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A batch of updates (possibly empty).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetUpdatesResponse"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/rejectUpdate": {
      "post": {
        "operationId": "rejectUpdate",
        "tags": [
          "Updates"
        ],
        "summary": "Move one issued, unconfirmed update to FAILED.",
        "description": "Use when the program cannot process one specific update and does not want it to block\nthe rest of the batch. The update leaves the active sequence, keeps its payload for the\noriginal retention window, and appears in the owner's FAILED list where it can be\nreplayed or skipped. Only updates already returned to the current lease and not yet\nconfirmed can be rejected.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKeyRequired"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RejectUpdateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The update is FAILED.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RejectUpdateResponse"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/sendMessage": {
      "post": {
        "operationId": "sendMessage",
        "tags": [
          "Messages"
        ],
        "summary": "Send a text message to a chat the user has started with this bot.",
        "description": "The sender is always the bot identified by the token; there is no `sender_id`\nparameter. The chat must be a private chat in which the user pressed Start and has\nnot stopped or blocked the bot.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKeyRequired"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendMessageRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The sent message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/editMessageText": {
      "post": {
        "operationId": "editMessageText",
        "tags": [
          "Messages"
        ],
        "summary": "Edit the text and buttons of a message this bot sent.",
        "description": "Only messages sent by this bot can be edited. If `reply_markup` is omitted, the message\nhas no buttons after the edit.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKeyRequired"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EditMessageTextRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The edited message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/answerCallbackQuery": {
      "post": {
        "operationId": "answerCallbackQuery",
        "tags": [
          "Messages"
        ],
        "summary": "Confirm that a button press was handled.",
        "description": "Stops the user's pending indicator and optionally shows a short notice. A press that is\nnot answered within 15 seconds shows \"The bot did not respond\" to the user; the bot can\nstill answer later and edit its message. A callback query can be answered once, within\n1 hour; a repeated identical answer returns the same result, and a different second answer\nfails with 409 IDEMPOTENCY_CONFLICT.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKeyOptional"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AnswerCallbackQueryRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Answered.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TrueResponse"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/getMyCommands": {
      "get": {
        "operationId": "getMyCommands",
        "tags": [
          "Bot"
        ],
        "summary": "Read the command menu.",
        "parameters": [
          {
            "name": "language_code",
            "in": "query",
            "required": false,
            "schema": {
              "$ref": "#/components/schemas/LanguageCode"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The commands.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommandsResponse"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "post": {
        "operationId": "getMyCommandsPost",
        "tags": [
          "Bot"
        ],
        "summary": "Same as GET /getMyCommands.",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "language_code": {
                    "$ref": "#/components/schemas/LanguageCode"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The commands.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommandsResponse"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/setMyCommands": {
      "post": {
        "operationId": "setMyCommands",
        "tags": [
          "Bot"
        ],
        "summary": "Replace the command menu shown when the user types \"/\".",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKeyOptional"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetMyCommandsRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Saved.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TrueResponse"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/getDeliveryInfo": {
      "get": {
        "operationId": "getDeliveryInfo",
        "tags": [
          "Delivery"
        ],
        "summary": "Read the delivery mode, queue sizes and the last error.",
        "description": "Never returns the webhook secret.",
        "responses": {
          "200": {
            "description": "Delivery state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryInfoResponse"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "post": {
        "operationId": "getDeliveryInfoPost",
        "tags": [
          "Delivery"
        ],
        "summary": "Same as GET /getDeliveryInfo.",
        "responses": {
          "200": {
            "description": "Delivery state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryInfoResponse"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/getWebhookInfo": {
      "get": {
        "operationId": "getWebhookInfo",
        "tags": [
          "Delivery"
        ],
        "summary": "Read the webhook state (a subset of getDeliveryInfo).",
        "responses": {
          "200": {
            "description": "Webhook state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookInfoResponse"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "post": {
        "operationId": "getWebhookInfoPost",
        "tags": [
          "Delivery"
        ],
        "summary": "Same as GET /getWebhookInfo.",
        "responses": {
          "200": {
            "description": "Webhook state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookInfoResponse"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/uploadFile": {
      "post": {
        "operationId": "uploadFile",
        "tags": [
          "Files"
        ],
        "summary": "Upload a photo or document for later sending.",
        "description": "The file is stored in quarantine and scanned. The returned `file_id` has status\n`scanning`; it can be sent only after `getFile` reports `ready`. Limits: photos up to\n10 MiB (JPEG, PNG, WebP, HEIC), documents up to 20 MiB, plus a daily volume quota per\nbot. The same `Idempotency-Key` never creates a second file.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKeyRequired"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/UploadFileRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The stored file (usually `scanning`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileResponse"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/sendPhoto": {
      "post": {
        "operationId": "sendPhoto",
        "tags": [
          "Files"
        ],
        "summary": "Send a ready photo file to a chat.",
        "description": "The file must have kind `photo` (a JPEG, PNG, WebP or HEIC by its content). Any other ready\nfile fails with 400 INVALID_REQUEST, `field: file_id`; send it with sendDocument instead.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKeyRequired"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendFileRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The sent message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/sendDocument": {
      "post": {
        "operationId": "sendDocument",
        "tags": [
          "Files"
        ],
        "summary": "Send a ready document file to a chat.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKeyRequired"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendFileRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The sent message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/getFile": {
      "get": {
        "operationId": "getFile",
        "tags": [
          "Files"
        ],
        "summary": "Read a file's metadata and processing status.",
        "description": "Works for files the bot uploaded and for attachments of messages it received. The\n`download_path` is present only while the status is `ready` and access is still\nallowed. For `scanning`, the response carries `retry_after`.\n",
        "parameters": [
          {
            "name": "file_id",
            "in": "query",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/FileId"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The file.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileResponse"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "post": {
        "operationId": "getFilePost",
        "tags": [
          "Files"
        ],
        "summary": "Same as GET /getFile.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "file_id"
                ],
                "properties": {
                  "file_id": {
                    "$ref": "#/components/schemas/FileId"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The file.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileResponse"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/files/{file_id}/content": {
      "get": {
        "operationId": "downloadFile",
        "tags": [
          "Files"
        ],
        "summary": "Download the bytes of a ready file.",
        "description": "Requires the bot token and current access rights on every call. There is no public or\npermanent storage URL. Access ends when the source message is deleted, the user stops\nor blocks the bot, or the user leaves the space.\n",
        "parameters": [
          {
            "name": "file_id",
            "in": "path",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/FileId"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The file bytes.",
            "headers": {
              "Content-Disposition": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    }
  },
  "webhooks": {
    "update": {
      "post": {
        "operationId": "webhookUpdate",
        "summary": "SMeet delivers one update to the owner's HTTPS endpoint.",
        "description": "Sent only when the bot's delivery mode is `webhook`. One request in flight per bot;\nnetwork timeout 10 seconds; redirects are not followed.\n\nVerify `X-SMeet-Signature` before trusting the body: `t` is a Unix timestamp and\n`v1` is the lowercase hex HMAC-SHA256 of `\"<t>.<raw request body>\"` keyed with the\nwebhook secret shown once in \"My bots\". Reject timestamps more than 5 minutes away\nfrom your clock. The API token is never sent to the webhook.\n\nStore the update durably (unique by `event_id`) and answer any 2xx quickly; run long\nbusiness logic afterwards. A 2xx confirms receipt, not business success. Lost responses\ncause repeats with the same `update_id` and `event_id`.\n\nResponse handling: 2xx confirms. 400, 413 or 422 marks this update FAILED and moves on.\n401, 403, 404, 410, a certificate that does not validate, an address webhooks may not\nreach, a redirect and any other unsupported answer pause the endpoint until the owner\nfixes it and resumes delivery in \"My bots\" (state `paused`, the cause in `last_error`);\nnothing is retried meanwhile, no update is charged and the queue is kept. 429 slows down\nand honours `Retry-After`. DNS and connection errors, and connect or TLS handshake\ntimeouts before the request is sent, keep the update pending and back off (5 seconds,\ndoubling up to 5 minutes). 5xx, 408 and failures after the request started share a\nbudget of 5 attempts or 10 minutes per update, after which it becomes FAILED; three such\nupdates in a row open a circuit breaker that probes with the current update before\ncontinuing.\n",
        "parameters": [
          {
            "name": "X-SMeet-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "t=1790416800,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
            }
          },
          {
            "name": "X-SMeet-Event-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "X-SMeet-Update-Id",
            "in": "header",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/Id"
            }
          },
          {
            "name": "X-SMeet-Delivery-Attempt",
            "in": "header",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Update"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Accepted. Any 2xx is treated the same way; the body is ignored."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "botToken": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "sbt1_<key id>_<secret>",
        "description": "The bot token shown once in \"My bots\" / SMeet BotFather. Format\n`sbt1_<key id>_<secret>`; treat it as opaque. SMeet stores only a hash. A revoked\ntoken fails with `401 INVALID_TOKEN` on every node immediately.\n"
      }
    },
    "parameters": {
      "IdempotencyKeyRequired": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "description": "Unique per logical operation (a UUID is fine). Reuse the same key when retrying after a\ntimeout, 429 or 503. Bind it to the `event_id` you are answering and the action, so a\nreplayed update does not produce a second reply.\n",
        "schema": {
          "$ref": "#/components/schemas/IdempotencyKey"
        }
      },
      "IdempotencyKeyOptional": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "Accepted and not needed: the method is idempotent by itself (a repeated identical call gives\nthe same result), so the key is not stored.\n",
        "schema": {
          "$ref": "#/components/schemas/IdempotencyKey"
        }
      }
    },
    "responses": {
      "Error": {
        "description": "Any failure. See `ErrorCode` for the meaning of each code and the HTTP status it comes\nwith. The `Retry-After` header is set whenever the body carries `retry_after` (429, 503, and\n409 CONSUMER_CONFLICT and FILE_NOT_READY).\n",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer"
            }
          },
          "X-Request-Id": {
            "schema": {
              "type": "string"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      }
    },
    "schemas": {
      "Id": {
        "type": "string",
        "pattern": "^[0-9]{1,20}$",
        "description": "A decimal identifier passed as a string. Not a secret, not a permission.",
        "example": "1842"
      },
      "FileId": {
        "type": "string",
        "pattern": "^[A-Za-z0-9_-]{16,64}$",
        "description": "An opaque file identifier valid only for this bot.",
        "example": "f_Qm9vdGZpbGVfMDAwMDAx"
      },
      "IdempotencyKey": {
        "type": "string",
        "minLength": 1,
        "maxLength": 128,
        "pattern": "^[A-Za-z0-9._:-]+$"
      },
      "LanguageCode": {
        "type": "string",
        "pattern": "^[a-z]{2}$",
        "example": "en"
      },
      "Timestamp": {
        "type": "string",
        "format": "date-time",
        "description": "RFC 3339, UTC.",
        "example": "2026-09-26T10:00:00Z"
      },
      "UpdateType": {
        "type": "string",
        "enum": [
          "message",
          "message_edited",
          "message_deleted",
          "callback_query",
          "chat_access_changed",
          "file_status_changed"
        ],
        "description": "Consumers must ignore unknown values."
      },
      "FileStatus": {
        "type": "string",
        "enum": [
          "scanning",
          "ready",
          "rejected",
          "scan_failed"
        ]
      },
      "ErrorCode": {
        "type": "string",
        "description": "| Code | HTTP | Meaning |\n|---|---|---|\n| INVALID_REQUEST | 400 | Bad text, buttons or parameters |\n| IDEMPOTENCY_KEY_REQUIRED | 400 | The method needs an Idempotency-Key header |\n| OFFSET_NOT_ISSUED | 400 | offset jumps over updates that were never returned |\n| INVALID_TOKEN | 401 | Unknown or revoked token |\n| BOT_STOPPED_OR_BLOCKED | 403 | The user stopped or blocked the bot |\n| BOT_SUSPENDED | 403 | Paused by the owner or suspended by the platform |\n| SPACE_BOTS_DISABLED | 403 | The organisation turned bot runtime off |\n| CHAT_NOT_FOUND | 404 | No such chat for this bot (foreign chats are not listed) |\n| MESSAGE_NOT_FOUND | 404 | No such message for this bot |\n| UPDATE_NOT_FOUND | 404 | The update is not issued, already confirmed or not this bot's |\n| CALLBACK_QUERY_NOT_FOUND | 404 | Unknown or expired callback query |\n| BOT_NOT_FOUND | 404 | The bot was deleted while the request was on its way; its token no longer works |\n| FILE_NOT_FOUND | 404 | Unknown file, or access to it has ended |\n| METHOD_NOT_FOUND | 404 | No such method |\n| DELIVERY_MODE_CONFLICT | 409 | getUpdates while the bot is in webhook mode |\n| CONSUMER_CONFLICT | 409 | Another poll holds the lease, or the epoch is stale; `retry_after` says when to ask again |\n| CURSOR_BEHIND | 409 | offset is below the confirmed position |\n| IDEMPOTENCY_CONFLICT | 409 | The key was used with different parameters |\n| FILE_NOT_READY | 409 | The file is still being scanned |\n| PAYLOAD_TOO_LARGE | 413 | The upload exceeds the size limit |\n| FILE_REJECTED | 422 | The file failed the check or is not allowed; `error.reason` says why (`not_a_photo`, `type_not_allowed`, `malware_detected`, ...) |\n| RATE_LIMITED | 429 | Too many requests; wait retry_after |\n| QUOTA_EXCEEDED | 429 | Daily file quota used up; wait retry_after |\n| TEMPORARILY_UNAVAILABLE | 503 | Transient failure; retry with the same key |\n| BOTS_MAINTENANCE | 503 | Technical pause of the bot platform; retry later with the same key and offset |\n",
        "enum": [
          "INVALID_REQUEST",
          "IDEMPOTENCY_KEY_REQUIRED",
          "OFFSET_NOT_ISSUED",
          "INVALID_TOKEN",
          "BOT_STOPPED_OR_BLOCKED",
          "BOT_SUSPENDED",
          "SPACE_BOTS_DISABLED",
          "CHAT_NOT_FOUND",
          "MESSAGE_NOT_FOUND",
          "UPDATE_NOT_FOUND",
          "CALLBACK_QUERY_NOT_FOUND",
          "BOT_NOT_FOUND",
          "FILE_NOT_FOUND",
          "METHOD_NOT_FOUND",
          "DELIVERY_MODE_CONFLICT",
          "CONSUMER_CONFLICT",
          "CURSOR_BEHIND",
          "IDEMPOTENCY_CONFLICT",
          "FILE_NOT_READY",
          "PAYLOAD_TOO_LARGE",
          "FILE_REJECTED",
          "RATE_LIMITED",
          "QUOTA_EXCEEDED",
          "TEMPORARILY_UNAVAILABLE",
          "BOTS_MAINTENANCE"
        ]
      },
      "Error": {
        "type": "object",
        "required": [
          "code",
          "message"
        ],
        "properties": {
          "code": {
            "$ref": "#/components/schemas/ErrorCode"
          },
          "message": {
            "type": "string",
            "description": "Human-readable, English, free of secrets. Do not parse it."
          },
          "retry_after": {
            "type": "integer",
            "minimum": 1,
            "description": "Seconds to wait before retrying. Present only when a retry makes sense."
          },
          "confirmed_offset": {
            "$ref": "#/components/schemas/Id",
            "description": "Present with CURSOR_BEHIND and OFFSET_NOT_ISSUED."
          },
          "max_issued_update_id": {
            "$ref": "#/components/schemas/Id",
            "description": "Present with OFFSET_NOT_ISSUED."
          },
          "field": {
            "type": "string",
            "description": "Present with INVALID_REQUEST when one parameter is at fault."
          },
          "reason": {
            "type": "string",
            "description": "Present with FILE_REJECTED: why the file cannot be used (`not_a_photo`, `type_not_allowed`,\n`malware_detected`, `too_large`, `empty`, `scanner_unavailable`, ...). Consumers must accept\nunknown values.\n"
          },
          "limit": {
            "type": "integer",
            "description": "Present with PAYLOAD_TOO_LARGE: the largest accepted size in bytes. A 413 answered by the\nproxy in front of the API, before the request reaches SMeet, may come without it.\n"
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "required": [
          "ok",
          "error",
          "request_id"
        ],
        "properties": {
          "ok": {
            "const": false
          },
          "error": {
            "$ref": "#/components/schemas/Error"
          },
          "request_id": {
            "type": "string"
          }
        }
      },
      "User": {
        "type": "object",
        "required": [
          "id",
          "display_name",
          "is_bot"
        ],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/Id"
          },
          "display_name": {
            "type": "string"
          },
          "username": {
            "type": "string",
            "description": "Public address without \"@\", when the user has one."
          },
          "is_bot": {
            "type": "boolean"
          }
        },
        "description": "Only what the chat needs. E-mail, phone, other chats, other accounts and sessions are\nnever included.\n"
      },
      "Chat": {
        "type": "object",
        "required": [
          "id",
          "type"
        ],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/Id"
          },
          "type": {
            "type": "string",
            "enum": [
              "private"
            ],
            "description": "Only private chats in version 1. Consumers must ignore unknown values."
          }
        }
      },
      "Space": {
        "type": "object",
        "required": [
          "id",
          "kind"
        ],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/Id"
          },
          "kind": {
            "type": "string",
            "enum": [
              "public",
              "organization"
            ]
          },
          "name": {
            "type": "string"
          }
        }
      },
      "Bot": {
        "type": "object",
        "required": [
          "id",
          "username",
          "display_name",
          "is_bot",
          "space",
          "status",
          "delivery_mode"
        ],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/Id",
            "description": "The bot's account id, the same value as `from.id` of the messages the bot sends."
          },
          "username": {
            "type": "string",
            "example": "support_helper_bot"
          },
          "display_name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "is_bot": {
            "const": true
          },
          "space": {
            "$ref": "#/components/schemas/Space"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "paused_by_owner",
              "suspended_by_admin"
            ]
          },
          "delivery_mode": {
            "type": "string",
            "enum": [
              "polling",
              "webhook"
            ]
          },
          "can_send_files": {
            "type": "boolean",
            "description": "False while file support is turned off for this installation."
          }
        }
      },
      "InlineKeyboardButton": {
        "type": "object",
        "required": [
          "text"
        ],
        "properties": {
          "text": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64
          },
          "callback_data": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "description": "Returned in callback_query.data when pressed. Exactly one of callback_data or url."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "An https:// link opened by the client. Exactly one of callback_data or url."
          }
        }
      },
      "InlineKeyboardMarkup": {
        "type": "object",
        "required": [
          "inline_keyboard"
        ],
        "properties": {
          "inline_keyboard": {
            "type": "array",
            "minItems": 1,
            "maxItems": 10,
            "description": "Rows of buttons; at most 8 buttons per row and 40 in total.",
            "items": {
              "type": "array",
              "minItems": 1,
              "maxItems": 8,
              "items": {
                "$ref": "#/components/schemas/InlineKeyboardButton"
              }
            }
          }
        }
      },
      "Attachment": {
        "type": "object",
        "required": [
          "file_id",
          "kind",
          "file_name",
          "size",
          "processing_status"
        ],
        "properties": {
          "file_id": {
            "$ref": "#/components/schemas/FileId"
          },
          "kind": {
            "type": "string",
            "enum": [
              "photo",
              "document"
            ]
          },
          "file_name": {
            "type": "string",
            "description": "Sanitised name, safe to display and to use as a download name."
          },
          "mime_type": {
            "type": "string",
            "description": "As the sender's app declared it. The type detected from the content is in `getFile` once\nthe check is done: markup, programs and other dangerous types are refused there\n(`type_not_allowed`), and any other mismatch is stored under the detected type (a \"photo\"\nthat is not a picture becomes a document).\n"
          },
          "size": {
            "type": "integer",
            "minimum": 0,
            "description": "As the sender's app declared it; `getFile` has the size of the stored copy."
          },
          "processing_status": {
            "$ref": "#/components/schemas/FileStatus"
          },
          "reason": {
            "type": "string",
            "description": "Only when the attachment is already `rejected` or `scan_failed`, for example `too_large` for an\nattachment above the size a bot may receive. Same values as File.reason.\n"
          }
        }
      },
      "Message": {
        "type": "object",
        "required": [
          "message_id",
          "chat",
          "from",
          "date"
        ],
        "properties": {
          "message_id": {
            "$ref": "#/components/schemas/Id"
          },
          "chat": {
            "$ref": "#/components/schemas/Chat"
          },
          "from": {
            "$ref": "#/components/schemas/User"
          },
          "date": {
            "$ref": "#/components/schemas/Timestamp"
          },
          "edit_date": {
            "$ref": "#/components/schemas/Timestamp"
          },
          "text": {
            "type": "string",
            "maxLength": 4096
          },
          "reply_to_message_id": {
            "$ref": "#/components/schemas/Id"
          },
          "attachments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Attachment"
            }
          },
          "reply_markup": {
            "$ref": "#/components/schemas/InlineKeyboardMarkup"
          },
          "has_unsupported_content": {
            "type": "boolean",
            "description": "True when the message carries content this API version does not expose (voice,\nlocation, poll and the like). `text` then holds the readable fallback, if any.\n"
          }
        }
      },
      "CallbackQuery": {
        "type": "object",
        "required": [
          "id",
          "from",
          "message",
          "data",
          "date"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Pass to answerCallbackQuery."
          },
          "from": {
            "$ref": "#/components/schemas/User"
          },
          "message": {
            "type": "object",
            "required": [
              "message_id",
              "chat"
            ],
            "description": "The bot's message whose button was pressed.",
            "properties": {
              "message_id": {
                "$ref": "#/components/schemas/Id"
              },
              "chat": {
                "$ref": "#/components/schemas/Chat"
              }
            }
          },
          "data": {
            "type": "string"
          },
          "date": {
            "$ref": "#/components/schemas/Timestamp"
          }
        }
      },
      "ChatAccess": {
        "type": "object",
        "required": [
          "chat",
          "user",
          "status",
          "date"
        ],
        "properties": {
          "chat": {
            "$ref": "#/components/schemas/Chat"
          },
          "user": {
            "$ref": "#/components/schemas/User"
          },
          "status": {
            "type": "string",
            "enum": [
              "started",
              "stopped",
              "blocked",
              "removed"
            ],
            "description": "`started`: the user pressed Start (again). `stopped`: the user pressed Stop.\n`blocked`: the user blocked the bot. `removed`: the user left the bot's space, so the\nplatform withdrew the bot's access. After anything but `started`, sending to this chat\nfails with BOT_STOPPED_OR_BLOCKED.\n"
          },
          "start_param": {
            "type": "string",
            "maxLength": 64,
            "pattern": "^[A-Za-z0-9_-]*$",
            "description": "The deep-link parameter of a `?start=` link, for `started` only. Never a command itself, but\nStart also puts a visible message from the user in the chat, `/start` or `/start <param>`,\nwhich arrives as its own `message` update right after this one. A bot that greets on Start\nshould react to one of the two, not both.\n"
          },
          "date": {
            "$ref": "#/components/schemas/Timestamp"
          }
        }
      },
      "DeletedMessage": {
        "type": "object",
        "required": [
          "message_id",
          "chat",
          "date"
        ],
        "properties": {
          "message_id": {
            "$ref": "#/components/schemas/Id"
          },
          "chat": {
            "$ref": "#/components/schemas/Chat"
          },
          "date": {
            "$ref": "#/components/schemas/Timestamp"
          }
        }
      },
      "FileStatusChange": {
        "type": "object",
        "required": [
          "file_id",
          "status"
        ],
        "description": "Sent when a check ends: for attachments the bot received (with `message_id` and `chat`), and for\nthe bot's own uploads (without them). Never sent into a chat the user has stopped or left.\n`scan_failed` is not always final: a file the scanner could not check (`scanner_unavailable`,\n`scanner_outdated`, `scan_timeout`) is checked again about an hour later, up to three rounds,\nand another file_status_changed follows; meanwhile getFile reports `scanning` again.\n",
        "properties": {
          "file_id": {
            "$ref": "#/components/schemas/FileId"
          },
          "status": {
            "$ref": "#/components/schemas/FileStatus"
          },
          "message_id": {
            "$ref": "#/components/schemas/Id",
            "description": "The message the file is attached to, for received attachments."
          },
          "chat": {
            "$ref": "#/components/schemas/Chat"
          },
          "reason": {
            "type": "string",
            "description": "For rejected and scan_failed. Values: `malware_detected`, `type_not_allowed` (markup, programs and\nother refused types), `too_large` (a file above the limit is rejected; one the scanner refuses\nfor its size ends as scan_failed), `empty` (no content), `scanner_unavailable`, `scanner_outdated`\n(signatures older than 3 days), `scan_timeout`, `source_missing` (the attachment disappeared\nbefore the check). Consumers must accept unknown values.\n"
          }
        }
      },
      "Update": {
        "type": "object",
        "required": [
          "update_id",
          "event_id",
          "type",
          "date"
        ],
        "description": "Exactly one payload field is present, chosen by `type`:\n\n| type | payload field |\n|---|---|\n| message | message |\n| message_edited | edited_message |\n| message_deleted | deleted_message |\n| callback_query | callback_query |\n| chat_access_changed | chat_access |\n| file_status_changed | file_status |\n\nEditing an old message is not a new command: treat `message_edited` as a correction.\n",
        "properties": {
          "update_id": {
            "$ref": "#/components/schemas/Id"
          },
          "event_id": {
            "type": "string",
            "format": "uuid"
          },
          "type": {
            "$ref": "#/components/schemas/UpdateType"
          },
          "replay_of_update_id": {
            "$ref": "#/components/schemas/Id",
            "description": "Set when the owner replayed a FAILED update."
          },
          "date": {
            "$ref": "#/components/schemas/Timestamp"
          },
          "message": {
            "$ref": "#/components/schemas/Message"
          },
          "edited_message": {
            "$ref": "#/components/schemas/Message"
          },
          "deleted_message": {
            "$ref": "#/components/schemas/DeletedMessage"
          },
          "callback_query": {
            "$ref": "#/components/schemas/CallbackQuery"
          },
          "chat_access": {
            "$ref": "#/components/schemas/ChatAccess"
          },
          "file_status": {
            "$ref": "#/components/schemas/FileStatusChange"
          }
        }
      },
      "Lease": {
        "type": "object",
        "required": [
          "consumer_id",
          "epoch",
          "expires_at"
        ],
        "properties": {
          "consumer_id": {
            "type": "string"
          },
          "epoch": {
            "$ref": "#/components/schemas/Id"
          },
          "expires_at": {
            "$ref": "#/components/schemas/Timestamp"
          }
        }
      },
      "SkipRange": {
        "type": "object",
        "required": [
          "from_update_id",
          "to_update_id",
          "reason"
        ],
        "properties": {
          "from_update_id": {
            "$ref": "#/components/schemas/Id"
          },
          "to_update_id": {
            "$ref": "#/components/schemas/Id",
            "description": "Inclusive."
          },
          "reason": {
            "type": "string",
            "enum": [
              "cancelled",
              "failed",
              "expired",
              "skipped_by_owner",
              "delivered"
            ],
            "description": "`cancelled`: access to the chat or its source ended before delivery. `failed`: moved to FAILED\n(rejectUpdate or the webhook budget). `expired`: the retention window ran out. `skipped_by_owner`:\nthe owner skipped a FAILED update. `delivered`: already confirmed another way, for example by the\nwebhook before the owner switched the bot back to long polling. Consumers must accept unknown values.\n"
          }
        }
      },
      "GetUpdatesRequest": {
        "type": "object",
        "required": [
          "consumer_id"
        ],
        "properties": {
          "offset": {
            "$ref": "#/components/schemas/Id"
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100,
            "default": 100
          },
          "timeout": {
            "type": "integer",
            "minimum": 0,
            "maximum": 25,
            "default": 25,
            "description": "Seconds to wait when nothing is pending. 0 returns immediately."
          },
          "consumer_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "pattern": "^[A-Za-z0-9._-]+$",
            "description": "A stable name of this receiving process, e.g. its host name."
          },
          "epoch": {
            "$ref": "#/components/schemas/Id",
            "description": "The epoch of the lease received earlier. Keep it with your offset and pass it after a restart\ntoo; omit it only when you have none. Without it, a lease this consumer still holds answers\n409 CONSUMER_CONFLICT until it expires (retry_after tells how long); then call again.\n"
          }
        }
      },
      "GetUpdatesResult": {
        "type": "object",
        "required": [
          "updates",
          "confirmed_offset",
          "next_offset",
          "skipped",
          "lease"
        ],
        "properties": {
          "updates": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Update"
            }
          },
          "confirmed_offset": {
            "$ref": "#/components/schemas/Id",
            "description": "Position after the confirmed prefix, after applying this request's offset."
          },
          "next_offset": {
            "$ref": "#/components/schemas/Id",
            "description": "Pass as `offset` after durably storing every update of this batch."
          },
          "skipped": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SkipRange"
            }
          },
          "lease": {
            "$ref": "#/components/schemas/Lease"
          },
          "queue_gap": {
            "type": "boolean",
            "description": "True when updates expired before delivery since the last confirmation."
          }
        }
      },
      "GetUpdatesResponse": {
        "type": "object",
        "required": [
          "ok",
          "result"
        ],
        "properties": {
          "ok": {
            "const": true
          },
          "result": {
            "$ref": "#/components/schemas/GetUpdatesResult"
          }
        }
      },
      "RejectUpdateRequest": {
        "type": "object",
        "required": [
          "update_id",
          "reason",
          "consumer_id",
          "epoch"
        ],
        "properties": {
          "update_id": {
            "$ref": "#/components/schemas/Id"
          },
          "reason": {
            "type": "string",
            "minLength": 1,
            "maxLength": 256,
            "description": "Shown to the owner. Must not contain secrets or personal data."
          },
          "consumer_id": {
            "type": "string"
          },
          "epoch": {
            "$ref": "#/components/schemas/Id"
          }
        }
      },
      "RejectUpdateResponse": {
        "type": "object",
        "required": [
          "ok",
          "result"
        ],
        "properties": {
          "ok": {
            "const": true
          },
          "result": {
            "type": "object",
            "required": [
              "update_id",
              "status"
            ],
            "properties": {
              "update_id": {
                "$ref": "#/components/schemas/Id"
              },
              "status": {
                "const": "failed"
              }
            }
          }
        }
      },
      "SendMessageRequest": {
        "type": "object",
        "required": [
          "chat_id",
          "text"
        ],
        "properties": {
          "chat_id": {
            "$ref": "#/components/schemas/Id"
          },
          "text": {
            "type": "string",
            "minLength": 1,
            "maxLength": 4096,
            "description": "Plain text. Strings starting with \"__META__:\" or \"__SYSTEM__\" are rejected."
          },
          "reply_to_message_id": {
            "$ref": "#/components/schemas/Id",
            "description": "A message in the same chat."
          },
          "reply_markup": {
            "$ref": "#/components/schemas/InlineKeyboardMarkup"
          }
        }
      },
      "EditMessageTextRequest": {
        "type": "object",
        "required": [
          "chat_id",
          "message_id",
          "text"
        ],
        "properties": {
          "chat_id": {
            "$ref": "#/components/schemas/Id"
          },
          "message_id": {
            "$ref": "#/components/schemas/Id"
          },
          "text": {
            "type": "string",
            "minLength": 1,
            "maxLength": 4096
          },
          "reply_markup": {
            "$ref": "#/components/schemas/InlineKeyboardMarkup"
          }
        }
      },
      "AnswerCallbackQueryRequest": {
        "type": "object",
        "required": [
          "callback_query_id"
        ],
        "properties": {
          "callback_query_id": {
            "type": "string"
          },
          "text": {
            "type": "string",
            "maxLength": 200,
            "description": "A short notice shown to the user."
          },
          "show_alert": {
            "type": "boolean",
            "default": false,
            "description": "Show the notice as a dialog instead of a toast."
          }
        }
      },
      "MessageResponse": {
        "type": "object",
        "required": [
          "ok",
          "result"
        ],
        "properties": {
          "ok": {
            "const": true
          },
          "result": {
            "$ref": "#/components/schemas/Message"
          }
        }
      },
      "TrueResponse": {
        "type": "object",
        "required": [
          "ok",
          "result"
        ],
        "properties": {
          "ok": {
            "const": true
          },
          "result": {
            "const": true
          }
        }
      },
      "BotCommand": {
        "type": "object",
        "required": [
          "command",
          "description"
        ],
        "properties": {
          "command": {
            "type": "string",
            "pattern": "^[a-z0-9_]{1,32}$",
            "description": "Without the leading slash."
          },
          "description": {
            "type": "string",
            "minLength": 1,
            "maxLength": 256
          }
        }
      },
      "SetMyCommandsRequest": {
        "type": "object",
        "required": [
          "commands"
        ],
        "properties": {
          "commands": {
            "type": "array",
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/BotCommand"
            }
          },
          "language_code": {
            "$ref": "#/components/schemas/LanguageCode",
            "description": "Omit for the default list shown to every language."
          }
        }
      },
      "CommandsResponse": {
        "type": "object",
        "required": [
          "ok",
          "result"
        ],
        "properties": {
          "ok": {
            "const": true
          },
          "result": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BotCommand"
            }
          }
        }
      },
      "DeliveryError": {
        "type": "object",
        "required": [
          "date",
          "code"
        ],
        "properties": {
          "date": {
            "$ref": "#/components/schemas/Timestamp"
          },
          "code": {
            "type": "string",
            "description": "Before the request left: DNS_ERROR, CONNECT_ERROR, CONNECT_TIMEOUT, TLS_ERROR. After it left:\nTIMEOUT, NO_RESPONSE, IO_ERROR, HTTP_408, HTTP_5xx (the status itself, for example HTTP_503).\nPausing until the owner resumes: HTTP_401, HTTP_403, HTTP_404, HTTP_410, TLS_CERTIFICATE_ERROR,\nADDRESS_FORBIDDEN, REDIRECT_NOT_FOLLOWED and HTTP_<status> of any other unsupported answer.\nAlso HTTP_400, HTTP_413, HTTP_422 (the update became FAILED), HTTP_429 (slowed down) and\nDISABLED_BY_PLATFORM. Consumers must accept unknown values.\n"
          },
          "message": {
            "type": "string",
            "description": "Sanitised; never contains secrets or message text."
          }
        }
      },
      "DeliveryInfo": {
        "type": "object",
        "required": [
          "mode",
          "config_version",
          "pending_update_count",
          "failed_update_count",
          "maintenance"
        ],
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "polling",
              "webhook"
            ]
          },
          "config_version": {
            "$ref": "#/components/schemas/Id"
          },
          "pending_update_count": {
            "type": "integer"
          },
          "failed_update_count": {
            "type": "integer"
          },
          "oldest_pending_date": {
            "$ref": "#/components/schemas/Timestamp"
          },
          "last_delivery_date": {
            "$ref": "#/components/schemas/Timestamp"
          },
          "last_error": {
            "$ref": "#/components/schemas/DeliveryError"
          },
          "confirmed_offset": {
            "$ref": "#/components/schemas/Id",
            "description": "Polling mode."
          },
          "lease": {
            "$ref": "#/components/schemas/Lease",
            "description": "Polling mode, when a consumer holds the lease."
          },
          "webhook": {
            "$ref": "#/components/schemas/WebhookInfo"
          },
          "maintenance": {
            "type": "boolean",
            "description": "True during a technical pause of the platform."
          },
          "retention_seconds": {
            "type": "integer",
            "description": "How long unconfirmed updates are kept (604800 = 7 days)."
          }
        }
      },
      "DeliveryInfoResponse": {
        "type": "object",
        "required": [
          "ok",
          "result"
        ],
        "properties": {
          "ok": {
            "const": true
          },
          "result": {
            "$ref": "#/components/schemas/DeliveryInfo"
          }
        }
      },
      "WebhookInfo": {
        "type": "object",
        "required": [
          "url",
          "state",
          "pending_update_count"
        ],
        "properties": {
          "url": {
            "type": "string",
            "description": "Empty string in polling mode."
          },
          "state": {
            "type": "string",
            "enum": [
              "none",
              "active",
              "paused",
              "circuit_open",
              "disabled_by_platform"
            ],
            "description": "`paused`: waiting for the owner after an answer only the owner can fix (see `last_error`);\nthe owner resumes delivery in \"My bots\". `circuit_open`: repeated failures, SMeet probes\nby itself. `disabled_by_platform`: an administrator turned the webhook off.\n"
          },
          "pending_update_count": {
            "type": "integer"
          },
          "next_attempt_date": {
            "$ref": "#/components/schemas/Timestamp"
          },
          "last_error": {
            "$ref": "#/components/schemas/DeliveryError"
          },
          "max_connections": {
            "type": "integer",
            "const": 1
          }
        }
      },
      "WebhookInfoResponse": {
        "type": "object",
        "required": [
          "ok",
          "result"
        ],
        "properties": {
          "ok": {
            "const": true
          },
          "result": {
            "$ref": "#/components/schemas/WebhookInfo"
          }
        }
      },
      "GetMeResponse": {
        "type": "object",
        "required": [
          "ok",
          "result"
        ],
        "properties": {
          "ok": {
            "const": true
          },
          "result": {
            "$ref": "#/components/schemas/Bot"
          }
        }
      },
      "UploadFileRequest": {
        "type": "object",
        "required": [
          "file"
        ],
        "properties": {
          "file": {
            "type": "string",
            "format": "binary"
          },
          "kind": {
            "type": "string",
            "enum": [
              "photo",
              "document"
            ],
            "default": "document"
          },
          "file_name": {
            "type": "string",
            "maxLength": 255
          }
        }
      },
      "SendFileRequest": {
        "type": "object",
        "required": [
          "chat_id",
          "file_id"
        ],
        "properties": {
          "chat_id": {
            "$ref": "#/components/schemas/Id"
          },
          "file_id": {
            "$ref": "#/components/schemas/FileId",
            "description": "A file this bot uploaded, or an attachment of a message this bot received in the\nsame space. Must be `ready`: otherwise FILE_NOT_READY (retry later with the same\nIdempotency-Key) or FILE_REJECTED.\n"
          },
          "caption": {
            "type": "string",
            "maxLength": 1024
          },
          "reply_to_message_id": {
            "$ref": "#/components/schemas/Id"
          },
          "reply_markup": {
            "$ref": "#/components/schemas/InlineKeyboardMarkup"
          }
        }
      },
      "File": {
        "type": "object",
        "required": [
          "file_id",
          "kind",
          "file_name",
          "size",
          "status"
        ],
        "properties": {
          "file_id": {
            "$ref": "#/components/schemas/FileId"
          },
          "kind": {
            "type": "string",
            "enum": [
              "photo",
              "document"
            ]
          },
          "file_name": {
            "type": "string"
          },
          "mime_type": {
            "type": "string"
          },
          "size": {
            "type": "integer"
          },
          "status": {
            "$ref": "#/components/schemas/FileStatus"
          },
          "reason": {
            "type": "string",
            "description": "For rejected and scan_failed. Values: `malware_detected`, `type_not_allowed` (markup, programs and\nother refused types), `too_large` (a file above the limit is rejected; one the scanner refuses\nfor its size ends as scan_failed), `empty` (no content), `scanner_unavailable`, `scanner_outdated`\n(signatures older than 3 days), `scan_timeout`, `source_missing` (the attachment disappeared\nbefore the check). Consumers must accept unknown values.\n"
          },
          "retry_after": {
            "type": "integer",
            "description": "For scanning; seconds before asking again."
          },
          "download_path": {
            "type": "string",
            "description": "Present only for ready files; relative to the API base, requires the token.",
            "example": "/files/f_Qm9vdGZpbGVfMDAwMDAx/content"
          }
        }
      },
      "FileResponse": {
        "type": "object",
        "required": [
          "ok",
          "result"
        ],
        "properties": {
          "ok": {
            "const": true
          },
          "result": {
            "$ref": "#/components/schemas/File"
          }
        }
      }
    }
  }
}
