{
  "openapi": "3.1.0",
  "info": {
    "title": "followers.vc API",
    "version": "1.0.0",
    "summary": "Schedule and publish posts, manage channels and media, read notifications and analytics of a followers.vc workspace.",
    "description": "The public API of followers.vc, version 1. Every request carries a key of the workspace: `Authorization: Bearer fvc_…`. Keys are created and revoked on the **API keys** page of the workspace (`/app/api-keys`); a key is shown once. A key acts with the rights of its author in the team and only in the scopes it carries (a key without scopes may do everything). Requests are JSON (`Content-Type: application/json`) except the upload, which is `multipart/form-data`; times in responses are unix seconds.\n\nErrors always have the form `{\"error\": {\"code\": \"…\", \"message\": \"…\", \"fields\": {…}}}` with the HTTP status; `fields` is only present for validation errors. Lists share one envelope with `data`, `meta` and `links`; `?page` starts at 1, `?per_page` is 25 by default and at most 100."
  },
  "servers": [
    {
      "url": "https://followers.vc/api/v1",
      "description": "Production"
    }
  ],
  "security": [
    {
      "apiKey": []
    },
    {
      "oauth2": []
    }
  ],
  "tags": [
    {
      "name": "Account",
      "description": "The team, plan and key the request acts for."
    },
    {
      "name": "Channels",
      "description": "Social network accounts connected to the workspace."
    },
    {
      "name": "Media",
      "description": "The media library: files to attach to posts."
    },
    {
      "name": "Posts",
      "description": "Create, schedule, change and retry posts."
    },
    {
      "name": "Notifications",
      "description": "Notifications of the key author."
    },
    {
      "name": "Analytics",
      "description": "Reports of channels from their networks."
    },
    {
      "name": "Webhooks",
      "description": "Requests followers.vc sends to your server. Set them up on the Webhooks page (`/app/webhooks`)."
    }
  ],
  "paths": {
    "/me": {
      "get": {
        "operationId": "getMe",
        "tags": [
          "Account"
        ],
        "summary": "Who the key acts for",
        "description": "The team of the key, its plan, the limits and the key itself. Needs no scope.",
        "security": [
          {
            "apiKey": []
          },
          {
            "oauth2": []
          }
        ],
        "x-scopes": [],
        "x-plan-module": null,
        "responses": {
          "200": {
            "description": "The team, plan, limits and key.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Me"
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: `unauthenticated`.",
            "x-error-codes": [
              "unauthenticated"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              }
            }
          },
          "403": {
            "description": "Forbidden: `api_not_in_plan`, `plan_expired`.",
            "x-error-codes": [
              "api_not_in_plan",
              "plan_expired"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: `rate_limited`.",
            "x-error-codes": [
              "rate_limited"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            }
          },
          "500": {
            "description": "Server error: `server_error`.",
            "x-error-codes": [
              "server_error"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/channels": {
      "get": {
        "operationId": "listChannels",
        "tags": [
          "Channels"
        ],
        "summary": "List channels",
        "description": "Channels of the team, oldest first, with their state and what each one publishes.",
        "security": [
          {
            "apiKey": [
              "channels:read"
            ]
          },
          {
            "oauth2": [
              "channels:read"
            ]
          }
        ],
        "x-scopes": [
          "channels:read"
        ],
        "x-plan-module": "appchannels",
        "responses": {
          "200": {
            "description": "A page of channels.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Channel"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: `unauthenticated`.",
            "x-error-codes": [
              "unauthenticated"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              }
            }
          },
          "403": {
            "description": "Forbidden: `api_not_in_plan`, `plan_expired`, `module_not_in_plan`, `insufficient_scope`.",
            "x-error-codes": [
              "api_not_in_plan",
              "plan_expired",
              "module_not_in_plan",
              "insufficient_scope"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Unprocessable: `validation_failed`.",
            "x-error-codes": [
              "validation_failed"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: `rate_limited`.",
            "x-error-codes": [
              "rate_limited"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            }
          },
          "500": {
            "description": "Server error: `server_error`.",
            "x-error-codes": [
              "server_error"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "network",
            "in": "query",
            "required": false,
            "description": "Only channels of this network, e.g. `telegram`.",
            "schema": {
              "type": "string",
              "maxLength": 50
            }
          },
          {
            "$ref": "#/components/parameters/page"
          },
          {
            "$ref": "#/components/parameters/per_page"
          }
        ]
      }
    },
    "/channels/{id}": {
      "get": {
        "operationId": "getChannel",
        "tags": [
          "Channels"
        ],
        "summary": "Get a channel",
        "description": "One channel of the team.",
        "security": [
          {
            "apiKey": [
              "channels:read"
            ]
          },
          {
            "oauth2": [
              "channels:read"
            ]
          }
        ],
        "x-scopes": [
          "channels:read"
        ],
        "x-plan-module": "appchannels",
        "responses": {
          "200": {
            "description": "The channel.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Channel"
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: `unauthenticated`.",
            "x-error-codes": [
              "unauthenticated"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              }
            }
          },
          "403": {
            "description": "Forbidden: `api_not_in_plan`, `plan_expired`, `module_not_in_plan`, `insufficient_scope`.",
            "x-error-codes": [
              "api_not_in_plan",
              "plan_expired",
              "module_not_in_plan",
              "insufficient_scope"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found: `not_found`.",
            "x-error-codes": [
              "not_found"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: `rate_limited`.",
            "x-error-codes": [
              "rate_limited"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            }
          },
          "500": {
            "description": "Server error: `server_error`.",
            "x-error-codes": [
              "server_error"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Public id of the channel.",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/channels/{id}/next-slot": {
      "get": {
        "operationId": "getChannelNextSlot",
        "tags": [
          "Channels"
        ],
        "summary": "Nearest free posting slot of a channel",
        "description": "The nearest free posting slot of one channel: the moment the editor puts into the time field for \"the nearest free slot\". A slot is taken when the channel already has a scheduled post at that minute. A channel without slots, or without a free one in the next 60 days, answers `next_slot: null`.",
        "security": [
          {
            "apiKey": [
              "channels:read"
            ]
          },
          {
            "oauth2": [
              "channels:read"
            ]
          }
        ],
        "x-scopes": [
          "channels:read"
        ],
        "x-plan-module": "appchannels",
        "responses": {
          "200": {
            "description": "The slot.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/NextSlot"
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: `unauthenticated`.",
            "x-error-codes": [
              "unauthenticated"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              }
            }
          },
          "403": {
            "description": "Forbidden: `api_not_in_plan`, `plan_expired`, `module_not_in_plan`, `insufficient_scope`.",
            "x-error-codes": [
              "api_not_in_plan",
              "plan_expired",
              "module_not_in_plan",
              "insufficient_scope"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found: `not_found`.",
            "x-error-codes": [
              "not_found"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: `rate_limited`.",
            "x-error-codes": [
              "rate_limited"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            }
          },
          "500": {
            "description": "Server error: `server_error`.",
            "x-error-codes": [
              "server_error"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Public id of the channel.",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/sets": {
      "get": {
        "operationId": "listSets",
        "tags": [
          "Channels"
        ],
        "summary": "List sets",
        "description": "Channel sets of the team, the recently changed first: a name, the channels of the set and the template (caption, link, files, options) the editor puts into empty fields.",
        "security": [
          {
            "apiKey": [
              "channels:read"
            ]
          },
          {
            "oauth2": [
              "channels:read"
            ]
          }
        ],
        "x-scopes": [
          "channels:read"
        ],
        "x-plan-module": "appgroups",
        "responses": {
          "200": {
            "description": "A page of sets.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/ChannelSet"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: `unauthenticated`.",
            "x-error-codes": [
              "unauthenticated"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              }
            }
          },
          "403": {
            "description": "Forbidden: `api_not_in_plan`, `plan_expired`, `module_not_in_plan`, `insufficient_scope`.",
            "x-error-codes": [
              "api_not_in_plan",
              "plan_expired",
              "module_not_in_plan",
              "insufficient_scope"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Unprocessable: `validation_failed`.",
            "x-error-codes": [
              "validation_failed"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: `rate_limited`.",
            "x-error-codes": [
              "rate_limited"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            }
          },
          "500": {
            "description": "Server error: `server_error`.",
            "x-error-codes": [
              "server_error"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/media": {
      "get": {
        "operationId": "listMedia",
        "tags": [
          "Media"
        ],
        "summary": "List media",
        "description": "Files of the media library of the team, newest first. Folders are not shown.",
        "security": [
          {
            "apiKey": [
              "media:read"
            ]
          },
          {
            "oauth2": [
              "media:read"
            ]
          }
        ],
        "x-scopes": [
          "media:read"
        ],
        "x-plan-module": "appfiles",
        "responses": {
          "200": {
            "description": "A page of files.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Media"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: `unauthenticated`.",
            "x-error-codes": [
              "unauthenticated"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              }
            }
          },
          "403": {
            "description": "Forbidden: `api_not_in_plan`, `plan_expired`, `module_not_in_plan`, `insufficient_scope`.",
            "x-error-codes": [
              "api_not_in_plan",
              "plan_expired",
              "module_not_in_plan",
              "insufficient_scope"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Unprocessable: `validation_failed`.",
            "x-error-codes": [
              "validation_failed"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: `rate_limited`.",
            "x-error-codes": [
              "rate_limited"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            }
          },
          "500": {
            "description": "Server error: `server_error`.",
            "x-error-codes": [
              "server_error"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "Only images or only videos.",
            "schema": {
              "type": "string",
              "enum": [
                "image",
                "video"
              ]
            }
          },
          {
            "$ref": "#/components/parameters/page"
          },
          {
            "$ref": "#/components/parameters/per_page"
          }
        ]
      },
      "post": {
        "operationId": "uploadMedia",
        "tags": [
          "Media"
        ],
        "summary": "Upload a file",
        "description": "Stores an uploaded file in the root of the media library. The allowed types, the largest file and the storage come from the plan, as in the cabinet. A video is read in the background (`media_status`).",
        "security": [
          {
            "apiKey": [
              "media:write"
            ]
          },
          {
            "oauth2": [
              "media:write"
            ]
          }
        ],
        "x-scopes": [
          "media:write"
        ],
        "x-plan-module": "appfiles",
        "responses": {
          "201": {
            "description": "The stored file.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Media"
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: `unauthenticated`.",
            "x-error-codes": [
              "unauthenticated"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              }
            }
          },
          "403": {
            "description": "Forbidden: `api_not_in_plan`, `plan_expired`, `module_not_in_plan`, `insufficient_scope`, `storage_limit`, `file_too_large`.",
            "x-error-codes": [
              "api_not_in_plan",
              "plan_expired",
              "module_not_in_plan",
              "insufficient_scope",
              "storage_limit",
              "file_too_large"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Payload too large: `payload_too_large`.",
            "x-error-codes": [
              "payload_too_large"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Unprocessable: `validation_failed`.",
            "x-error-codes": [
              "validation_failed"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: `rate_limited`.",
            "x-error-codes": [
              "rate_limited"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            }
          },
          "500": {
            "description": "Server error: `server_error`.",
            "x-error-codes": [
              "server_error"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "The file."
                  }
                },
                "required": [
                  "file"
                ],
                "additionalProperties": false
              }
            }
          }
        }
      }
    },
    "/media/from-url": {
      "post": {
        "operationId": "uploadMediaFromUrl",
        "tags": [
          "Media"
        ],
        "summary": "Save a file from an address",
        "description": "Downloads a public `http`/`https` address into the media library. Addresses inside private networks are refused (422 on `url`).",
        "security": [
          {
            "apiKey": [
              "media:write"
            ]
          },
          {
            "oauth2": [
              "media:write"
            ]
          }
        ],
        "x-scopes": [
          "media:write"
        ],
        "x-plan-module": "appfiles",
        "responses": {
          "201": {
            "description": "The stored file.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Media"
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: `unauthenticated`.",
            "x-error-codes": [
              "unauthenticated"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              }
            }
          },
          "403": {
            "description": "Forbidden: `api_not_in_plan`, `plan_expired`, `module_not_in_plan`, `insufficient_scope`, `storage_limit`, `file_too_large`.",
            "x-error-codes": [
              "api_not_in_plan",
              "plan_expired",
              "module_not_in_plan",
              "insufficient_scope",
              "storage_limit",
              "file_too_large"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Unprocessable: `validation_failed`.",
            "x-error-codes": [
              "validation_failed"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: `rate_limited`.",
            "x-error-codes": [
              "rate_limited"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            }
          },
          "500": {
            "description": "Server error: `server_error`.",
            "x-error-codes": [
              "server_error"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "description": "Public http or https address.",
                    "examples": [
                      "https://example.com/photo.png"
                    ],
                    "maxLength": 2048,
                    "format": "uri"
                  },
                  "name": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Name to store the file under.",
                    "examples": [
                      "launch.png"
                    ],
                    "maxLength": 255
                  }
                },
                "required": [
                  "url"
                ],
                "additionalProperties": false
              }
            }
          }
        }
      }
    },
    "/media/{id}": {
      "get": {
        "operationId": "getMedia",
        "tags": [
          "Media"
        ],
        "summary": "Get a file",
        "description": "One file of the media library.",
        "security": [
          {
            "apiKey": [
              "media:read"
            ]
          },
          {
            "oauth2": [
              "media:read"
            ]
          }
        ],
        "x-scopes": [
          "media:read"
        ],
        "x-plan-module": "appfiles",
        "responses": {
          "200": {
            "description": "The file.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Media"
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: `unauthenticated`.",
            "x-error-codes": [
              "unauthenticated"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              }
            }
          },
          "403": {
            "description": "Forbidden: `api_not_in_plan`, `plan_expired`, `module_not_in_plan`, `insufficient_scope`.",
            "x-error-codes": [
              "api_not_in_plan",
              "plan_expired",
              "module_not_in_plan",
              "insufficient_scope"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found: `not_found`.",
            "x-error-codes": [
              "not_found"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: `rate_limited`.",
            "x-error-codes": [
              "rate_limited"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            }
          },
          "500": {
            "description": "Server error: `server_error`.",
            "x-error-codes": [
              "server_error"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Public id of the file.",
            "schema": {
              "type": "string"
            }
          }
        ]
      },
      "delete": {
        "operationId": "deleteMedia",
        "tags": [
          "Media"
        ],
        "summary": "Delete a file",
        "description": "Deletes the file and its stored copy.",
        "security": [
          {
            "apiKey": [
              "media:write"
            ]
          },
          {
            "oauth2": [
              "media:write"
            ]
          }
        ],
        "x-scopes": [
          "media:write"
        ],
        "x-plan-module": "appfiles",
        "responses": {
          "204": {
            "description": "Deleted.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            }
          },
          "401": {
            "description": "Unauthorized: `unauthenticated`.",
            "x-error-codes": [
              "unauthenticated"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              }
            }
          },
          "403": {
            "description": "Forbidden: `api_not_in_plan`, `plan_expired`, `module_not_in_plan`, `insufficient_scope`.",
            "x-error-codes": [
              "api_not_in_plan",
              "plan_expired",
              "module_not_in_plan",
              "insufficient_scope"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found: `not_found`.",
            "x-error-codes": [
              "not_found"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: `rate_limited`.",
            "x-error-codes": [
              "rate_limited"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            }
          },
          "500": {
            "description": "Server error: `server_error`.",
            "x-error-codes": [
              "server_error"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Public id of the file.",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/posts": {
      "get": {
        "operationId": "listPosts",
        "tags": [
          "Posts"
        ],
        "summary": "List posts",
        "description": "Posts of the team, latest publishing time first.",
        "security": [
          {
            "apiKey": [
              "posts:read"
            ]
          },
          {
            "oauth2": [
              "posts:read"
            ]
          }
        ],
        "x-scopes": [
          "posts:read"
        ],
        "x-plan-module": "apppublishing",
        "responses": {
          "200": {
            "description": "A page of posts.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Post"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: `unauthenticated`.",
            "x-error-codes": [
              "unauthenticated"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              }
            }
          },
          "403": {
            "description": "Forbidden: `api_not_in_plan`, `plan_expired`, `module_not_in_plan`, `insufficient_scope`.",
            "x-error-codes": [
              "api_not_in_plan",
              "plan_expired",
              "module_not_in_plan",
              "insufficient_scope"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Unprocessable: `validation_failed`.",
            "x-error-codes": [
              "validation_failed"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: `rate_limited`.",
            "x-error-codes": [
              "rate_limited"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            }
          },
          "500": {
            "description": "Server error: `server_error`.",
            "x-error-codes": [
              "server_error"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Only posts in this status.",
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "scheduled",
                "reconnect_wait",
                "published",
                "failed"
              ]
            }
          },
          {
            "name": "channel",
            "in": "query",
            "required": false,
            "description": "Public id of a channel.",
            "schema": {
              "type": "string",
              "maxLength": 64
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Publishing time from (inclusive).",
            "schema": {
              "type": [
                "string",
                "integer"
              ],
              "description": "ISO 8601 (`2026-11-05T10:30:00+03:00`, `…Z`) or unix seconds."
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Publishing time to (inclusive).",
            "schema": {
              "type": [
                "string",
                "integer"
              ],
              "description": "ISO 8601 (`2026-11-05T10:30:00+03:00`, `…Z`) or unix seconds."
            }
          },
          {
            "$ref": "#/components/parameters/page"
          },
          {
            "$ref": "#/components/parameters/per_page"
          }
        ]
      },
      "post": {
        "operationId": "createPost",
        "tags": [
          "Posts"
        ],
        "summary": "Create a post",
        "description": "Creates one post per channel through the same scheduler as the composer. Without `publish_at` the post is due now and the queue publishes it — never inside the request. If any channel refuses the post, nothing is saved: the refusal is the field `channels.<id>`. With `Idempotency-Key`, the same key and body within 24 hours return the first response (`Idempotent-Replayed: true`).",
        "security": [
          {
            "apiKey": [
              "posts:write"
            ]
          },
          {
            "oauth2": [
              "posts:write"
            ]
          }
        ],
        "x-scopes": [
          "posts:write"
        ],
        "x-plan-module": "apppublishing",
        "responses": {
          "201": {
            "description": "One post per channel.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Post"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: `unauthenticated`.",
            "x-error-codes": [
              "unauthenticated"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              }
            }
          },
          "403": {
            "description": "Forbidden: `api_not_in_plan`, `plan_expired`, `module_not_in_plan`, `insufficient_scope`, `post_quota`.",
            "x-error-codes": [
              "api_not_in_plan",
              "plan_expired",
              "module_not_in_plan",
              "insufficient_scope",
              "post_quota"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict: `idempotency_key_in_use`.",
            "x-error-codes": [
              "idempotency_key_in_use"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Unprocessable: `validation_failed`, `idempotency_key_reused`, `not_saved`.",
            "x-error-codes": [
              "validation_failed",
              "idempotency_key_reused",
              "not_saved"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: `rate_limited`.",
            "x-error-codes": [
              "rate_limited"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            }
          },
          "500": {
            "description": "Server error: `server_error`.",
            "x-error-codes": [
              "server_error"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/Idempotency-Key"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "channels": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "maxLength": 64
                    },
                    "description": "Public ids of channels of the team; one post per channel.",
                    "minItems": 1,
                    "maxItems": 50,
                    "uniqueItems": true,
                    "examples": [
                      [
                        "a8d3f0c2e1b94f7d"
                      ]
                    ]
                  },
                  "type": {
                    "type": "string",
                    "examples": [
                      "text"
                    ],
                    "enum": [
                      "text",
                      "link",
                      "media"
                    ]
                  },
                  "caption": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "examples": [
                      "Hello from the API"
                    ],
                    "maxLength": 100000
                  },
                  "thread": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "maxLength": 100000
                    },
                    "maxItems": 25,
                    "description": "Messages to publish after the post: a thread or comments, as the network takes them (see `thread` of a post). Networks without either get only the post."
                  },
                  "thread_media": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "maxLength": 64
                      },
                      "maxItems": 4
                    },
                    "description": "Files of the messages of the thread: number of the message in `thread` (from 0) => up to 4 media ids. X, Bluesky and Mastodon publish them under the message; other networks get its text only.",
                    "examples": [
                      {
                        "0": [
                          "fil-5f1c0e2a9b"
                        ]
                      }
                    ]
                  },
                  "thread_delay": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 60,
                    "default": 0,
                    "description": "Minutes to wait between the messages of the thread, from 0 to 60. 0 — each message follows the one before it at once.",
                    "examples": [
                      5
                    ]
                  },
                  "captions": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string",
                      "maxLength": 100000
                    },
                    "description": "Own text of a network, by its name (`x`, `telegram`, `facebook`…, as `channel.network` of a post): it replaces `caption` in the posts of the channels of that network.",
                    "examples": [
                      {
                        "x": "A shorter text for X"
                      }
                    ]
                  },
                  "signature": {
                    "type": "string",
                    "maxLength": 32,
                    "description": "A signature of the team to put under the text of the post and under every own text of a network: its id, or `default` for the default signature. Needs signatures in the plan of the team.",
                    "examples": [
                      "default"
                    ]
                  },
                  "channel_captions": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string",
                      "maxLength": 100000
                    },
                    "description": "The own text of a channel: channel id (one of `channels`) => its text. It replaces the text of the post and the own text of the network (`captions`) for that channel only.",
                    "examples": [
                      {
                        "acc-5f1c0e2a9b": "Hello, our page"
                      }
                    ]
                  },
                  "link": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "For a `link` post.",
                    "maxLength": 2048
                  },
                  "media": {
                    "type": [
                      "array",
                      "null"
                    ],
                    "items": {
                      "type": "string",
                      "maxLength": 64
                    },
                    "maxItems": 50,
                    "description": "Ids from `/media`, in order.",
                    "examples": [
                      []
                    ]
                  },
                  "media_alt": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string",
                      "maxLength": 1000
                    },
                    "description": "Descriptions of the files for screen readers: media id => text, up to 1000 characters. Ids that are not in media are ignored.",
                    "examples": [
                      {
                        "fil-5f1c0e2a9b": "A cat asleep on a keyboard"
                      }
                    ]
                  },
                  "options": {
                    "type": [
                      "object",
                      "null"
                    ],
                    "description": "Network-specific options, as the composer takes them."
                  },
                  "publish_at": {
                    "type": [
                      "string",
                      "integer",
                      "null"
                    ],
                    "description": "ISO 8601 (`2026-11-05T10:30:00+03:00`, `…Z`) or unix seconds. Without an offset — the time zone of the key author. Must be in the future. Leave it out to publish now: the post goes to the queue.",
                    "examples": [
                      "2026-11-05T10:30:00+03:00"
                    ]
                  },
                  "channel_publish_at": {
                    "type": "object",
                    "additionalProperties": {
                      "type": [
                        "string",
                        "integer"
                      ]
                    },
                    "description": "Own time of a channel, instead of publish_at: channel id => ISO 8601 date-time or unix seconds, in the future. Needs publish_at, the time of the other channels.",
                    "examples": [
                      {
                        "a8d3f0c2e1b94f7d": "2026-11-05T09:30:00+03:00"
                      }
                    ]
                  }
                },
                "required": [
                  "channels",
                  "type"
                ],
                "additionalProperties": false
              }
            }
          }
        }
      }
    },
    "/posts/{id}": {
      "get": {
        "operationId": "getPost",
        "tags": [
          "Posts"
        ],
        "summary": "Get a post",
        "description": "One post with its publishing attempts.",
        "security": [
          {
            "apiKey": [
              "posts:read"
            ]
          },
          {
            "oauth2": [
              "posts:read"
            ]
          }
        ],
        "x-scopes": [
          "posts:read"
        ],
        "x-plan-module": "apppublishing",
        "responses": {
          "200": {
            "description": "The post.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PostDetail"
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: `unauthenticated`.",
            "x-error-codes": [
              "unauthenticated"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              }
            }
          },
          "403": {
            "description": "Forbidden: `api_not_in_plan`, `plan_expired`, `module_not_in_plan`, `insufficient_scope`.",
            "x-error-codes": [
              "api_not_in_plan",
              "plan_expired",
              "module_not_in_plan",
              "insufficient_scope"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found: `not_found`.",
            "x-error-codes": [
              "not_found"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: `rate_limited`.",
            "x-error-codes": [
              "rate_limited"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            }
          },
          "500": {
            "description": "Server error: `server_error`.",
            "x-error-codes": [
              "server_error"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Public id of the post.",
            "schema": {
              "type": "string"
            }
          }
        ]
      },
      "patch": {
        "operationId": "updatePost",
        "tags": [
          "Posts"
        ],
        "summary": "Change a scheduled post",
        "description": "Reschedules or edits a scheduled post that is not being published right now.",
        "security": [
          {
            "apiKey": [
              "posts:write"
            ]
          },
          {
            "oauth2": [
              "posts:write"
            ]
          }
        ],
        "x-scopes": [
          "posts:write"
        ],
        "x-plan-module": "apppublishing",
        "responses": {
          "200": {
            "description": "The changed post.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PostDetail"
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: `unauthenticated`.",
            "x-error-codes": [
              "unauthenticated"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              }
            }
          },
          "403": {
            "description": "Forbidden: `api_not_in_plan`, `plan_expired`, `module_not_in_plan`, `insufficient_scope`.",
            "x-error-codes": [
              "api_not_in_plan",
              "plan_expired",
              "module_not_in_plan",
              "insufficient_scope"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found: `not_found`.",
            "x-error-codes": [
              "not_found"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict: `not_editable`.",
            "x-error-codes": [
              "not_editable"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Unprocessable: `validation_failed`.",
            "x-error-codes": [
              "validation_failed"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: `rate_limited`.",
            "x-error-codes": [
              "rate_limited"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            }
          },
          "500": {
            "description": "Server error: `server_error`.",
            "x-error-codes": [
              "server_error"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Public id of the post.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "At least one field.",
                "properties": {
                  "caption": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "examples": [
                      "New text"
                    ],
                    "maxLength": 100000
                  },
                  "options": {
                    "type": [
                      "object",
                      "null"
                    ]
                  },
                  "publish_at": {
                    "type": [
                      "string",
                      "integer",
                      "null"
                    ],
                    "description": "ISO 8601 (`2026-11-05T10:30:00+03:00`, `…Z`) or unix seconds. Must be in the future.",
                    "examples": [
                      1793000000
                    ]
                  }
                },
                "required": [],
                "additionalProperties": false
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deletePost",
        "tags": [
          "Posts"
        ],
        "summary": "Delete a post",
        "description": "Deletes a draft, scheduled or failed post that is not being published right now.",
        "security": [
          {
            "apiKey": [
              "posts:write"
            ]
          },
          {
            "oauth2": [
              "posts:write"
            ]
          }
        ],
        "x-scopes": [
          "posts:write"
        ],
        "x-plan-module": "apppublishing",
        "responses": {
          "204": {
            "description": "Deleted.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            }
          },
          "401": {
            "description": "Unauthorized: `unauthenticated`.",
            "x-error-codes": [
              "unauthenticated"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              }
            }
          },
          "403": {
            "description": "Forbidden: `api_not_in_plan`, `plan_expired`, `module_not_in_plan`, `insufficient_scope`.",
            "x-error-codes": [
              "api_not_in_plan",
              "plan_expired",
              "module_not_in_plan",
              "insufficient_scope"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found: `not_found`.",
            "x-error-codes": [
              "not_found"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict: `not_deletable`.",
            "x-error-codes": [
              "not_deletable"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: `rate_limited`.",
            "x-error-codes": [
              "rate_limited"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            }
          },
          "500": {
            "description": "Server error: `server_error`.",
            "x-error-codes": [
              "server_error"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Public id of the post.",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/posts/{id}/retry": {
      "post": {
        "operationId": "retryPost",
        "tags": [
          "Posts"
        ],
        "summary": "Retry a failed post",
        "description": "Puts a failed post back in the queue.",
        "security": [
          {
            "apiKey": [
              "posts:write"
            ]
          },
          {
            "oauth2": [
              "posts:write"
            ]
          }
        ],
        "x-scopes": [
          "posts:write"
        ],
        "x-plan-module": "apppublishing",
        "responses": {
          "200": {
            "description": "The post, due again.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PostDetail"
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: `unauthenticated`.",
            "x-error-codes": [
              "unauthenticated"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              }
            }
          },
          "403": {
            "description": "Forbidden: `api_not_in_plan`, `plan_expired`, `module_not_in_plan`, `insufficient_scope`.",
            "x-error-codes": [
              "api_not_in_plan",
              "plan_expired",
              "module_not_in_plan",
              "insufficient_scope"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found: `not_found`.",
            "x-error-codes": [
              "not_found"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict: `not_retryable`.",
            "x-error-codes": [
              "not_retryable"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: `rate_limited`.",
            "x-error-codes": [
              "rate_limited"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            }
          },
          "500": {
            "description": "Server error: `server_error`.",
            "x-error-codes": [
              "server_error"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Public id of the post.",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/posts/{id}/link": {
      "post": {
        "operationId": "linkPost",
        "tags": [
          "Posts"
        ],
        "summary": "Link a post that came out to its post in the network",
        "description": "For a failed post whose error is `uncertain` (the request may have gone through, so the post is not retried): marks it as published with the link to its post in the network. The link must be `https` and, for a network with known hosts, on one of them; the id of the post in the network is read from the link where the link carries it (`result.social_id`). The attempt log gets a `published` line, and the `post.published` webhook fires.",
        "security": [
          {
            "apiKey": [
              "posts:write"
            ]
          },
          {
            "oauth2": [
              "posts:write"
            ]
          }
        ],
        "x-scopes": [
          "posts:write"
        ],
        "x-plan-module": "apppublishing",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "maxLength": 2000,
                    "description": "The https link to the post in the network.",
                    "examples": [
                      "https://x.com/someone/status/1849000000000000001"
                    ]
                  }
                },
                "required": [
                  "url"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The post, published with the link.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PostDetail"
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: `unauthenticated`.",
            "x-error-codes": [
              "unauthenticated"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              }
            }
          },
          "403": {
            "description": "Forbidden: `api_not_in_plan`, `plan_expired`, `module_not_in_plan`, `insufficient_scope`.",
            "x-error-codes": [
              "api_not_in_plan",
              "plan_expired",
              "module_not_in_plan",
              "insufficient_scope"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found: `not_found`.",
            "x-error-codes": [
              "not_found"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict: `not_linkable`.",
            "x-error-codes": [
              "not_linkable"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Unprocessable: `validation_failed`.",
            "x-error-codes": [
              "validation_failed"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: `rate_limited`.",
            "x-error-codes": [
              "rate_limited"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            }
          },
          "500": {
            "description": "Server error: `server_error`.",
            "x-error-codes": [
              "server_error"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Public id of the post.",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/notifications": {
      "get": {
        "operationId": "listNotifications",
        "tags": [
          "Notifications"
        ],
        "summary": "List notifications",
        "description": "Notifications of the key author — the same ones as the bell of the cabinet — newest first.",
        "security": [
          {
            "apiKey": [
              "notifications:read"
            ]
          },
          {
            "oauth2": [
              "notifications:read"
            ]
          }
        ],
        "x-scopes": [
          "notifications:read"
        ],
        "x-plan-module": null,
        "responses": {
          "200": {
            "description": "A page of notifications.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Notification"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: `unauthenticated`.",
            "x-error-codes": [
              "unauthenticated"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              }
            }
          },
          "403": {
            "description": "Forbidden: `api_not_in_plan`, `plan_expired`, `insufficient_scope`.",
            "x-error-codes": [
              "api_not_in_plan",
              "plan_expired",
              "insufficient_scope"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Unprocessable: `validation_failed`.",
            "x-error-codes": [
              "validation_failed"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: `rate_limited`.",
            "x-error-codes": [
              "rate_limited"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            }
          },
          "500": {
            "description": "Server error: `server_error`.",
            "x-error-codes": [
              "server_error"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "unread",
            "in": "query",
            "required": false,
            "description": "`1` — only unread ones.",
            "schema": {
              "type": [
                "boolean",
                "integer"
              ],
              "enum": [
                true,
                false,
                0,
                1
              ]
            }
          },
          {
            "$ref": "#/components/parameters/page"
          },
          {
            "$ref": "#/components/parameters/per_page"
          }
        ]
      }
    },
    "/notifications/unread-count": {
      "get": {
        "operationId": "countUnreadNotifications",
        "tags": [
          "Notifications"
        ],
        "summary": "Count unread notifications",
        "description": "How many notifications of the key author are unread.",
        "security": [
          {
            "apiKey": [
              "notifications:read"
            ]
          },
          {
            "oauth2": [
              "notifications:read"
            ]
          }
        ],
        "x-scopes": [
          "notifications:read"
        ],
        "x-plan-module": null,
        "responses": {
          "200": {
            "description": "The count.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/UnreadCount"
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: `unauthenticated`.",
            "x-error-codes": [
              "unauthenticated"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              }
            }
          },
          "403": {
            "description": "Forbidden: `api_not_in_plan`, `plan_expired`, `insufficient_scope`.",
            "x-error-codes": [
              "api_not_in_plan",
              "plan_expired",
              "insufficient_scope"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: `rate_limited`.",
            "x-error-codes": [
              "rate_limited"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            }
          },
          "500": {
            "description": "Server error: `server_error`.",
            "x-error-codes": [
              "server_error"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/notifications/{id}/read": {
      "post": {
        "operationId": "readNotification",
        "tags": [
          "Notifications"
        ],
        "summary": "Mark a notification read",
        "description": "Repeatable: an already read notification answers 200 too.",
        "security": [
          {
            "apiKey": [
              "notifications:write"
            ]
          },
          {
            "oauth2": [
              "notifications:write"
            ]
          }
        ],
        "x-scopes": [
          "notifications:write"
        ],
        "x-plan-module": null,
        "responses": {
          "200": {
            "description": "The notification.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Notification"
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: `unauthenticated`.",
            "x-error-codes": [
              "unauthenticated"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              }
            }
          },
          "403": {
            "description": "Forbidden: `api_not_in_plan`, `plan_expired`, `insufficient_scope`.",
            "x-error-codes": [
              "api_not_in_plan",
              "plan_expired",
              "insufficient_scope"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found: `not_found`.",
            "x-error-codes": [
              "not_found"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: `rate_limited`.",
            "x-error-codes": [
              "rate_limited"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            }
          },
          "500": {
            "description": "Server error: `server_error`.",
            "x-error-codes": [
              "server_error"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Public id of the notification.",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/notifications/read-all": {
      "post": {
        "operationId": "readAllNotifications",
        "tags": [
          "Notifications"
        ],
        "summary": "Mark all notifications read",
        "description": "Marks every notification of the key author read.",
        "security": [
          {
            "apiKey": [
              "notifications:write"
            ]
          },
          {
            "oauth2": [
              "notifications:write"
            ]
          }
        ],
        "x-scopes": [
          "notifications:write"
        ],
        "x-plan-module": null,
        "responses": {
          "200": {
            "description": "Nothing is left unread.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/UnreadCount"
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: `unauthenticated`.",
            "x-error-codes": [
              "unauthenticated"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              }
            }
          },
          "403": {
            "description": "Forbidden: `api_not_in_plan`, `plan_expired`, `insufficient_scope`.",
            "x-error-codes": [
              "api_not_in_plan",
              "plan_expired",
              "insufficient_scope"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: `rate_limited`.",
            "x-error-codes": [
              "rate_limited"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            }
          },
          "500": {
            "description": "Server error: `server_error`.",
            "x-error-codes": [
              "server_error"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/analytics": {
      "get": {
        "operationId": "listAnalytics",
        "tags": [
          "Analytics"
        ],
        "summary": "Channels with analytics",
        "description": "Channels of the team that have a report, grouped by network. Networks the plan does not include are left out.",
        "security": [
          {
            "apiKey": [
              "analytics:read"
            ]
          },
          {
            "oauth2": [
              "analytics:read"
            ]
          }
        ],
        "x-scopes": [
          "analytics:read"
        ],
        "x-plan-module": "appanalytics",
        "responses": {
          "200": {
            "description": "Networks with their channels.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AnalyticsNetwork"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: `unauthenticated`.",
            "x-error-codes": [
              "unauthenticated"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              }
            }
          },
          "403": {
            "description": "Forbidden: `api_not_in_plan`, `plan_expired`, `module_not_in_plan`, `insufficient_scope`.",
            "x-error-codes": [
              "api_not_in_plan",
              "plan_expired",
              "module_not_in_plan",
              "insufficient_scope"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: `rate_limited`.",
            "x-error-codes": [
              "rate_limited"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            }
          },
          "500": {
            "description": "Server error: `server_error`.",
            "x-error-codes": [
              "server_error"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/analytics/{id}": {
      "get": {
        "operationId": "getAnalytics",
        "tags": [
          "Analytics"
        ],
        "summary": "Report of a channel",
        "description": "The report of one channel from the same service of the network as the cabinet. The network may be asked while the request runs, so it can take a while.",
        "security": [
          {
            "apiKey": [
              "analytics:read"
            ]
          },
          {
            "oauth2": [
              "analytics:read"
            ]
          }
        ],
        "x-scopes": [
          "analytics:read"
        ],
        "x-plan-module": "appanalytics",
        "responses": {
          "200": {
            "description": "The report.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalyticsReport"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: `unauthenticated`.",
            "x-error-codes": [
              "unauthenticated"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              }
            }
          },
          "403": {
            "description": "Forbidden: `api_not_in_plan`, `plan_expired`, `module_not_in_plan`, `insufficient_scope`, `analytics_not_in_plan`.",
            "x-error-codes": [
              "api_not_in_plan",
              "plan_expired",
              "module_not_in_plan",
              "insufficient_scope",
              "analytics_not_in_plan"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found: `not_found`, `analytics_unavailable`.",
            "x-error-codes": [
              "not_found",
              "analytics_unavailable"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Unprocessable: `validation_failed`.",
            "x-error-codes": [
              "validation_failed"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: `rate_limited`.",
            "x-error-codes": [
              "rate_limited"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            }
          },
          "500": {
            "description": "Server error: `server_error`.",
            "x-error-codes": [
              "server_error"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Bad gateway: `provider_error`.",
            "x-error-codes": [
              "provider_error"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Public id of the channel.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "ISO 8601 or unix seconds; a bare date covers its whole day. Default: 30 days before `to`.",
            "schema": {
              "type": "string",
              "maxLength": 64
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "ISO 8601 or unix seconds. Default: now. The range is at most 365 days.",
            "schema": {
              "type": "string",
              "maxLength": 64
            }
          }
        ]
      }
    }
  },
  "webhooks": {
    "post.scheduled": {
      "post": {
        "operationId": "webhook_post_scheduled",
        "tags": [
          "Webhooks"
        ],
        "summary": "A post has been scheduled",
        "description": "A post has been scheduled for a time (one event per channel and time).",
        "parameters": [
          {
            "name": "X-Followers-Event",
            "in": "header",
            "required": true,
            "description": "The event, e.g. `post.published`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Followers-Delivery",
            "in": "header",
            "required": true,
            "description": "Id of this delivery (new for a manual redelivery).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Followers-Timestamp",
            "in": "header",
            "required": true,
            "description": "Unix time the request was signed.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Followers-Signature",
            "in": "header",
            "required": true,
            "description": "`t=<timestamp>,v1=<signature>`: hex HMAC-SHA256 of `<timestamp>.<raw body>` with the secret of the webhook (`whsec_…`). Refuse a `t` more than 5 minutes away from your clock.",
            "schema": {
              "type": "string",
              "examples": [
                "t=1792143000,v1=5f2b…"
              ]
            }
          },
          {
            "name": "User-Agent",
            "in": "header",
            "required": true,
            "description": "`followers.vc-Webhooks/1.0`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookPostScheduled"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Any 2xx within 10 seconds: delivered. The body is not used."
          },
          "default": {
            "description": "No answer, a timeout, 429 or 5xx: tried again after 1 minute, 5 minutes, 30 minutes, 2 hours and 12 hours. Any other 4xx or 3xx fails at once. After 20 failed requests in a row the webhook is turned off."
          }
        }
      }
    },
    "post.published": {
      "post": {
        "operationId": "webhook_post_published",
        "tags": [
          "Webhooks"
        ],
        "summary": "A post has been published",
        "description": "A post has been published on a network.",
        "parameters": [
          {
            "name": "X-Followers-Event",
            "in": "header",
            "required": true,
            "description": "The event, e.g. `post.published`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Followers-Delivery",
            "in": "header",
            "required": true,
            "description": "Id of this delivery (new for a manual redelivery).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Followers-Timestamp",
            "in": "header",
            "required": true,
            "description": "Unix time the request was signed.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Followers-Signature",
            "in": "header",
            "required": true,
            "description": "`t=<timestamp>,v1=<signature>`: hex HMAC-SHA256 of `<timestamp>.<raw body>` with the secret of the webhook (`whsec_…`). Refuse a `t` more than 5 minutes away from your clock.",
            "schema": {
              "type": "string",
              "examples": [
                "t=1792143000,v1=5f2b…"
              ]
            }
          },
          {
            "name": "User-Agent",
            "in": "header",
            "required": true,
            "description": "`followers.vc-Webhooks/1.0`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookPostPublished"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Any 2xx within 10 seconds: delivered. The body is not used."
          },
          "default": {
            "description": "No answer, a timeout, 429 or 5xx: tried again after 1 minute, 5 minutes, 30 minutes, 2 hours and 12 hours. Any other 4xx or 3xx fails at once. After 20 failed requests in a row the webhook is turned off."
          }
        }
      }
    },
    "post.failed": {
      "post": {
        "operationId": "webhook_post_failed",
        "tags": [
          "Webhooks"
        ],
        "summary": "A post has failed for good",
        "description": "A post has failed for good: no more automatic attempts will be made.",
        "parameters": [
          {
            "name": "X-Followers-Event",
            "in": "header",
            "required": true,
            "description": "The event, e.g. `post.published`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Followers-Delivery",
            "in": "header",
            "required": true,
            "description": "Id of this delivery (new for a manual redelivery).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Followers-Timestamp",
            "in": "header",
            "required": true,
            "description": "Unix time the request was signed.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Followers-Signature",
            "in": "header",
            "required": true,
            "description": "`t=<timestamp>,v1=<signature>`: hex HMAC-SHA256 of `<timestamp>.<raw body>` with the secret of the webhook (`whsec_…`). Refuse a `t` more than 5 minutes away from your clock.",
            "schema": {
              "type": "string",
              "examples": [
                "t=1792143000,v1=5f2b…"
              ]
            }
          },
          {
            "name": "User-Agent",
            "in": "header",
            "required": true,
            "description": "`followers.vc-Webhooks/1.0`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookPostFailed"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Any 2xx within 10 seconds: delivered. The body is not used."
          },
          "default": {
            "description": "No answer, a timeout, 429 or 5xx: tried again after 1 minute, 5 minutes, 30 minutes, 2 hours and 12 hours. Any other 4xx or 3xx fails at once. After 20 failed requests in a row the webhook is turned off."
          }
        }
      }
    },
    "channel.disconnected": {
      "post": {
        "operationId": "webhook_channel_disconnected",
        "tags": [
          "Webhooks"
        ],
        "summary": "A channel needs reconnecting",
        "description": "A channel has lost access and needs reconnecting; its posts wait.",
        "parameters": [
          {
            "name": "X-Followers-Event",
            "in": "header",
            "required": true,
            "description": "The event, e.g. `post.published`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Followers-Delivery",
            "in": "header",
            "required": true,
            "description": "Id of this delivery (new for a manual redelivery).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Followers-Timestamp",
            "in": "header",
            "required": true,
            "description": "Unix time the request was signed.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Followers-Signature",
            "in": "header",
            "required": true,
            "description": "`t=<timestamp>,v1=<signature>`: hex HMAC-SHA256 of `<timestamp>.<raw body>` with the secret of the webhook (`whsec_…`). Refuse a `t` more than 5 minutes away from your clock.",
            "schema": {
              "type": "string",
              "examples": [
                "t=1792143000,v1=5f2b…"
              ]
            }
          },
          {
            "name": "User-Agent",
            "in": "header",
            "required": true,
            "description": "`followers.vc-Webhooks/1.0`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookChannelDisconnected"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Any 2xx within 10 seconds: delivered. The body is not used."
          },
          "default": {
            "description": "No answer, a timeout, 429 or 5xx: tried again after 1 minute, 5 minutes, 30 minutes, 2 hours and 12 hours. Any other 4xx or 3xx fails at once. After 20 failed requests in a row the webhook is turned off."
          }
        }
      }
    },
    "approval.approved": {
      "post": {
        "operationId": "webhook_approval_approved",
        "tags": [
          "Webhooks"
        ],
        "summary": "A post has been approved",
        "description": "The reviewer approved the posts on the public approval page.",
        "parameters": [
          {
            "name": "X-Followers-Event",
            "in": "header",
            "required": true,
            "description": "The event, e.g. `post.published`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Followers-Delivery",
            "in": "header",
            "required": true,
            "description": "Id of this delivery (new for a manual redelivery).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Followers-Timestamp",
            "in": "header",
            "required": true,
            "description": "Unix time the request was signed.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Followers-Signature",
            "in": "header",
            "required": true,
            "description": "`t=<timestamp>,v1=<signature>`: hex HMAC-SHA256 of `<timestamp>.<raw body>` with the secret of the webhook (`whsec_…`). Refuse a `t` more than 5 minutes away from your clock.",
            "schema": {
              "type": "string",
              "examples": [
                "t=1792143000,v1=5f2b…"
              ]
            }
          },
          {
            "name": "User-Agent",
            "in": "header",
            "required": true,
            "description": "`followers.vc-Webhooks/1.0`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookApprovalApproved"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Any 2xx within 10 seconds: delivered. The body is not used."
          },
          "default": {
            "description": "No answer, a timeout, 429 or 5xx: tried again after 1 minute, 5 minutes, 30 minutes, 2 hours and 12 hours. Any other 4xx or 3xx fails at once. After 20 failed requests in a row the webhook is turned off."
          }
        }
      }
    },
    "approval.changes_requested": {
      "post": {
        "operationId": "webhook_approval_changes_requested",
        "tags": [
          "Webhooks"
        ],
        "summary": "Changes to a post have been requested",
        "description": "The reviewer asked for changes on the public approval page; the posts wait for a new approval.",
        "parameters": [
          {
            "name": "X-Followers-Event",
            "in": "header",
            "required": true,
            "description": "The event, e.g. `post.published`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Followers-Delivery",
            "in": "header",
            "required": true,
            "description": "Id of this delivery (new for a manual redelivery).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Followers-Timestamp",
            "in": "header",
            "required": true,
            "description": "Unix time the request was signed.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Followers-Signature",
            "in": "header",
            "required": true,
            "description": "`t=<timestamp>,v1=<signature>`: hex HMAC-SHA256 of `<timestamp>.<raw body>` with the secret of the webhook (`whsec_…`). Refuse a `t` more than 5 minutes away from your clock.",
            "schema": {
              "type": "string",
              "examples": [
                "t=1792143000,v1=5f2b…"
              ]
            }
          },
          {
            "name": "User-Agent",
            "in": "header",
            "required": true,
            "description": "`followers.vc-Webhooks/1.0`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookApprovalChangesRequested"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Any 2xx within 10 seconds: delivered. The body is not used."
          },
          "default": {
            "description": "No answer, a timeout, 429 or 5xx: tried again after 1 minute, 5 minutes, 30 minutes, 2 hours and 12 hours. Any other 4xx or 3xx fails at once. After 20 failed requests in a row the webhook is turned off."
          }
        }
      }
    },
    "ping": {
      "post": {
        "operationId": "webhook_ping",
        "tags": [
          "Webhooks"
        ],
        "summary": "Test delivery",
        "description": "Sent only by the **Send test** button of the Webhooks page.",
        "parameters": [
          {
            "name": "X-Followers-Event",
            "in": "header",
            "required": true,
            "description": "The event, e.g. `post.published`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Followers-Delivery",
            "in": "header",
            "required": true,
            "description": "Id of this delivery (new for a manual redelivery).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Followers-Timestamp",
            "in": "header",
            "required": true,
            "description": "Unix time the request was signed.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Followers-Signature",
            "in": "header",
            "required": true,
            "description": "`t=<timestamp>,v1=<signature>`: hex HMAC-SHA256 of `<timestamp>.<raw body>` with the secret of the webhook (`whsec_…`). Refuse a `t` more than 5 minutes away from your clock.",
            "schema": {
              "type": "string",
              "examples": [
                "t=1792143000,v1=5f2b…"
              ]
            }
          },
          {
            "name": "User-Agent",
            "in": "header",
            "required": true,
            "description": "`followers.vc-Webhooks/1.0`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookPing"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Any 2xx within 10 seconds: delivered. The body is not used."
          },
          "default": {
            "description": "No answer, a timeout, 429 or 5xx: tried again after 1 minute, 5 minutes, 30 minutes, 2 hours and 12 hours. Any other 4xx or 3xx fails at once. After 20 failed requests in a row the webhook is turned off."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "fvc_…",
        "description": "A workspace key `fvc_` + 40 characters in `Authorization: Bearer <key>` (never in the query string). Scopes: `channels:read`, `media:read`, `media:write`, `posts:read`, `posts:write`, `notifications:read`, `notifications:write`, `analytics:read`."
      },
      "oauth2": {
        "type": "oauth2",
        "description": "OAuth 2.1 for applications that act for a person: the authorization code grant with PKCE (S256), access tokens `fvc_at_…` for an hour and refresh tokens `fvc_rt_…` for 30 days, rotated on every refresh. The person picks nothing but allows the scopes the application asks for, in the team they work in. MCP clients find the server through `/.well-known/oauth-protected-resource/api/mcp` and register themselves at `/oauth/register`.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://followers.vc/app/api-keys/authorize",
            "tokenUrl": "https://followers.vc/oauth/token",
            "refreshUrl": "https://followers.vc/oauth/token",
            "scopes": {
              "channels:read": "Read channels",
              "media:read": "Read the media library",
              "media:write": "Upload and delete media",
              "posts:read": "Read posts",
              "posts:write": "Create, change and delete posts",
              "notifications:read": "Read notifications",
              "notifications:write": "Mark notifications as read",
              "analytics:read": "Read analytics"
            }
          }
        }
      }
    },
    "parameters": {
      "page": {
        "name": "page",
        "in": "query",
        "required": false,
        "description": "Page, from 1.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "default": 1
        }
      },
      "per_page": {
        "name": "per_page",
        "in": "query",
        "required": false,
        "description": "Items per page; more than 100 is cut to 100.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "default": 25
        }
      },
      "Idempotency-Key": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "Any unique string up to 255 characters. The same key with the same body within 24 hours returns the first successful response; with another body — 422 `idempotency_key_reused`; while the first is still running — 409 `idempotency_key_in_use`.",
        "schema": {
          "type": "string",
          "maxLength": 255,
          "examples": [
            "order-42"
          ]
        }
      }
    },
    "headers": {
      "X-RateLimit-Limit": {
        "description": "Requests per minute for this key (from the plan, 60 by default). Absent when the plan sets no limit.",
        "schema": {
          "type": "integer"
        }
      },
      "X-RateLimit-Remaining": {
        "description": "Requests left in the current minute.",
        "schema": {
          "type": "integer"
        }
      },
      "Retry-After": {
        "description": "Seconds to wait before the next request.",
        "schema": {
          "type": "integer"
        }
      },
      "WWW-Authenticate": {
        "description": "`Bearer`.",
        "schema": {
          "type": "string"
        }
      },
      "Idempotent-Replayed": {
        "description": "`true` when the response is the stored first response for this Idempotency-Key.",
        "schema": {
          "type": "string",
          "enum": [
            "true"
          ]
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "description": "Machine-readable code, snake_case.",
                "examples": [
                  "validation_failed"
                ],
                "enum": [
                  "unauthenticated",
                  "api_not_in_plan",
                  "plan_expired",
                  "module_not_in_plan",
                  "insufficient_scope",
                  "storage_limit",
                  "file_too_large",
                  "post_quota",
                  "analytics_not_in_plan",
                  "not_found",
                  "analytics_unavailable",
                  "method_not_allowed",
                  "not_editable",
                  "not_deletable",
                  "not_retryable",
                  "not_linkable",
                  "idempotency_key_in_use",
                  "payload_too_large",
                  "validation_failed",
                  "idempotency_key_reused",
                  "not_saved",
                  "rate_limited",
                  "server_error",
                  "provider_error"
                ]
              },
              "message": {
                "type": "string",
                "description": "Human-readable text.",
                "examples": [
                  "The given data was invalid."
                ]
              },
              "fields": {
                "type": "object",
                "description": "Only for validation: field → messages.",
                "additionalProperties": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "examples": [
                  {
                    "caption": [
                      "The caption field must be a string."
                    ]
                  }
                ]
              }
            },
            "required": [
              "code",
              "message"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "error"
        ],
        "additionalProperties": false
      },
      "ListEnvelope": {
        "type": "object",
        "description": "The one list format of the API. Links keep `per_page` and the filters in force.",
        "properties": {
          "data": {
            "type": "array",
            "items": {},
            "description": "Items of this page."
          },
          "meta": {
            "type": "object",
            "properties": {
              "page": {
                "type": "integer",
                "description": "Current page, from 1.",
                "examples": [
                  1
                ]
              },
              "per_page": {
                "type": "integer",
                "description": "Items per page (25 by default, at most 100).",
                "examples": [
                  25
                ]
              },
              "total": {
                "type": "integer",
                "description": "Items in all pages.",
                "examples": [
                  40
                ]
              }
            },
            "required": [
              "page",
              "per_page",
              "total"
            ],
            "additionalProperties": false
          },
          "links": {
            "type": "object",
            "properties": {
              "next": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Next page; null on the last.",
                "examples": [
                  "https://followers.vc/api/v1/posts?per_page=25&page=2"
                ]
              },
              "prev": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Previous page; null on the first."
              }
            },
            "required": [
              "next",
              "prev"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "data",
          "meta",
          "links"
        ],
        "additionalProperties": false
      },
      "Me": {
        "type": "object",
        "properties": {
          "team": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "description": "Public id of the team.",
                "examples": [
                  "t-91ac0f"
                ]
              },
              "name": {
                "type": "string",
                "examples": [
                  "Acme"
                ]
              }
            },
            "required": [
              "id",
              "name"
            ],
            "additionalProperties": false
          },
          "plan": {
            "type": "object",
            "properties": {
              "name": {
                "type": [
                  "string",
                  "null"
                ],
                "examples": [
                  "Pro"
                ]
              },
              "expires_at": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "When the plan expires; null — it does not. Unix seconds.",
                "examples": [
                  1793000000
                ]
              }
            },
            "required": [
              "name",
              "expires_at"
            ],
            "additionalProperties": false
          },
          "limits": {
            "type": "object",
            "properties": {
              "rate_limit": {
                "type": "integer",
                "description": "Requests per minute for this key; -1 — no limit.",
                "examples": [
                  60
                ]
              },
              "posts": {
                "type": "object",
                "properties": {
                  "limit": {
                    "type": "integer",
                    "description": "-1 — no limit.",
                    "examples": [
                      300
                    ]
                  },
                  "used": {
                    "type": "integer",
                    "examples": [
                      12
                    ]
                  },
                  "left": {
                    "type": "integer",
                    "examples": [
                      288
                    ]
                  }
                },
                "required": [
                  "limit",
                  "used",
                  "left"
                ],
                "additionalProperties": false
              },
              "max_channels": {
                "type": "integer",
                "description": "-1 — no limit.",
                "examples": [
                  10
                ]
              }
            },
            "required": [
              "rate_limit",
              "posts",
              "max_channels"
            ],
            "additionalProperties": false
          },
          "key": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string",
                "examples": [
                  "n8n"
                ]
              },
              "prefix": {
                "type": "string",
                "description": "First 8 characters of the key.",
                "examples": [
                  "fvc_a1B2"
                ]
              },
              "scopes": {
                "type": [
                  "array",
                  "null"
                ],
                "items": {
                  "type": "string",
                  "enum": [
                    "channels:read",
                    "media:read",
                    "media:write",
                    "posts:read",
                    "posts:write",
                    "notifications:read",
                    "notifications:write",
                    "analytics:read"
                  ]
                },
                "description": "null — every scope.",
                "examples": [
                  null
                ]
              }
            },
            "required": [
              "name",
              "prefix",
              "scopes"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "team",
          "plan",
          "limits",
          "key"
        ],
        "additionalProperties": false
      },
      "Channel": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Public id of the channel.",
            "examples": [
              "a8d3f0c2e1b94f7d"
            ]
          },
          "network": {
            "type": "string",
            "description": "Social network.",
            "examples": [
              "telegram"
            ]
          },
          "category": {
            "type": [
              "string",
              "null"
            ],
            "description": "profile, page, channel, group…",
            "examples": [
              "channel"
            ]
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "Acme news"
            ]
          },
          "username": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "acmenews"
            ]
          },
          "avatar": {
            "type": [
              "string",
              "null"
            ],
            "description": "Address of the avatar.",
            "examples": [
              "https://followers.vc/storage/avatar.png"
            ]
          },
          "status": {
            "type": "string",
            "description": "`needs_reconnect` — the channel lost access; its posts wait.",
            "examples": [
              "active"
            ],
            "enum": [
              "active",
              "paused",
              "needs_reconnect"
            ]
          },
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why the channel must be reconnected; null for any other status."
          },
          "capabilities": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ChannelCapabilities"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "id",
          "network",
          "category",
          "name",
          "username",
          "avatar",
          "status",
          "reason",
          "capabilities"
        ],
        "additionalProperties": false
      },
      "NextSlot": {
        "type": "object",
        "properties": {
          "channel": {
            "type": "string",
            "description": "Public id of the channel.",
            "examples": [
              "a8d3f0c2e1b94f7d"
            ]
          },
          "next_slot": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Unix time of the nearest free slot; null when the channel has no slots or none is free within 60 days.",
            "examples": [
              1792569600
            ]
          },
          "slots": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/PostingSlots"
              },
              {
                "type": "null"
              }
            ],
            "description": "The slots set for the channel; null when there are none."
          }
        },
        "required": [
          "channel",
          "next_slot",
          "slots"
        ],
        "additionalProperties": false
      },
      "PostingSlots": {
        "type": "object",
        "description": "Posting slots of a channel: times of day on chosen weekdays in one time zone.",
        "properties": {
          "times": {
            "type": "array",
            "items": {
              "type": "string",
              "examples": [
                "09:00"
              ]
            },
            "description": "Times of day, `HH:MM`."
          },
          "days": {
            "type": "array",
            "items": {
              "type": "integer",
              "minimum": 1,
              "maximum": 7
            },
            "description": "ISO weekdays, Monday is 1."
          },
          "timezone": {
            "type": "string",
            "description": "The time zone of the times.",
            "examples": [
              "Europe/Moscow"
            ]
          }
        },
        "required": [
          "times",
          "days",
          "timezone"
        ],
        "additionalProperties": false
      },
      "ChannelSet": {
        "type": "object",
        "description": "A channel set: channels published to together, with an optional template.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Public id of the set.",
            "examples": [
              "b7e4c1a9d2f0"
            ]
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "Shops"
            ]
          },
          "color": {
            "type": [
              "string",
              "null"
            ],
            "description": "The colour of the set in the cabinet.",
            "examples": [
              "primary"
            ]
          },
          "channels": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Public ids of the channels of the set; pass them to `POST /posts`."
          },
          "template": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/SetTemplate"
              },
              {
                "type": "null"
              }
            ],
            "description": "What the editor puts into empty fields when the set is chosen; null for a set without a template."
          }
        },
        "required": [
          "id",
          "name",
          "color",
          "channels",
          "template"
        ],
        "additionalProperties": false
      },
      "SetTemplate": {
        "type": "object",
        "properties": {
          "caption": {
            "type": "string",
            "description": "The text."
          },
          "link": {
            "type": "string",
            "description": "The link; empty when there is none."
          },
          "media": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Media ids of the files, as in `/media`."
          },
          "options": {
            "type": "object",
            "description": "Options of the networks, e.g. `fb_type`.",
            "additionalProperties": true
          }
        },
        "required": [
          "caption",
          "link",
          "media",
          "options"
        ],
        "additionalProperties": false
      },
      "ChannelCapabilities": {
        "type": "object",
        "description": "What the channel publishes.",
        "properties": {
          "types": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "text",
                "link",
                "media",
                "video"
              ]
            },
            "description": "Post types the channel publishes.",
            "examples": [
              [
                "text",
                "link",
                "media"
              ]
            ]
          },
          "max_length": {
            "type": "object",
            "description": "Longest text per post type, in characters; null — no limit.",
            "additionalProperties": {
              "type": [
                "integer",
                "null"
              ]
            },
            "examples": [
              {
                "text": 4096,
                "link": 4096,
                "media": 1024
              }
            ]
          },
          "max_media": {
            "type": "integer",
            "description": "Files in one post.",
            "examples": [
              10
            ]
          },
          "images": {
            "type": "boolean",
            "description": "Takes images.",
            "examples": [
              true
            ]
          },
          "videos": {
            "type": "boolean",
            "description": "Takes videos.",
            "examples": [
              true
            ]
          },
          "files": {
            "type": "boolean",
            "description": "Takes other files (audio, documents).",
            "examples": [
              false
            ]
          },
          "mixed": {
            "type": "boolean",
            "description": "Images and videos in one post.",
            "examples": [
              false
            ]
          },
          "limits": {
            "type": "object",
            "properties": {
              "image": {
                "type": "object",
                "properties": {
                  "max_bytes": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "description": "Largest image; null — not known.",
                    "examples": [
                      10485760
                    ]
                  },
                  "aspect": {
                    "oneOf": [
                      {
                        "$ref": "#/components/schemas/AspectRange"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  }
                },
                "required": [
                  "max_bytes",
                  "aspect"
                ],
                "additionalProperties": false
              },
              "video": {
                "type": "object",
                "properties": {
                  "max_bytes": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "description": "Largest video.",
                    "examples": [
                      52428800
                    ]
                  },
                  "min_seconds": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "description": "Shortest video.",
                    "examples": [
                      3
                    ]
                  },
                  "max_seconds": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "description": "Longest video.",
                    "examples": [
                      90
                    ]
                  },
                  "aspect": {
                    "oneOf": [
                      {
                        "$ref": "#/components/schemas/AspectRange"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "min_width": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  },
                  "min_height": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  },
                  "max_width": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "examples": [
                      1920
                    ]
                  },
                  "max_height": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "examples": [
                      1920
                    ]
                  },
                  "codecs": {
                    "type": [
                      "array",
                      "null"
                    ],
                    "items": {
                      "type": "string"
                    },
                    "description": "Video codecs as ffprobe names them.",
                    "examples": [
                      [
                        "h264",
                        "hevc"
                      ]
                    ]
                  }
                },
                "required": [
                  "max_bytes",
                  "min_seconds",
                  "max_seconds",
                  "aspect",
                  "min_width",
                  "min_height",
                  "max_width",
                  "max_height",
                  "codecs"
                ],
                "additionalProperties": false
              }
            },
            "required": [
              "image",
              "video"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "types",
          "max_length",
          "max_media",
          "images",
          "videos",
          "files",
          "mixed",
          "limits"
        ],
        "additionalProperties": false
      },
      "AspectRange": {
        "type": "object",
        "description": "Narrowest and widest, width / height.",
        "properties": {
          "min": {
            "type": "number",
            "examples": [
              0.8
            ]
          },
          "max": {
            "type": "number",
            "examples": [
              1.91
            ]
          }
        },
        "required": [
          "min",
          "max"
        ],
        "additionalProperties": false
      },
      "Media": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Public id of the file.",
            "examples": [
              "fil-5f1c0e2a9b"
            ]
          },
          "name": {
            "type": "string",
            "examples": [
              "photo_x1Y2z3.png"
            ]
          },
          "url": {
            "type": "string",
            "description": "Public address of the file.",
            "examples": [
              "https://followers.vc/storage/files/photo.png"
            ]
          },
          "type": {
            "type": [
              "string",
              "null"
            ],
            "description": "Kind of file: image, video, pdf, doc, csv…",
            "examples": [
              "image"
            ]
          },
          "mime": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "image/png"
            ]
          },
          "extension": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "png"
            ]
          },
          "size": {
            "type": "integer",
            "description": "Bytes.",
            "examples": [
              48213
            ]
          },
          "width": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Pixels; null — not known.",
            "examples": [
              1080
            ]
          },
          "height": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Pixels; null — not known.",
            "examples": [
              1080
            ]
          },
          "duration": {
            "type": [
              "number",
              "null"
            ],
            "description": "Seconds, for a video."
          },
          "media_status": {
            "type": [
              "string",
              "null"
            ],
            "description": "Background reading of the file: pending, ready or failed; null — never read (a document, an older file).",
            "examples": [
              "ready"
            ],
            "enum": [
              "pending",
              "ready",
              "failed",
              null
            ]
          },
          "media_error": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why the file could not be read."
          },
          "created_at": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Upload time. Unix seconds.",
            "examples": [
              1793000000
            ]
          }
        },
        "required": [
          "id",
          "name",
          "url",
          "type",
          "mime",
          "extension",
          "size",
          "width",
          "height",
          "duration",
          "media_status",
          "media_error",
          "created_at"
        ],
        "additionalProperties": false
      },
      "Post": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Public id of the post.",
            "examples": [
              "kQ7x9a1b2c3d"
            ]
          },
          "channel": {
            "type": "object",
            "properties": {
              "id": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Public id of the channel; null when it was removed.",
                "examples": [
                  "a8d3f0c2e1b94f7d"
                ]
              },
              "network": {
                "type": [
                  "string",
                  "null"
                ],
                "examples": [
                  "telegram"
                ]
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ],
                "examples": [
                  "Acme news"
                ]
              }
            },
            "required": [
              "id",
              "network",
              "name"
            ],
            "additionalProperties": false
          },
          "type": {
            "type": "string",
            "description": "text, link or media (older posts may carry other types).",
            "examples": [
              "text"
            ]
          },
          "caption": {
            "type": "string",
            "examples": [
              "Hello from the API"
            ]
          },
          "thread": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Messages published after the post: a thread of replies where the network has threads (X, Bluesky, Mastodon, Threads, Telegram, Discord, Slack), comments under the post where it has comments (Facebook, Instagram, Reddit, VK). Empty when there are none."
          },
          "thread_delay": {
            "type": "integer",
            "description": "Minutes between the messages of the thread; 0 — at once."
          },
          "link": {
            "type": [
              "string",
              "null"
            ]
          },
          "media": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Id in the media library; null when the file is not there.",
                  "examples": [
                    "fil-5f1c0e2a9b"
                  ]
                },
                "url": {
                  "type": "string",
                  "examples": [
                    "https://followers.vc/storage/files/photo.png"
                  ]
                },
                "alt": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Description of the file for screen readers, sent to the networks that take one (Bluesky, Mastodon, X, Pinterest). Null when there is none."
                }
              },
              "required": [
                "id",
                "url",
                "alt"
              ],
              "additionalProperties": false
            },
            "description": "Files of the post, in order."
          },
          "options": {
            "type": "object",
            "description": "Network-specific options as given.",
            "examples": [
              {}
            ]
          },
          "status": {
            "type": "string",
            "description": "`reconnect_wait` — scheduled, the channel needs reconnecting. `unknown` — a status the API does not name.",
            "examples": [
              "scheduled"
            ],
            "enum": [
              "draft",
              "scheduled",
              "reconnect_wait",
              "published",
              "failed",
              "unknown"
            ]
          },
          "publish_at": {
            "type": [
              "integer",
              "null"
            ],
            "description": "When the post is (or was) due; null for a draft. Unix seconds.",
            "examples": [
              1793000000
            ]
          },
          "retry_at": {
            "type": [
              "integer",
              "null"
            ],
            "description": "A transient error: the queue tries again at this time. Unix seconds.",
            "examples": [
              1793000000
            ]
          },
          "attempts": {
            "type": "integer",
            "description": "Publishing attempts so far.",
            "examples": [
              0
            ]
          },
          "result": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/PostResult"
              },
              {
                "type": "null"
              }
            ]
          },
          "url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Address of the published post, when the network gives one."
          },
          "metrics": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/PostMetrics"
              },
              {
                "type": "null"
              }
            ],
            "description": "How the published post is doing, as the network last told it. Read every hour in the first two days after publication, then twice a day until the post is a month old. X, Bluesky, Mastodon, Threads, LinkedIn and Facebook pages, Instagram, YouTube, TikTok, Reddit and VK; null until the network has told any counter."
          },
          "created_at": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Creation time. Unix seconds.",
            "examples": [
              1793000000
            ]
          }
        },
        "required": [
          "id",
          "channel",
          "type",
          "caption",
          "thread",
          "thread_delay",
          "link",
          "media",
          "options",
          "status",
          "publish_at",
          "retry_at",
          "attempts",
          "result",
          "url",
          "metrics",
          "created_at"
        ],
        "additionalProperties": false
      },
      "PostDetail": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Public id of the post.",
            "examples": [
              "kQ7x9a1b2c3d"
            ]
          },
          "channel": {
            "type": "object",
            "properties": {
              "id": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Public id of the channel; null when it was removed.",
                "examples": [
                  "a8d3f0c2e1b94f7d"
                ]
              },
              "network": {
                "type": [
                  "string",
                  "null"
                ],
                "examples": [
                  "telegram"
                ]
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ],
                "examples": [
                  "Acme news"
                ]
              }
            },
            "required": [
              "id",
              "network",
              "name"
            ],
            "additionalProperties": false
          },
          "type": {
            "type": "string",
            "description": "text, link or media (older posts may carry other types).",
            "examples": [
              "text"
            ]
          },
          "caption": {
            "type": "string",
            "examples": [
              "Hello from the API"
            ]
          },
          "thread": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Messages published after the post: a thread of replies where the network has threads (X, Bluesky, Mastodon, Threads, Telegram, Discord, Slack), comments under the post where it has comments (Facebook, Instagram, Reddit, VK). Empty when there are none."
          },
          "thread_delay": {
            "type": "integer",
            "description": "Minutes between the messages of the thread; 0 — at once."
          },
          "link": {
            "type": [
              "string",
              "null"
            ]
          },
          "media": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Id in the media library; null when the file is not there.",
                  "examples": [
                    "fil-5f1c0e2a9b"
                  ]
                },
                "url": {
                  "type": "string",
                  "examples": [
                    "https://followers.vc/storage/files/photo.png"
                  ]
                },
                "alt": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Description of the file for screen readers, sent to the networks that take one (Bluesky, Mastodon, X, Pinterest). Null when there is none."
                }
              },
              "required": [
                "id",
                "url",
                "alt"
              ],
              "additionalProperties": false
            },
            "description": "Files of the post, in order."
          },
          "options": {
            "type": "object",
            "description": "Network-specific options as given.",
            "examples": [
              {}
            ]
          },
          "status": {
            "type": "string",
            "description": "`reconnect_wait` — scheduled, the channel needs reconnecting. `unknown` — a status the API does not name.",
            "examples": [
              "scheduled"
            ],
            "enum": [
              "draft",
              "scheduled",
              "reconnect_wait",
              "published",
              "failed",
              "unknown"
            ]
          },
          "publish_at": {
            "type": [
              "integer",
              "null"
            ],
            "description": "When the post is (or was) due; null for a draft. Unix seconds.",
            "examples": [
              1793000000
            ]
          },
          "retry_at": {
            "type": [
              "integer",
              "null"
            ],
            "description": "A transient error: the queue tries again at this time. Unix seconds.",
            "examples": [
              1793000000
            ]
          },
          "attempts": {
            "type": "integer",
            "description": "Publishing attempts so far.",
            "examples": [
              0
            ]
          },
          "result": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/PostResult"
              },
              {
                "type": "null"
              }
            ]
          },
          "url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Address of the published post, when the network gives one."
          },
          "metrics": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/PostMetrics"
              },
              {
                "type": "null"
              }
            ],
            "description": "How the published post is doing, as the network last told it. Read every hour in the first two days after publication, then twice a day until the post is a month old. X, Bluesky, Mastodon, Threads, LinkedIn and Facebook pages, Instagram, YouTube, TikTok, Reddit and VK; null until the network has told any counter."
          },
          "created_at": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Creation time. Unix seconds.",
            "examples": [
              1793000000
            ]
          },
          "attempt_log": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "attempt": {
                  "type": "integer",
                  "examples": [
                    1
                  ]
                },
                "outcome": {
                  "type": "string",
                  "description": "published, retry, failed or reconnect.",
                  "examples": [
                    "retry"
                  ]
                },
                "error_class": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "transient, uncertain, auth or permanent.",
                  "examples": [
                    "transient"
                  ]
                },
                "message": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "examples": [
                    "Rate limit"
                  ]
                },
                "at": {
                  "type": [
                    "integer",
                    "null"
                  ],
                  "description": "Time of the attempt. Unix seconds.",
                  "examples": [
                    1793000000
                  ]
                }
              },
              "required": [
                "attempt",
                "outcome",
                "error_class",
                "message",
                "at"
              ],
              "additionalProperties": false
            },
            "description": "Every publishing attempt, oldest first."
          }
        },
        "required": [
          "id",
          "channel",
          "type",
          "caption",
          "thread",
          "thread_delay",
          "link",
          "media",
          "options",
          "status",
          "publish_at",
          "retry_at",
          "attempts",
          "result",
          "url",
          "metrics",
          "created_at",
          "attempt_log"
        ],
        "additionalProperties": false
      },
      "PostResult": {
        "type": "object",
        "description": "What the last publishing attempt returned.",
        "properties": {
          "message": {
            "type": [
              "string",
              "null"
            ],
            "description": "Message of the network or of followers.vc.",
            "examples": [
              "Bad token"
            ]
          },
          "error_class": {
            "type": [
              "string",
              "null"
            ],
            "description": "transient, uncertain (the post may have been published), auth or permanent; null — no error.",
            "examples": [
              "permanent"
            ]
          },
          "social_id": {
            "type": [
              "string",
              "integer",
              "null"
            ],
            "description": "Id of the post on the network.",
            "examples": [
              null
            ]
          },
          "thread": {
            "type": "array",
            "description": "What came of each message of the thread, in order; publishing stops at the first refusal.",
            "items": {
              "type": "object",
              "properties": {
                "social_id": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Id of the message in the network."
                },
                "url": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Address of the message, when the network gives one."
                },
                "error": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Why the network refused the message, or why the network takes no thread."
                }
              },
              "required": [
                "social_id",
                "url",
                "error"
              ],
              "additionalProperties": false
            }
          }
        },
        "required": [
          "message",
          "error_class",
          "social_id",
          "thread"
        ],
        "additionalProperties": false
      },
      "PostMetrics": {
        "type": "object",
        "description": "Counters of a published post. A counter the network does not keep is null: Bluesky, Mastodon and Reddit count no views, only X, Bluesky, Mastodon and Threads count quotes.",
        "properties": {
          "likes": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Likes, favourites, reactions.",
            "examples": [
              42
            ]
          },
          "reposts": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Reposts, retweets, boosts, shares.",
            "examples": [
              7
            ]
          },
          "comments": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Replies and comments.",
            "examples": [
              5
            ]
          },
          "quotes": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Reposts with own text.",
            "examples": [
              1
            ]
          },
          "views": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Views or impressions.",
            "examples": [
              1800
            ]
          },
          "updated_at": {
            "type": "integer",
            "description": "When the counters were read. Unix seconds.",
            "examples": [
              1793003600
            ]
          }
        },
        "required": [
          "likes",
          "reposts",
          "comments",
          "quotes",
          "views",
          "updated_at"
        ]
      },
      "Notification": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Public id of the notification.",
            "examples": [
              "n-7d1e2f"
            ]
          },
          "message": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "Your post on Acme news has been published."
            ]
          },
          "url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where the notification leads."
          },
          "source": {
            "type": "string",
            "description": "`auto` — sent by followers.vc, `manual` — by the administrators.",
            "examples": [
              "auto"
            ],
            "enum": [
              "auto",
              "manual"
            ]
          },
          "type": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "news"
            ]
          },
          "read": {
            "type": "boolean",
            "examples": [
              false
            ]
          },
          "created_at": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Time of the notification. Unix seconds.",
            "examples": [
              1793000000
            ]
          }
        },
        "required": [
          "id",
          "message",
          "url",
          "source",
          "type",
          "read",
          "created_at"
        ],
        "additionalProperties": false
      },
      "UnreadCount": {
        "type": "object",
        "properties": {
          "unread": {
            "type": "integer",
            "examples": [
              3
            ]
          }
        },
        "required": [
          "unread"
        ],
        "additionalProperties": false
      },
      "AnalyticsNetwork": {
        "type": "object",
        "properties": {
          "network": {
            "type": "string",
            "examples": [
              "facebook"
            ]
          },
          "name": {
            "type": "string",
            "examples": [
              "Facebook"
            ]
          },
          "channels": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "description": "Public id of the channel.",
                  "examples": [
                    "a8d3f0c2e1b94f7d"
                  ]
                },
                "name": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "examples": [
                    "Acme page"
                  ]
                },
                "username": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "examples": [
                    "acme"
                  ]
                },
                "avatar": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              },
              "required": [
                "id",
                "name",
                "username",
                "avatar"
              ],
              "additionalProperties": false
            }
          }
        },
        "required": [
          "network",
          "name",
          "channels"
        ],
        "additionalProperties": false
      },
      "AnalyticsReport": {
        "type": "object",
        "properties": {
          "data": {
            "type": [
              "object",
              "array"
            ],
            "description": "The report as the service of the network builds it (the same numbers the cabinet shows). Its fields differ per network.",
            "examples": [
              {
                "followers": 120,
                "chart": [
                  1,
                  2,
                  3
                ]
              }
            ]
          },
          "meta": {
            "type": "object",
            "properties": {
              "channel": {
                "type": "string",
                "description": "Public id of the channel.",
                "examples": [
                  "a8d3f0c2e1b94f7d"
                ]
              },
              "from": {
                "type": "string",
                "description": "Start of the range, ISO 8601.",
                "examples": [
                  "2026-09-16T00:00:00+00:00"
                ],
                "format": "date-time"
              },
              "to": {
                "type": "string",
                "description": "End of the range, ISO 8601.",
                "examples": [
                  "2026-10-16T09:30:00+00:00"
                ],
                "format": "date-time"
              }
            },
            "required": [
              "channel",
              "from",
              "to"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "data",
          "meta"
        ],
        "additionalProperties": false
      },
      "WebhookPost": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Public id of the post.",
            "examples": [
              "kQ7x9a1"
            ]
          },
          "status": {
            "type": "string",
            "examples": [
              "published"
            ],
            "enum": [
              "scheduled",
              "published",
              "failed"
            ]
          },
          "type": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "media"
            ]
          },
          "time_post": {
            "type": [
              "string",
              "null"
            ],
            "description": "When the post is due, ISO 8601 UTC.",
            "examples": [
              "2026-10-16T09:30:00Z"
            ],
            "format": "date-time"
          },
          "url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Set for a published post when the network returns a link.",
            "examples": [
              "https://t.me/example/123"
            ]
          }
        },
        "required": [
          "id",
          "status",
          "type",
          "time_post",
          "url"
        ],
        "additionalProperties": false
      },
      "WebhookChannel": {
        "type": "object",
        "properties": {
          "id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Public id of the channel.",
            "examples": [
              "a8d3f0c2"
            ]
          },
          "network": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "telegram"
            ]
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "Example"
            ]
          }
        },
        "required": [
          "id",
          "network",
          "name"
        ],
        "additionalProperties": false
      },
      "WebhookPostScheduled": {
        "type": "object",
        "description": "Body of the `post.scheduled` request.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Id of the event: the same for retries and manual redeliveries — use it to skip duplicates.",
            "examples": [
              "5b0c2a4e-1f0e-4c55-9a43-2d8c7e1b6f10"
            ],
            "format": "uuid"
          },
          "event": {
            "const": "post.scheduled",
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "description": "Time of the event, ISO 8601 UTC.",
            "examples": [
              "2026-10-16T09:30:00Z"
            ],
            "format": "date-time"
          },
          "data": {
            "type": "object",
            "properties": {
              "post": {
                "$ref": "#/components/schemas/WebhookPost"
              },
              "channel": {
                "$ref": "#/components/schemas/WebhookChannel"
              }
            },
            "required": [
              "post",
              "channel"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "id",
          "event",
          "created_at",
          "data"
        ],
        "additionalProperties": false,
        "examples": [
          {
            "id": "5b0c2a4e-1f0e-4c55-9a43-2d8c7e1b6f10",
            "event": "post.scheduled",
            "created_at": "2026-10-16T09:30:00Z",
            "data": {
              "post": {
                "id": "kQ7x9a1",
                "status": "scheduled",
                "type": "media",
                "time_post": "2026-10-16T09:30:00Z",
                "url": null
              },
              "channel": {
                "id": "a8d3f0c2",
                "network": "telegram",
                "name": "Example"
              }
            }
          }
        ]
      },
      "WebhookPostPublished": {
        "type": "object",
        "description": "Body of the `post.published` request.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Id of the event: the same for retries and manual redeliveries — use it to skip duplicates.",
            "examples": [
              "5b0c2a4e-1f0e-4c55-9a43-2d8c7e1b6f10"
            ],
            "format": "uuid"
          },
          "event": {
            "const": "post.published",
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "description": "Time of the event, ISO 8601 UTC.",
            "examples": [
              "2026-10-16T09:30:00Z"
            ],
            "format": "date-time"
          },
          "data": {
            "type": "object",
            "properties": {
              "post": {
                "$ref": "#/components/schemas/WebhookPost"
              },
              "channel": {
                "$ref": "#/components/schemas/WebhookChannel"
              }
            },
            "required": [
              "post",
              "channel"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "id",
          "event",
          "created_at",
          "data"
        ],
        "additionalProperties": false,
        "examples": [
          {
            "id": "5b0c2a4e-1f0e-4c55-9a43-2d8c7e1b6f10",
            "event": "post.published",
            "created_at": "2026-10-16T09:30:00Z",
            "data": {
              "post": {
                "id": "kQ7x9a1",
                "status": "published",
                "type": "media",
                "time_post": "2026-10-16T09:30:00Z",
                "url": "https://t.me/example/123"
              },
              "channel": {
                "id": "a8d3f0c2",
                "network": "telegram",
                "name": "Example"
              }
            }
          }
        ]
      },
      "WebhookPostFailed": {
        "type": "object",
        "description": "Body of the `post.failed` request.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Id of the event: the same for retries and manual redeliveries — use it to skip duplicates.",
            "examples": [
              "5b0c2a4e-1f0e-4c55-9a43-2d8c7e1b6f10"
            ],
            "format": "uuid"
          },
          "event": {
            "const": "post.failed",
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "description": "Time of the event, ISO 8601 UTC.",
            "examples": [
              "2026-10-16T09:30:00Z"
            ],
            "format": "date-time"
          },
          "data": {
            "type": "object",
            "properties": {
              "post": {
                "$ref": "#/components/schemas/WebhookPost"
              },
              "channel": {
                "$ref": "#/components/schemas/WebhookChannel"
              },
              "error": {
                "type": "object",
                "properties": {
                  "class": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "permanent, transient, uncertain (the post may have been published) or auth.",
                    "examples": [
                      "permanent"
                    ],
                    "enum": [
                      "permanent",
                      "transient",
                      "uncertain",
                      "auth",
                      null
                    ]
                  },
                  "message": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "At most 500 characters.",
                    "examples": [
                      "The token was revoked."
                    ]
                  }
                },
                "required": [
                  "class",
                  "message"
                ],
                "additionalProperties": false
              }
            },
            "required": [
              "post",
              "channel",
              "error"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "id",
          "event",
          "created_at",
          "data"
        ],
        "additionalProperties": false,
        "examples": [
          {
            "id": "5b0c2a4e-1f0e-4c55-9a43-2d8c7e1b6f10",
            "event": "post.failed",
            "created_at": "2026-10-16T09:30:00Z",
            "data": {
              "post": {
                "id": "kQ7x9a1",
                "status": "failed",
                "type": "media",
                "time_post": "2026-10-16T09:30:00Z",
                "url": null
              },
              "channel": {
                "id": "a8d3f0c2",
                "network": "telegram",
                "name": "Example"
              },
              "error": {
                "class": "permanent",
                "message": "The token was revoked."
              }
            }
          }
        ]
      },
      "WebhookChannelDisconnected": {
        "type": "object",
        "description": "Body of the `channel.disconnected` request.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Id of the event: the same for retries and manual redeliveries — use it to skip duplicates.",
            "examples": [
              "5b0c2a4e-1f0e-4c55-9a43-2d8c7e1b6f10"
            ],
            "format": "uuid"
          },
          "event": {
            "const": "channel.disconnected",
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "description": "Time of the event, ISO 8601 UTC.",
            "examples": [
              "2026-10-16T09:30:00Z"
            ],
            "format": "date-time"
          },
          "data": {
            "type": "object",
            "properties": {
              "channel": {
                "$ref": "#/components/schemas/WebhookChannel"
              },
              "reason": {
                "type": "string",
                "description": "Why the channel must be reconnected; at most 500 characters.",
                "examples": [
                  "The token was revoked."
                ]
              }
            },
            "required": [
              "channel",
              "reason"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "id",
          "event",
          "created_at",
          "data"
        ],
        "additionalProperties": false,
        "examples": [
          {
            "id": "5b0c2a4e-1f0e-4c55-9a43-2d8c7e1b6f10",
            "event": "channel.disconnected",
            "created_at": "2026-10-16T09:30:00Z",
            "data": {
              "channel": {
                "id": "a8d3f0c2",
                "network": "telegram",
                "name": "Example"
              },
              "reason": "The token was revoked."
            }
          }
        ]
      },
      "WebhookApprovalApproved": {
        "type": "object",
        "description": "Body of the `approval.approved` request.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Id of the event: the same for retries and manual redeliveries — use it to skip duplicates.",
            "examples": [
              "5b0c2a4e-1f0e-4c55-9a43-2d8c7e1b6f10"
            ],
            "format": "uuid"
          },
          "event": {
            "const": "approval.approved",
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "description": "Time of the event, ISO 8601 UTC.",
            "examples": [
              "2026-10-16T09:30:00Z"
            ],
            "format": "date-time"
          },
          "data": {
            "type": "object",
            "properties": {
              "approval": {
                "type": "object",
                "description": "The decision: approved.",
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Id of the approval request: the same for every decision on it.",
                    "examples": [
                      "Zp4r2Lx8"
                    ]
                  },
                  "status": {
                    "type": "string",
                    "const": "approved"
                  },
                  "decided_by": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "The name the reviewer gave on the approval page.",
                    "examples": [
                      "Anna"
                    ]
                  },
                  "decided_at": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Time of the decision, ISO 8601 UTC.",
                    "format": "date-time",
                    "examples": [
                      "2026-10-16T09:30:00Z"
                    ]
                  },
                  "comment": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "What the reviewer wrote with the decision; at most 500 characters.",
                    "examples": [
                      "Looks good."
                    ]
                  }
                },
                "required": [
                  "id",
                  "status",
                  "decided_by",
                  "decided_at",
                  "comment"
                ],
                "additionalProperties": false
              },
              "posts": {
                "type": "array",
                "description": "The posts of the approval request, one per channel.",
                "items": {
                  "type": "object",
                  "properties": {
                    "post": {
                      "$ref": "#/components/schemas/WebhookPost"
                    },
                    "channel": {
                      "$ref": "#/components/schemas/WebhookChannel"
                    },
                    "error": {
                      "type": "object",
                      "properties": {
                        "class": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "permanent, transient, uncertain (the post may have been published) or auth.",
                          "examples": [
                            "permanent"
                          ],
                          "enum": [
                            "permanent",
                            "transient",
                            "uncertain",
                            "auth",
                            null
                          ]
                        },
                        "message": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "At most 500 characters.",
                          "examples": [
                            "The token was revoked."
                          ]
                        }
                      },
                      "required": [
                        "class",
                        "message"
                      ],
                      "additionalProperties": false,
                      "description": "Only for a post that has failed."
                    }
                  },
                  "required": [
                    "post",
                    "channel"
                  ],
                  "additionalProperties": false
                }
              }
            },
            "required": [
              "approval",
              "posts"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "id",
          "event",
          "created_at",
          "data"
        ],
        "additionalProperties": false,
        "examples": [
          {
            "id": "5b0c2a4e-1f0e-4c55-9a43-2d8c7e1b6f10",
            "event": "approval.approved",
            "created_at": "2026-10-16T09:30:00Z",
            "data": {
              "approval": {
                "id": "Zp4r2Lx8",
                "status": "approved",
                "decided_by": "Anna",
                "decided_at": "2026-10-16T09:30:00Z",
                "comment": "Looks good."
              },
              "posts": [
                {
                  "post": {
                    "id": "kQ7x9a1",
                    "status": "scheduled",
                    "type": "media",
                    "time_post": "2026-10-17T09:00:00Z",
                    "url": null
                  },
                  "channel": {
                    "id": "a8d3f0c2",
                    "network": "telegram",
                    "name": "Example"
                  }
                }
              ]
            }
          }
        ]
      },
      "WebhookApprovalChangesRequested": {
        "type": "object",
        "description": "Body of the `approval.changes_requested` request.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Id of the event: the same for retries and manual redeliveries — use it to skip duplicates.",
            "examples": [
              "5b0c2a4e-1f0e-4c55-9a43-2d8c7e1b6f10"
            ],
            "format": "uuid"
          },
          "event": {
            "const": "approval.changes_requested",
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "description": "Time of the event, ISO 8601 UTC.",
            "examples": [
              "2026-10-16T09:30:00Z"
            ],
            "format": "date-time"
          },
          "data": {
            "type": "object",
            "properties": {
              "approval": {
                "type": "object",
                "description": "The decision: changes requested.",
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Id of the approval request: the same for every decision on it.",
                    "examples": [
                      "Zp4r2Lx8"
                    ]
                  },
                  "status": {
                    "type": "string",
                    "const": "changes_requested"
                  },
                  "decided_by": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "The name the reviewer gave on the approval page.",
                    "examples": [
                      "Anna"
                    ]
                  },
                  "decided_at": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Time of the decision, ISO 8601 UTC.",
                    "format": "date-time",
                    "examples": [
                      "2026-10-16T09:30:00Z"
                    ]
                  },
                  "comment": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "What the reviewer wrote with the decision; at most 500 characters.",
                    "examples": [
                      "Please shorten the text."
                    ]
                  }
                },
                "required": [
                  "id",
                  "status",
                  "decided_by",
                  "decided_at",
                  "comment"
                ],
                "additionalProperties": false
              },
              "posts": {
                "type": "array",
                "description": "The posts of the approval request, one per channel.",
                "items": {
                  "type": "object",
                  "properties": {
                    "post": {
                      "$ref": "#/components/schemas/WebhookPost"
                    },
                    "channel": {
                      "$ref": "#/components/schemas/WebhookChannel"
                    },
                    "error": {
                      "type": "object",
                      "properties": {
                        "class": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "permanent, transient, uncertain (the post may have been published) or auth.",
                          "examples": [
                            "permanent"
                          ],
                          "enum": [
                            "permanent",
                            "transient",
                            "uncertain",
                            "auth",
                            null
                          ]
                        },
                        "message": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "At most 500 characters.",
                          "examples": [
                            "The token was revoked."
                          ]
                        }
                      },
                      "required": [
                        "class",
                        "message"
                      ],
                      "additionalProperties": false,
                      "description": "Only for a post that has failed."
                    }
                  },
                  "required": [
                    "post",
                    "channel"
                  ],
                  "additionalProperties": false
                }
              }
            },
            "required": [
              "approval",
              "posts"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "id",
          "event",
          "created_at",
          "data"
        ],
        "additionalProperties": false,
        "examples": [
          {
            "id": "5b0c2a4e-1f0e-4c55-9a43-2d8c7e1b6f10",
            "event": "approval.changes_requested",
            "created_at": "2026-10-16T09:30:00Z",
            "data": {
              "approval": {
                "id": "Zp4r2Lx8",
                "status": "changes_requested",
                "decided_by": "Anna",
                "decided_at": "2026-10-16T09:30:00Z",
                "comment": "Please shorten the text."
              },
              "posts": [
                {
                  "post": {
                    "id": "kQ7x9a1",
                    "status": "scheduled",
                    "type": "media",
                    "time_post": "2026-10-17T09:00:00Z",
                    "url": null
                  },
                  "channel": {
                    "id": "a8d3f0c2",
                    "network": "telegram",
                    "name": "Example"
                  }
                }
              ]
            }
          }
        ]
      },
      "WebhookPing": {
        "type": "object",
        "description": "Body of the `ping` request.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Id of the event: the same for retries and manual redeliveries — use it to skip duplicates.",
            "examples": [
              "5b0c2a4e-1f0e-4c55-9a43-2d8c7e1b6f10"
            ],
            "format": "uuid"
          },
          "event": {
            "const": "ping",
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "description": "Time of the event, ISO 8601 UTC.",
            "examples": [
              "2026-10-16T09:30:00Z"
            ],
            "format": "date-time"
          },
          "data": {
            "type": "object",
            "properties": {
              "webhook": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "examples": [
                      "Xh2…"
                    ]
                  },
                  "events": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "post.scheduled",
                        "post.published",
                        "post.failed",
                        "channel.disconnected"
                      ]
                    }
                  }
                },
                "required": [
                  "id",
                  "events"
                ],
                "additionalProperties": false
              },
              "message": {
                "type": "string",
                "examples": [
                  "Test delivery from followers.vc"
                ]
              }
            },
            "required": [
              "webhook",
              "message"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "id",
          "event",
          "created_at",
          "data"
        ],
        "additionalProperties": false,
        "examples": [
          {
            "id": "5b0c2a4e-1f0e-4c55-9a43-2d8c7e1b6f10",
            "event": "ping",
            "created_at": "2026-10-16T09:30:00Z",
            "data": {
              "webhook": {
                "id": "Xh2k9",
                "events": [
                  "post.published"
                ]
              },
              "message": "Test delivery from followers.vc"
            }
          }
        ]
      }
    }
  },
  "x-error-codes": {
    "unauthenticated": {
      "status": 401,
      "description": "No key, an unknown or revoked key, or its author is no longer active in the team."
    },
    "api_not_in_plan": {
      "status": 403,
      "description": "The plan of the team does not include API access."
    },
    "plan_expired": {
      "status": 403,
      "description": "The plan of the team owner has expired."
    },
    "module_not_in_plan": {
      "status": 403,
      "description": "The plan of the team, or the role of the key author in it, does not include this resource."
    },
    "insufficient_scope": {
      "status": 403,
      "description": "The key does not carry the scope of the operation."
    },
    "storage_limit": {
      "status": 403,
      "description": "The media storage of the plan is full."
    },
    "file_too_large": {
      "status": 403,
      "description": "The file is larger than the plan allows."
    },
    "post_quota": {
      "status": 403,
      "description": "The post quota of the plan is used up."
    },
    "analytics_not_in_plan": {
      "status": 403,
      "description": "The plan does not include analytics for the network of this channel."
    },
    "not_found": {
      "status": 404,
      "description": "No such record in the team of the key (or among the notifications of its author)."
    },
    "analytics_unavailable": {
      "status": 404,
      "description": "The network of the channel has no analytics."
    },
    "method_not_allowed": {
      "status": 405,
      "description": "The path exists, the method does not."
    },
    "not_editable": {
      "status": 409,
      "description": "Only a scheduled post that is not being published right now can be changed."
    },
    "not_deletable": {
      "status": 409,
      "description": "Only a draft, scheduled or failed post that is not being published right now can be deleted."
    },
    "not_retryable": {
      "status": 409,
      "description": "Only a failed post can be published again."
    },
    "not_linkable": {
      "status": 409,
      "description": "Only a failed post with an uncertain error can be linked to its post in the network."
    },
    "idempotency_key_in_use": {
      "status": 409,
      "description": "A request with this Idempotency-Key is still being processed."
    },
    "payload_too_large": {
      "status": 413,
      "description": "The request body is larger than the server accepts."
    },
    "validation_failed": {
      "status": 422,
      "description": "The request is invalid; `fields` names each field with its messages."
    },
    "idempotency_key_reused": {
      "status": 422,
      "description": "This Idempotency-Key was already used with a different request."
    },
    "not_saved": {
      "status": 422,
      "description": "The post was not saved for a reason that is not about a field."
    },
    "rate_limited": {
      "status": 429,
      "description": "Too many requests for this key; wait `Retry-After` seconds."
    },
    "server_error": {
      "status": 500,
      "description": "Something failed on our side. Details are never sent."
    },
    "provider_error": {
      "status": 502,
      "description": "The social network could not be asked for the report. Try again later."
    }
  }
}