{
  "openapi": "3.1.0",
  "info": {
    "title": "Schedulala Developer API",
    "version": "1.6",
    "description": "Schedule and publish posts to 12 social platforms (Twitter/X, Instagram, LinkedIn, Facebook, YouTube, Bluesky, TikTok, Telegram, Threads, Pinterest, Mastodon, Google Business Profile), pull post and account analytics, manage engagement (comments, mentions, replies), and run social listening feeds. Mastodon accounts are connected on the Schedulala dashboard (per-instance OAuth), then posted to via the API like any other platform. Google Business Profile posts target selected business locations (accountId is the locationId) and support STANDARD, EVENT, and OFFER post types with call-to-action buttons via platformSettings; note Google provides no post-level analytics for Business Profile. All endpoints return a JSON envelope: successful responses include success: true plus the documented data fields; errors include success: false and an error object with code and message. Full documentation lives at https://schedulala.com/developers/docs",
    "contact": {
      "email": "dev@schedulala.com"
    }
  },
  "servers": [
    {
      "url": "https://schedulala.com"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Posts",
      "description": "Create, list, update, retry, validate, and bulk-create posts"
    },
    {
      "name": "Threads",
      "description": "Multi-post threads on Twitter/X, Threads, and Bluesky"
    },
    {
      "name": "Accounts",
      "description": "Connected social accounts and connection health"
    },
    {
      "name": "Media",
      "description": "Upload images and videos for use in posts"
    },
    {
      "name": "Analytics",
      "description": "Post, follower, and account analytics"
    },
    {
      "name": "Engagement",
      "description": "Comments, mentions, replies, moderation, and transcripts"
    },
    {
      "name": "Feeds",
      "description": "Social listening: keyword search, profile lookup, saved keywords"
    },
    {
      "name": "Usage",
      "description": "Plan usage and quota"
    },
    {
      "name": "Billing",
      "description": "API plan details"
    }
  ],
  "paths": {
    "/api/v1/posts": {
      "post": {
        "operationId": "createPost",
        "tags": [
          "Posts"
        ],
        "summary": "Create a post",
        "description": "Create a post on one or more platforms. Exactly one scheduling mode is required: publishNow (publish immediately), queue (auto-schedule into the next configured queue slot), scheduledFor (specific time, at least 5 minutes and at most 90 days in the future), or status set to 'draft'. Supports an optional Idempotency-Key header.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PostCreateRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Post created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "post": {
                      "$ref": "#/components/schemas/Post"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "get": {
        "operationId": "listPosts",
        "tags": [
          "Posts"
        ],
        "summary": "List posts",
        "description": "List posts created through the API, newest scheduled first. Supports status, platform, brandId, and date range filters with page-based pagination.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "description": "Filter by post status, for example scheduled, posted, failed, draft, cancelled.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "platform",
            "in": "query",
            "description": "Filter to posts targeting this platform.",
            "schema": {
              "$ref": "#/components/schemas/PlatformName"
            }
          },
          {
            "name": "brandId",
            "in": "query",
            "description": "Filter by brand (profile) id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "after",
            "in": "query",
            "description": "Only posts scheduled at or after this ISO 8601 date.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "before",
            "in": "query",
            "description": "Only posts scheduled at or before this ISO 8601 date.",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Limit"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of posts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "posts": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Post"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/PagePagination"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/posts/{id}": {
      "get": {
        "operationId": "getPost",
        "tags": [
          "Posts"
        ],
        "summary": "Get a post",
        "parameters": [
          {
            "$ref": "#/components/parameters/PostId"
          }
        ],
        "responses": {
          "200": {
            "description": "The post",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "post": {
                      "$ref": "#/components/schemas/Post"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "operationId": "updatePost",
        "tags": [
          "Posts"
        ],
        "summary": "Update a post",
        "description": "Update a draft or scheduled post. Posts that are processing, posted, failed, or cancelled cannot be updated.",
        "parameters": [
          {
            "$ref": "#/components/parameters/PostId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "content": {
                    "type": "string",
                    "description": "New post text."
                  },
                  "platforms": {
                    "type": "array",
                    "description": "Replace the target platforms. Strings or objects with platform and accountId.",
                    "items": {
                      "$ref": "#/components/schemas/PlatformSelector"
                    }
                  },
                  "mediaItems": {
                    "type": "array",
                    "description": "Replace all media. Pass an empty array to remove media.",
                    "items": {
                      "$ref": "#/components/schemas/MediaItemInput"
                    }
                  },
                  "scheduledFor": {
                    "type": "string",
                    "format": "date-time",
                    "description": "New schedule time. At least 5 minutes and at most 90 days in the future."
                  },
                  "timezone": {
                    "type": "string",
                    "description": "IANA timezone name."
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "draft",
                      "scheduled"
                    ],
                    "description": "Switch between draft and scheduled."
                  },
                  "platformSettings": {
                    "type": "object",
                    "description": "Per-platform settings keyed by platform name. A content field inside overrides the post text for that platform.",
                    "additionalProperties": {
                      "type": "object",
                      "additionalProperties": true
                    }
                  },
                  "firstComment": {
                    "type": "string",
                    "description": "Shorthand: auto-post this text as the first comment on the main post right after publishing (equivalent to firstComments: [{target: 0, text}]). Supported platforms: linkedin, facebook, instagram, youtube, twitter, threads, bluesky, mastodon. Text and links only, no media."
                  },
                  "firstComments": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/FirstCommentInput"
                    },
                    "description": "Replace ALL auto-posted first comments. An EMPTY array clears pending comments. Auto-posted first comments. Limits: instagram 1000 UTF-8 bytes, linkedin 1500, twitter 280, bluesky 300, threads 500, youtube 10000 characters; facebook/mastodon no fixed limit. Not supported on tiktok, pinterest, telegram, google-business (selecting only unsupported platforms is a validation error). A failed comment never fails the post — check platforms[].firstComments on the post for delivery status."
                  }
                },
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated post",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "post": {
                      "$ref": "#/components/schemas/Post"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "deletePost",
        "tags": [
          "Posts"
        ],
        "summary": "Delete or cancel a post",
        "description": "Drafts are hard-deleted. Scheduled posts are cancelled (soft delete). Posts that are processing or already posted cannot be deleted.",
        "parameters": [
          {
            "$ref": "#/components/parameters/PostId"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted or cancelled",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "deleted": {
                      "type": "boolean"
                    },
                    "id": {
                      "type": "string"
                    },
                    "method": {
                      "type": "string",
                      "description": "Set to 'cancelled' when a scheduled post was soft-cancelled instead of hard-deleted."
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/posts/{id}/retry": {
      "post": {
        "operationId": "retryPost",
        "tags": [
          "Posts"
        ],
        "summary": "Retry failed platforms of a post",
        "description": "Re-dispatch the failed platforms of a post. Optionally limit the retry to specific platforms or delay it by a number of seconds.",
        "parameters": [
          {
            "$ref": "#/components/parameters/PostId"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "platforms": {
                    "type": "array",
                    "description": "Only retry these platforms. Defaults to all failed platforms.",
                    "items": {
                      "$ref": "#/components/schemas/PlatformName"
                    }
                  },
                  "delay": {
                    "type": "number",
                    "description": "Seconds to wait before retrying. Default 0 (immediate)."
                  }
                },
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Retry dispatched",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "post": {
                      "$ref": "#/components/schemas/Post"
                    },
                    "retriedPlatforms": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/posts/validate": {
      "post": {
        "operationId": "validatePost",
        "tags": [
          "Posts"
        ],
        "summary": "Validate post content",
        "description": "Lightweight per-platform content validation (character limits, media requirements). Does not create anything and does not consume post quota.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "platforms"
                ],
                "properties": {
                  "content": {
                    "type": "string"
                  },
                  "platforms": {
                    "type": "array",
                    "minItems": 1,
                    "items": {
                      "$ref": "#/components/schemas/PlatformName"
                    }
                  },
                  "mediaItems": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/MediaItemInput"
                    }
                  },
                  "platformSettings": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "object",
                      "additionalProperties": true
                    }
                  },
                  "firstComment": {
                    "type": "string",
                    "description": "Shorthand: auto-post this text as the first comment on the main post right after publishing (equivalent to firstComments: [{target: 0, text}]). Supported platforms: linkedin, facebook, instagram, youtube, twitter, threads, bluesky, mastodon. Text and links only, no media."
                  },
                  "firstComments": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/FirstCommentInput"
                    },
                    "description": "Auto-posted first comments. Limits: instagram 1000 UTF-8 bytes, linkedin 1500, twitter 280, bluesky 300, threads 500, youtube 10000 characters; facebook/mastodon no fixed limit. Not supported on tiktok, pinterest, telegram, google-business (selecting only unsupported platforms is a validation error). A failed comment never fails the post — check platforms[].firstComments on the post for delivery status."
                  }
                },
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Validation results per platform",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "validation": {
                      "$ref": "#/components/schemas/ValidationResult"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/posts/bulk": {
      "post": {
        "operationId": "bulkCreatePosts",
        "tags": [
          "Posts"
        ],
        "summary": "Create multiple posts",
        "description": "Create up to 25 posts in one request. Each entry accepts the same fields as createPost. Returns per-index results; some entries can succeed while others fail. Supports an optional Idempotency-Key header.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "posts"
                ],
                "properties": {
                  "posts": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 25,
                    "items": {
                      "$ref": "#/components/schemas/PostCreateRequest"
                    }
                  },
                  "timezone": {
                    "type": "string",
                    "description": "Default IANA timezone applied to posts that do not set their own."
                  }
                },
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Per-post results with a summary",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "index": {
                            "type": "integer"
                          },
                          "success": {
                            "type": "boolean"
                          },
                          "post": {
                            "$ref": "#/components/schemas/Post"
                          },
                          "error": {
                            "type": "object",
                            "properties": {
                              "code": {
                                "type": "string"
                              },
                              "message": {
                                "type": "string"
                              }
                            },
                            "additionalProperties": true
                          }
                        },
                        "additionalProperties": true
                      }
                    },
                    "summary": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "integer"
                        },
                        "succeeded": {
                          "type": "integer"
                        },
                        "failed": {
                          "type": "integer"
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/threads": {
      "post": {
        "operationId": "createThread",
        "tags": [
          "Threads"
        ],
        "summary": "Create a thread",
        "description": "Create a thread of 2 to 25 sequential posts. Threads are supported on twitter, threads, and bluesky only. Each thread post counts against the monthly post quota. Supports an optional Idempotency-Key header.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "posts",
                  "platforms"
                ],
                "properties": {
                  "posts": {
                    "type": "array",
                    "minItems": 2,
                    "maxItems": 25,
                    "description": "Ordered thread entries.",
                    "items": {
                      "type": "object",
                      "properties": {
                        "content": {
                          "type": "string"
                        },
                        "mediaItems": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/MediaItemInput"
                          }
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "platforms": {
                    "type": "array",
                    "minItems": 1,
                    "description": "Thread-capable platforms: twitter, threads, bluesky. Strings or objects with platform and accountId.",
                    "items": {
                      "oneOf": [
                        {
                          "type": "string",
                          "enum": [
                            "twitter",
                            "threads",
                            "bluesky"
                          ]
                        },
                        {
                          "type": "object",
                          "required": [
                            "platform"
                          ],
                          "properties": {
                            "platform": {
                              "type": "string",
                              "enum": [
                                "twitter",
                                "threads",
                                "bluesky"
                              ]
                            },
                            "accountId": {
                              "type": "string"
                            }
                          },
                          "additionalProperties": true
                        }
                      ]
                    }
                  },
                  "scheduledFor": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "publishNow": {
                    "type": "boolean"
                  },
                  "config": {
                    "type": "object",
                    "properties": {
                      "postDelay": {
                        "type": "integer",
                        "description": "Milliseconds between thread posts. Default 2000."
                      },
                      "continueOnError": {
                        "type": "boolean",
                        "description": "Keep posting later entries when one fails. Default false."
                      },
                      "maxRetries": {
                        "type": "integer",
                        "description": "Default 3."
                      }
                    },
                    "additionalProperties": true
                  },
                  "brandId": {
                    "type": "string"
                  },
                  "timezone": {
                    "type": "string"
                  },
                  "firstComment": {
                    "type": "string",
                    "description": "Shorthand: auto-post this text as a comment on the ROOT entry after the thread publishes (equivalent to firstComments: [{target: 0, text}])."
                  },
                  "firstComments": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/FirstCommentInput"
                    },
                    "description": "Auto-posted comments per thread entry: target 0 = entry 1 (the root), target i = entry i+1; max target = number of entries - 1. Thread-targeted comments (target > 0) deliver on twitter/threads/bluesky only; mastodon takes only the target-0 comment."
                  }
                },
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Thread created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "thread": {
                      "$ref": "#/components/schemas/Thread"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/threads/{id}": {
      "get": {
        "operationId": "getThread",
        "tags": [
          "Threads"
        ],
        "summary": "Get a thread",
        "description": "Returns the thread with each entry populated with its full post object.",
        "parameters": [
          {
            "$ref": "#/components/parameters/ThreadId"
          }
        ],
        "responses": {
          "200": {
            "description": "The thread",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "thread": {
                      "$ref": "#/components/schemas/Thread"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "operationId": "updateThread",
        "tags": [
          "Threads"
        ],
        "summary": "Update a thread",
        "description": "Update a draft or scheduled thread. Changing scheduledFor also reschedules all child posts.",
        "parameters": [
          {
            "$ref": "#/components/parameters/ThreadId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "scheduledFor": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "title": {
                    "type": "string"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "draft",
                      "scheduled"
                    ]
                  },
                  "config": {
                    "type": "object",
                    "additionalProperties": true
                  }
                },
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated thread",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "thread": {
                      "$ref": "#/components/schemas/Thread"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "deleteThread",
        "tags": [
          "Threads"
        ],
        "summary": "Delete or cancel a thread",
        "description": "Draft threads are hard-deleted along with their child posts. Scheduled threads are cancelled (soft delete).",
        "parameters": [
          {
            "$ref": "#/components/parameters/ThreadId"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted or cancelled",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "deleted": {
                      "type": "boolean"
                    },
                    "id": {
                      "type": "string"
                    },
                    "method": {
                      "type": "string"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/accounts": {
      "get": {
        "operationId": "listAccounts",
        "tags": [
          "Accounts"
        ],
        "summary": "List connected accounts",
        "description": "Lists API-connected accounts and dashboard-connected accounts (source field distinguishes them). Use the returned id as accountId in posts, analytics, and engagement calls.",
        "parameters": [
          {
            "name": "brandId",
            "in": "query",
            "description": "Filter API-connected accounts by brand (profile) id. Dashboard accounts are omitted when set.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "platform",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/PlatformName"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Connected accounts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "accounts": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Account"
                      }
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/accounts/health": {
      "get": {
        "operationId": "checkAccountsHealth",
        "tags": [
          "Accounts"
        ],
        "summary": "Bulk account health check",
        "description": "Runs a lightweight token health check on every connected API account in parallel and returns per-account results plus a summary.",
        "parameters": [
          {
            "name": "brandId",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "platform",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/PlatformName"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Health results",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AccountHealth"
                      }
                    },
                    "summary": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "integer"
                        },
                        "healthy": {
                          "type": "integer"
                        },
                        "expired": {
                          "type": "integer"
                        },
                        "error": {
                          "type": "integer"
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/accounts/{id}": {
      "get": {
        "operationId": "getAccount",
        "tags": [
          "Accounts"
        ],
        "summary": "Get an account",
        "parameters": [
          {
            "$ref": "#/components/parameters/AccountIdPath"
          }
        ],
        "responses": {
          "200": {
            "description": "The account",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "account": {
                      "$ref": "#/components/schemas/Account"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/accounts/{id}/health": {
      "get": {
        "operationId": "checkAccountHealth",
        "tags": [
          "Accounts"
        ],
        "summary": "Single account health check",
        "description": "Checks the account token against the platform API, attempting a token refresh where the platform supports it. May include a reconnectUrl when the connection is broken.",
        "parameters": [
          {
            "$ref": "#/components/parameters/AccountIdPath"
          }
        ],
        "responses": {
          "200": {
            "description": "Health result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "health": {
                      "$ref": "#/components/schemas/AccountHealth"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/media": {
      "post": {
        "operationId": "uploadMedia",
        "tags": [
          "Media"
        ],
        "summary": "Upload media (direct)",
        "description": "Direct upload for images: multipart/form-data with a file field, or JSON with base64 data (3MB max decoded, images only; for AI chat clients where an attached photo has no URL). Videos cannot be uploaded directly; upload them in the dashboard Media library, use createMediaUploadUrl for a presigned upload, or uploadMediaFromUrl. The returned url can be used in mediaItems when creating posts.",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "The image file."
                  },
                  "alt_text": {
                    "type": "string",
                    "description": "Optional alt text, up to 1000 characters."
                  }
                }
              }
            },
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data",
                  "mimeType"
                ],
                "properties": {
                  "data": {
                    "type": "string",
                    "description": "Base64-encoded file contents (images only, 3MB max decoded). Accepts bare base64 or a data: URI."
                  },
                  "mimeType": {
                    "type": "string",
                    "example": "image/png"
                  },
                  "fileName": {
                    "type": "string"
                  },
                  "altText": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Uploaded media",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "media": {
                      "$ref": "#/components/schemas/Media"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "413": {
            "description": "File too large (media_too_large)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Unsupported media type (unsupported_media)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "get": {
        "operationId": "listMedia",
        "tags": [
          "Media"
        ],
        "summary": "List recent media",
        "description": "Recent confirmed uploads for the key's user (dashboard Media library + all API keys), newest first. Use the returned url in mediaItems when creating posts; this is the reference path for posting large videos (upload once in the dashboard, then list and attach).",
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "image",
                "video"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Recent media",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "media": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "url": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string",
                            "enum": [
                              "image",
                              "video"
                            ]
                          },
                          "mimeType": {
                            "type": "string"
                          },
                          "sizeBytes": {
                            "type": "integer"
                          },
                          "fileName": {
                            "type": "string"
                          },
                          "altText": {
                            "type": "string",
                            "nullable": true
                          },
                          "createdAt": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/media/upload-url": {
      "post": {
        "operationId": "createMediaUploadUrl",
        "tags": [
          "Media"
        ],
        "summary": "Create a presigned upload URL",
        "description": "Returns a presigned URL valid for one hour. PUT the file bytes to uploadUrl with the same Content-Type, then call confirmMediaUpload.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "fileName",
                  "mimeType",
                  "sizeBytes"
                ],
                "properties": {
                  "fileName": {
                    "type": "string"
                  },
                  "mimeType": {
                    "type": "string",
                    "description": "For example image/jpeg or video/mp4."
                  },
                  "sizeBytes": {
                    "type": "integer",
                    "minimum": 1
                  },
                  "altText": {
                    "type": "string",
                    "description": "Optional alt text, up to 1000 characters."
                  }
                },
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Presigned upload details",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "media": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "description": "Media id to confirm after uploading."
                        },
                        "uploadUrl": {
                          "type": "string",
                          "description": "Presigned S3 URL. Send a PUT request with the file bytes."
                        },
                        "method": {
                          "type": "string"
                        },
                        "headers": {
                          "type": "object",
                          "description": "Headers to send with the PUT, including Content-Type.",
                          "additionalProperties": {
                            "type": "string"
                          }
                        },
                        "expiresIn": {
                          "type": "integer",
                          "description": "Seconds until the presigned URL expires (3600)."
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "413": {
            "description": "File too large (media_too_large)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Unsupported media type (unsupported_media)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/media/{id}/confirm": {
      "post": {
        "operationId": "confirmMediaUpload",
        "tags": [
          "Media"
        ],
        "summary": "Confirm a presigned upload",
        "description": "Verifies the file landed in storage and finalizes the media record. Returns 410 when the presigned URL expired before confirmation.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Media id from createMediaUploadUrl.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Confirmed media",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "media": {
                      "$ref": "#/components/schemas/Media"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "410": {
            "description": "Presigned URL expired (gone). Request a new upload URL.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/media/from-url": {
      "post": {
        "operationId": "uploadMediaFromUrl",
        "tags": [
          "Media"
        ],
        "summary": "Import media from a URL",
        "description": "Downloads a publicly reachable image or video and re-hosts it on the Schedulala CDN, returning a stable url for use in mediaItems. The file type is identified from file contents, not the Content-Type header. Private and internal addresses are rejected.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "description": "Public http(s) URL of the image or video."
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "image",
                      "video"
                    ],
                    "description": "Optional hint, verified against the downloaded bytes."
                  },
                  "altText": {
                    "type": "string",
                    "description": "Optional alt text, up to 1000 characters."
                  },
                  "fileName": {
                    "type": "string",
                    "description": "Optional file name. Defaults to the URL basename."
                  }
                },
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Imported media",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "media": {
                      "$ref": "#/components/schemas/Media"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "413": {
            "description": "File too large (media_too_large)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Unsupported media type (unsupported_media)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Download failed (download_failed)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/analytics/posts": {
      "get": {
        "operationId": "listPostAnalytics",
        "tags": [
          "Analytics"
        ],
        "summary": "Per-post analytics",
        "description": "Per-post analytics for one account with cursor pagination. The engagementRate field is always null for YouTube posts (YouTube API policy does not allow derived metrics). Requires a plan with analytics access.",
        "parameters": [
          {
            "name": "accountId",
            "in": "query",
            "required": false,
            "description": "Account id from listAccounts. Omit to merge the top posts across all analytics-enabled accounts. Merged responses do not paginate (pagination.cursor is null, pagination.merged is true); passing cursor without accountId returns 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "platform",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Scope the merged mode to one platform. Auto-resolved from accountId otherwise."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "Opaque cursor from a previous response. Requires accountId — merged results don't paginate.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "date",
                "likes",
                "impressions",
                "engagement"
              ],
              "default": "date"
            }
          },
          {
            "name": "order",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "desc"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Post analytics",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AnalyticsPost"
                      }
                    },
                    "pagination": {
                      "type": "object",
                      "properties": {
                        "cursor": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Pass as cursor to fetch the next page. Null when there are no more results."
                        },
                        "hasMore": {
                          "type": "boolean"
                        },
                        "limit": {
                          "type": "integer"
                        },
                        "merged": {
                          "type": "boolean",
                          "description": "Present and true when accountId was omitted and results were merged across accounts (no cursor pagination)."
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/analytics/daily": {
      "get": {
        "operationId": "getDailyAnalytics",
        "tags": [
          "Analytics"
        ],
        "summary": "Daily aggregated analytics",
        "description": "Daily aggregated post metrics across accounts, with a summary and trend. Requires a plan with analytics access.",
        "parameters": [
          {
            "name": "accountId",
            "in": "query",
            "description": "Limit to one account. All analytics-enabled accounts when omitted.",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/AnalyticsPlatform"
          },
          {
            "name": "days",
            "in": "query",
            "description": "Lookback window in days. Ignored when startDate is provided.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 180,
              "default": 30
            }
          },
          {
            "name": "startDate",
            "in": "query",
            "schema": {
              "type": "string",
              "description": "ISO date, for example 2026-01-15."
            }
          },
          {
            "name": "endDate",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Daily metrics",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "daily": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "date": {
                                "type": "string",
                                "description": "YYYY-MM-DD"
                              },
                              "posts": {
                                "type": "integer"
                              },
                              "likes": {
                                "type": "integer"
                              },
                              "comments": {
                                "type": "integer"
                              },
                              "replies": {
                                "type": "integer"
                              },
                              "shares": {
                                "type": "integer"
                              },
                              "impressions": {
                                "type": "integer"
                              },
                              "reach": {
                                "type": "integer"
                              },
                              "views": {
                                "type": "integer"
                              },
                              "reposts": {
                                "type": "integer"
                              },
                              "quotes": {
                                "type": "integer"
                              },
                              "byPlatform": {
                                "type": "object",
                                "additionalProperties": true
                              }
                            },
                            "additionalProperties": true
                          }
                        },
                        "summary": {
                          "type": "object",
                          "properties": {
                            "totalPosts": {
                              "type": "integer"
                            },
                            "totalLikes": {
                              "type": "integer"
                            },
                            "totalImpressions": {
                              "type": "integer"
                            },
                            "avgEngagementRate": {
                              "type": [
                                "number",
                                "null"
                              ]
                            },
                            "trend": {
                              "type": [
                                "number",
                                "null"
                              ],
                              "description": "Percent change in likes, second half of the window vs first half."
                            }
                          },
                          "additionalProperties": true
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/analytics/followers": {
      "get": {
        "operationId": "getFollowerAnalytics",
        "tags": [
          "Analytics"
        ],
        "summary": "Follower history and growth",
        "description": "Follower count history and 7/30 day growth for a single account. Requires a plan with analytics access.",
        "parameters": [
          {
            "name": "accountId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Account id from listAccounts. Omit to get per-account blocks for all analytics-enabled accounts plus summed totals under data.merged/data.totals/data.accounts. Totals exclude YouTube channels (YouTube API policy forbids derived metrics); their per-account blocks report raw counts."
          },
          {
            "name": "platform",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Scope the merged mode to one platform. Auto-resolved from accountId otherwise."
          },
          {
            "name": "days",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 365,
              "default": 30
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Follower data (single account, or merged across accounts when accountId is omitted)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "oneOf": [
                        {
                          "type": "object",
                          "properties": {
                            "platform": {
                              "type": "string"
                            },
                            "accountId": {
                              "type": "string"
                            },
                            "current": {
                              "type": [
                                "integer",
                                "null"
                              ]
                            },
                            "growth": {
                              "type": "object",
                              "properties": {
                                "last7d": {
                                  "type": [
                                    "integer",
                                    "null"
                                  ]
                                },
                                "last30d": {
                                  "type": [
                                    "integer",
                                    "null"
                                  ]
                                }
                              },
                              "additionalProperties": true
                            },
                            "history": {
                              "type": "array",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "date": {
                                    "type": "string"
                                  },
                                  "followers": {
                                    "type": [
                                      "integer",
                                      "null"
                                    ]
                                  },
                                  "change": {
                                    "type": [
                                      "integer",
                                      "null"
                                    ]
                                  }
                                },
                                "additionalProperties": true
                              }
                            }
                          },
                          "additionalProperties": true
                        },
                        {
                          "type": "object",
                          "description": "Merged shape returned when accountId is omitted.",
                          "properties": {
                            "merged": {
                              "type": "boolean"
                            },
                            "totals": {
                              "type": "object",
                              "properties": {
                                "current": {
                                  "type": [
                                    "integer",
                                    "null"
                                  ]
                                },
                                "growth": {
                                  "type": "object",
                                  "properties": {
                                    "last7d": {
                                      "type": [
                                        "integer",
                                        "null"
                                      ]
                                    },
                                    "last30d": {
                                      "type": [
                                        "integer",
                                        "null"
                                      ]
                                    }
                                  },
                                  "additionalProperties": true
                                }
                              },
                              "additionalProperties": true,
                              "description": "Sums across accounts; null when nothing contributed. Excludes YouTube."
                            },
                            "accounts": {
                              "type": "array",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "platform": {
                                    "type": "string"
                                  },
                                  "accountId": {
                                    "type": "string"
                                  },
                                  "current": {
                                    "type": [
                                      "integer",
                                      "null"
                                    ]
                                  },
                                  "growth": {
                                    "type": "object",
                                    "properties": {
                                      "last7d": {
                                        "type": [
                                          "integer",
                                          "null"
                                        ]
                                      },
                                      "last30d": {
                                        "type": [
                                          "integer",
                                          "null"
                                        ]
                                      }
                                    },
                                    "additionalProperties": true
                                  },
                                  "history": {
                                    "type": "array",
                                    "items": {
                                      "type": "object",
                                      "properties": {
                                        "date": {
                                          "type": "string"
                                        },
                                        "followers": {
                                          "type": [
                                            "integer",
                                            "null"
                                          ]
                                        },
                                        "change": {
                                          "type": [
                                            "integer",
                                            "null"
                                          ]
                                        }
                                      },
                                      "additionalProperties": true
                                    }
                                  }
                                },
                                "additionalProperties": true,
                                "description": "Per-account block (single-account shape plus username)."
                              }
                            }
                          },
                          "additionalProperties": true
                        }
                      ]
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/analytics/best-time": {
      "get": {
        "operationId": "getBestPostingTimes",
        "tags": [
          "Analytics"
        ],
        "summary": "Best posting times",
        "description": "Optimal posting hours and days computed from your historical engagement. YouTube accounts are excluded entirely (YouTube API policy does not allow derived guidance from API data). Requires a plan with analytics access.",
        "parameters": [
          {
            "name": "accountId",
            "in": "query",
            "description": "Limit to one account. All analytics-enabled accounts when omitted.",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/AnalyticsPlatform"
          }
        ],
        "responses": {
          "200": {
            "description": "Best time data",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "byHour": {
                          "type": "object",
                          "description": "24 entries keyed '0' through '23', each with avgEngagement and postCount.",
                          "additionalProperties": {
                            "$ref": "#/components/schemas/EngagementBucket"
                          }
                        },
                        "byDayOfWeek": {
                          "type": "object",
                          "description": "Entries keyed sunday through saturday.",
                          "additionalProperties": {
                            "$ref": "#/components/schemas/EngagementBucket"
                          }
                        },
                        "bestHours": {
                          "type": "array",
                          "description": "Top 3 hours by average engagement.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "hour": {
                                "type": "integer"
                              },
                              "avgEngagement": {
                                "type": "number"
                              },
                              "postCount": {
                                "type": "integer"
                              }
                            },
                            "additionalProperties": true
                          }
                        },
                        "bestDays": {
                          "type": "array",
                          "description": "Top 3 days by average engagement.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "day": {
                                "type": "string"
                              },
                              "avgEngagement": {
                                "type": "number"
                              },
                              "postCount": {
                                "type": "integer"
                              }
                            },
                            "additionalProperties": true
                          }
                        },
                        "byPlatform": {
                          "type": "object",
                          "additionalProperties": true
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/analytics/content-types": {
      "get": {
        "operationId": "getContentTypeAnalytics",
        "tags": [
          "Analytics"
        ],
        "summary": "Performance by content type",
        "description": "Performance breakdown by content type (image, video, carousel, text and similar). Available for Instagram, Twitter, and Threads accounts. Requires a plan with analytics access.",
        "parameters": [
          {
            "name": "accountId",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/AnalyticsPlatform"
          }
        ],
        "responses": {
          "200": {
            "description": "Content type breakdown",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "contentTypes": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "description": "image, video, carousel, text, text_only, with_image, with_video, or with_link."
                              },
                              "postCount": {
                                "type": "integer"
                              },
                              "avgEngagement": {
                                "type": [
                                  "number",
                                  "null"
                                ]
                              },
                              "avgImpressions": {
                                "type": [
                                  "number",
                                  "null"
                                ]
                              }
                            },
                            "additionalProperties": true
                          }
                        },
                        "byPlatform": {
                          "type": "object",
                          "additionalProperties": true
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/analytics/refresh": {
      "post": {
        "operationId": "refreshAnalytics",
        "tags": [
          "Analytics"
        ],
        "summary": "Refresh analytics for an account",
        "description": "Manually pulls fresh analytics from the platform for one account. Supported platforms: instagram, twitter, bluesky, threads, facebook, youtube. Limited to one refresh per account per hour (429 rate_limited otherwise). Requires a plan with analytics access. Supports an optional Idempotency-Key header.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "accountId"
                ],
                "properties": {
                  "accountId": {
                    "type": "string",
                    "description": "Account id from listAccounts."
                  }
                },
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Refresh completed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "accountId": {
                          "type": "string"
                        },
                        "platform": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "postsUpdated": {
                          "type": "integer"
                        },
                        "lastRefreshed": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "sandbox": {
                          "type": "boolean",
                          "description": "True when simulated with a test key."
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/analytics/retention": {
      "get": {
        "operationId": "getAudienceRetention",
        "tags": [
          "Analytics"
        ],
        "summary": "YouTube audience retention",
        "description": "Per-video audience retention for YouTube only. Returns the raw YouTube Analytics rows: retention maps percent of video elapsed to audienceWatchRatio, and relativePerformance maps the same keys to YouTube's relativeRetentionPerformance metric. No averaged score or performance label is included. Requires the yt-analytics.readonly scope on the connected channel; results are cached for 7 days.",
        "parameters": [
          {
            "name": "videoId",
            "in": "query",
            "required": true,
            "description": "The YouTube video id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "accountId",
            "in": "query",
            "description": "Channel account id. Defaults to the first connected YouTube channel.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "platform",
            "in": "query",
            "description": "Only youtube is supported.",
            "schema": {
              "type": "string",
              "enum": [
                "youtube"
              ],
              "default": "youtube"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Retention rows",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "platform": {
                      "type": "string"
                    },
                    "account": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "username": {
                          "type": [
                            "string",
                            "null"
                          ]
                        }
                      },
                      "additionalProperties": true
                    },
                    "videoId": {
                      "type": "string"
                    },
                    "retention": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "description": "Keys are percent of video elapsed ('0' to '100'); values are audienceWatchRatio (fraction of viewers still watching). Null when no data is available.",
                      "additionalProperties": {
                        "type": "number"
                      }
                    },
                    "relativePerformance": {
                      "type": "object",
                      "description": "Same keys; values are YouTube's relativeRetentionPerformance vs similar videos (1.0 is on par).",
                      "additionalProperties": {
                        "type": "number"
                      }
                    },
                    "message": {
                      "type": "string",
                      "description": "Present when no retention data is available."
                    },
                    "fromCache": {
                      "type": "boolean"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/PlatformError"
          }
        }
      }
    },
    "/api/v1/engagement/comments": {
      "get": {
        "operationId": "listComments",
        "tags": [
          "Engagement"
        ],
        "summary": "List comments",
        "description": "Recent comments across an account (no postId) or comments on a specific post (postId). postId semantics per platform: media id (instagram, facebook), video id (youtube), post URN (linkedin), thread id (threads), post URI (bluesky).",
        "parameters": [
          {
            "name": "platform",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "facebook",
                "instagram",
                "youtube",
                "linkedin",
                "threads",
                "bluesky"
              ]
            }
          },
          {
            "$ref": "#/components/parameters/EngagementAccountId"
          },
          {
            "name": "postId",
            "in": "query",
            "description": "Platform post identifier to fetch comments for. Omit for recent comments across the account.",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/EngagementCursor"
          },
          {
            "$ref": "#/components/parameters/EngagementLimit"
          }
        ],
        "responses": {
          "200": {
            "description": "Comments",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "platform": {
                      "type": "string"
                    },
                    "account": {
                      "$ref": "#/components/schemas/EngagementAccount"
                    },
                    "postId": {
                      "type": "string"
                    },
                    "comments": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Comment"
                      }
                    },
                    "hasMore": {
                      "type": "boolean"
                    },
                    "nextCursor": {
                      "type": "string"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/PlatformError"
          }
        }
      }
    },
    "/api/v1/engagement/mentions": {
      "get": {
        "operationId": "listMentions",
        "tags": [
          "Engagement"
        ],
        "summary": "List mentions",
        "description": "Posts and comments where the account is mentioned or tagged. Supported: instagram (tags and mentions), threads, bluesky (mentions and quotes).",
        "parameters": [
          {
            "name": "platform",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "instagram",
                "threads",
                "bluesky"
              ]
            }
          },
          {
            "$ref": "#/components/parameters/EngagementAccountId"
          },
          {
            "$ref": "#/components/parameters/EngagementCursor"
          },
          {
            "$ref": "#/components/parameters/EngagementLimit"
          }
        ],
        "responses": {
          "200": {
            "description": "Mentions",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "platform": {
                      "type": "string"
                    },
                    "account": {
                      "$ref": "#/components/schemas/EngagementAccount"
                    },
                    "mentions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    },
                    "hasMore": {
                      "type": "boolean"
                    },
                    "nextCursor": {
                      "type": "string"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/PlatformError"
          }
        }
      }
    },
    "/api/v1/engagement/transcript": {
      "get": {
        "operationId": "getVideoTranscript",
        "tags": [
          "Engagement"
        ],
        "summary": "Get a video transcript",
        "description": "Caption track metadata plus the parsed transcript of the best caption track. Supported: youtube.",
        "parameters": [
          {
            "name": "videoId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "platform",
            "in": "query",
            "description": "Only youtube is supported.",
            "schema": {
              "type": "string",
              "enum": [
                "youtube"
              ],
              "default": "youtube"
            }
          },
          {
            "$ref": "#/components/parameters/EngagementAccountId"
          }
        ],
        "responses": {
          "200": {
            "description": "Transcript",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "platform": {
                      "type": "string"
                    },
                    "account": {
                      "$ref": "#/components/schemas/EngagementAccount"
                    },
                    "videoId": {
                      "type": "string"
                    },
                    "tracks": {
                      "type": "array",
                      "description": "Available caption tracks.",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    },
                    "transcript": {
                      "description": "Parsed transcript of the best caption track."
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/PlatformError"
          }
        }
      }
    },
    "/api/v1/engagement/reply": {
      "post": {
        "operationId": "replyToComment",
        "tags": [
          "Engagement"
        ],
        "summary": "Reply to a comment or post",
        "description": "Post a reply. Target fields by platform: facebook, instagram, youtube, linkedin use commentId (facebook and linkedin also accept postId for a top-level comment); threads uses postId; bluesky uses parentUri and parentCid (plus rootUri and rootCid for nested replies). Test keys simulate the reply without writing to the platform.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "platform",
                  "message"
                ],
                "properties": {
                  "platform": {
                    "type": "string",
                    "enum": [
                      "facebook",
                      "instagram",
                      "youtube",
                      "linkedin",
                      "threads",
                      "bluesky"
                    ]
                  },
                  "accountId": {
                    "type": "string",
                    "description": "Defaults to the first connected account on the platform."
                  },
                  "message": {
                    "type": "string"
                  },
                  "commentId": {
                    "type": "string"
                  },
                  "postId": {
                    "type": "string"
                  },
                  "parentUri": {
                    "type": "string"
                  },
                  "parentCid": {
                    "type": "string"
                  },
                  "rootUri": {
                    "type": "string"
                  },
                  "rootCid": {
                    "type": "string"
                  }
                },
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Reply created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "platform": {
                      "type": "string"
                    },
                    "account": {
                      "$ref": "#/components/schemas/EngagementAccount"
                    },
                    "reply": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": [
                            "string",
                            "null"
                          ]
                        }
                      },
                      "additionalProperties": true
                    },
                    "sandbox": {
                      "type": "boolean"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/PlatformError"
          }
        }
      }
    },
    "/api/v1/engagement/hide": {
      "post": {
        "operationId": "hideComment",
        "tags": [
          "Engagement"
        ],
        "summary": "Hide or unhide a comment",
        "description": "Hide (default) or unhide a comment. Supported: facebook, instagram, youtube, threads. Test keys simulate without writing to the platform.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "platform",
                  "commentId"
                ],
                "properties": {
                  "platform": {
                    "type": "string",
                    "enum": [
                      "facebook",
                      "instagram",
                      "youtube",
                      "threads"
                    ]
                  },
                  "accountId": {
                    "type": "string"
                  },
                  "commentId": {
                    "type": "string"
                  },
                  "hide": {
                    "type": "boolean",
                    "default": true,
                    "description": "False to unhide."
                  }
                },
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Moderation applied",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "platform": {
                      "type": "string"
                    },
                    "account": {
                      "$ref": "#/components/schemas/EngagementAccount"
                    },
                    "commentId": {
                      "type": "string"
                    },
                    "hidden": {
                      "type": "boolean"
                    },
                    "sandbox": {
                      "type": "boolean"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/PlatformError"
          }
        }
      }
    },
    "/api/v1/feeds/search": {
      "get": {
        "operationId": "searchFeeds",
        "tags": [
          "Feeds"
        ],
        "summary": "Keyword search of public posts",
        "description": "Search public posts by keyword (social listening). Platforms: bluesky (sort and cursor pagination supported) and threads (Meta caps keyword search at 500 per rolling 7 days per account; the shared budget is returned as usage). Each result carries its reply target for replyToComment. Threads search is disabled for test keys.",
        "parameters": [
          {
            "name": "platform",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "bluesky",
                "threads"
              ]
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Search query.",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/EngagementAccountId"
          },
          {
            "name": "sort",
            "in": "query",
            "description": "Bluesky only.",
            "schema": {
              "type": "string",
              "enum": [
                "latest",
                "top"
              ],
              "default": "latest"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Search results",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "platform": {
                      "type": "string"
                    },
                    "account": {
                      "$ref": "#/components/schemas/EngagementAccount"
                    },
                    "query": {
                      "type": "string"
                    },
                    "posts": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    },
                    "nextCursor": {
                      "type": "string"
                    },
                    "hasMore": {
                      "type": "boolean"
                    },
                    "usage": {
                      "type": "object",
                      "description": "Threads only: the rolling 7 day search budget shared with the dashboard.",
                      "properties": {
                        "used": {
                          "type": "integer"
                        },
                        "remaining": {
                          "type": "integer"
                        },
                        "limit": {
                          "type": "integer"
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/PlatformError"
          }
        }
      }
    },
    "/api/v1/feeds/profile": {
      "get": {
        "operationId": "lookupProfile",
        "tags": [
          "Feeds"
        ],
        "summary": "Public profile lookup",
        "description": "Look up any public profile plus its recent posts. Supported: bluesky. Results carry reply targets for replyToComment.",
        "parameters": [
          {
            "name": "handle",
            "in": "query",
            "required": true,
            "description": "The profile handle, for example name.bsky.social.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "platform",
            "in": "query",
            "description": "Only bluesky is supported.",
            "schema": {
              "type": "string",
              "enum": [
                "bluesky"
              ],
              "default": "bluesky"
            }
          },
          {
            "$ref": "#/components/parameters/EngagementAccountId"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Profile and recent posts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "platform": {
                      "type": "string"
                    },
                    "profile": {
                      "type": "object",
                      "properties": {
                        "did": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "handle": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "displayName": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "description": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "avatar": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "followersCount": {
                          "type": [
                            "integer",
                            "null"
                          ]
                        },
                        "followsCount": {
                          "type": [
                            "integer",
                            "null"
                          ]
                        },
                        "postsCount": {
                          "type": [
                            "integer",
                            "null"
                          ]
                        }
                      },
                      "additionalProperties": true
                    },
                    "posts": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    },
                    "nextCursor": {
                      "type": "string"
                    },
                    "hasMore": {
                      "type": "boolean"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/PlatformError"
          }
        }
      }
    },
    "/api/v1/feeds/keywords": {
      "get": {
        "operationId": "listKeywords",
        "tags": [
          "Feeds"
        ],
        "summary": "List saved keywords",
        "description": "Saved social listening keywords for a platform. The list is shared with the Schedulala dashboard.",
        "parameters": [
          {
            "$ref": "#/components/parameters/FeedsPlatform"
          }
        ],
        "responses": {
          "200": {
            "description": "Saved keywords",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KeywordsResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "operationId": "addKeyword",
        "tags": [
          "Feeds"
        ],
        "summary": "Save a keyword",
        "description": "Add a keyword (2 to 100 characters, case-insensitive dedupe, maximum 20 per platform). Returns 409 when the keyword is already saved.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "platform",
                  "keyword"
                ],
                "properties": {
                  "platform": {
                    "type": "string",
                    "enum": [
                      "bluesky",
                      "threads"
                    ]
                  },
                  "keyword": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 100
                  }
                },
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Keyword saved; returns the updated list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KeywordsResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "description": "Keyword already saved",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "deleteKeyword",
        "tags": [
          "Feeds"
        ],
        "summary": "Delete a saved keyword",
        "description": "Remove a saved keyword (case-insensitive exact match). Returns the updated list.",
        "parameters": [
          {
            "$ref": "#/components/parameters/FeedsPlatform"
          },
          {
            "name": "keyword",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Keyword removed; returns the updated list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KeywordsResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/usage": {
      "get": {
        "operationId": "getUsage",
        "tags": [
          "Usage"
        ],
        "summary": "Current plan usage",
        "description": "Current plan, post quota, social set usage, and rate limit state, including a human-readable summary string.",
        "responses": {
          "200": {
            "description": "Usage details",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "usage": {
                      "type": "object",
                      "properties": {
                        "plan": {
                          "type": "string",
                          "description": "Plan id, for example api_sandbox, api_launch, api_scale, api_unlimited."
                        },
                        "plan_name": {
                          "type": "string"
                        },
                        "summary": {
                          "type": "string",
                          "description": "Human-readable usage summary."
                        },
                        "period": {
                          "type": "object",
                          "properties": {
                            "start": {
                              "type": "string",
                              "format": "date-time"
                            },
                            "end": {
                              "type": "string",
                              "format": "date-time"
                            },
                            "days_remaining": {
                              "type": "integer"
                            }
                          },
                          "additionalProperties": true
                        },
                        "posts": {
                          "type": "object",
                          "properties": {
                            "used": {
                              "type": "integer"
                            },
                            "limit": {
                              "type": "integer",
                              "description": "-1 means unlimited."
                            },
                            "remaining": {
                              "type": "integer"
                            },
                            "percent_used": {
                              "type": "integer"
                            },
                            "unlimited": {
                              "type": "boolean"
                            }
                          },
                          "additionalProperties": true
                        },
                        "socialSets": {
                          "type": "object",
                          "properties": {
                            "used": {
                              "type": "integer"
                            },
                            "limit": {
                              "type": "integer"
                            },
                            "remaining": {
                              "type": "integer"
                            },
                            "unlimited": {
                              "type": "boolean"
                            }
                          },
                          "additionalProperties": true
                        },
                        "apiRequests": {
                          "type": "object",
                          "properties": {
                            "used": {
                              "type": "integer"
                            },
                            "limit": {
                              "type": "integer"
                            }
                          },
                          "additionalProperties": true
                        },
                        "rateLimit": {
                          "type": "object",
                          "properties": {
                            "requestsPerMinute": {
                              "type": "integer"
                            },
                            "remaining": {
                              "type": "integer"
                            },
                            "resetsAt": {
                              "type": "string",
                              "format": "date-time"
                            }
                          },
                          "additionalProperties": true
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/billing/plan": {
      "get": {
        "operationId": "getBillingPlan",
        "tags": [
          "Billing"
        ],
        "summary": "Current API plan",
        "description": "Plan details, limits, usage, billing period, and feature flags for the API key's plan.",
        "responses": {
          "200": {
            "description": "Plan details",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "plan": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "description": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string",
                          "description": "active, free, or inactive."
                        },
                        "activatedAt": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "format": "date-time"
                        }
                      },
                      "additionalProperties": true
                    },
                    "limits": {
                      "type": "object",
                      "properties": {
                        "socialSets": {
                          "type": "integer",
                          "description": "-1 means unlimited."
                        },
                        "postsPerMonth": {
                          "type": "integer",
                          "description": "-1 means unlimited."
                        },
                        "requestsPerMinute": {
                          "type": "integer"
                        },
                        "webhooks": {
                          "type": "integer",
                          "description": "-1 means unlimited."
                        }
                      },
                      "additionalProperties": true
                    },
                    "usage": {
                      "type": "object",
                      "properties": {
                        "posts": {
                          "type": "object",
                          "additionalProperties": true
                        },
                        "socialSets": {
                          "type": "object",
                          "additionalProperties": true
                        }
                      },
                      "additionalProperties": true
                    },
                    "billing": {
                      "type": "object",
                      "properties": {
                        "period": {
                          "type": "object",
                          "properties": {
                            "start": {
                              "type": "string",
                              "format": "date-time"
                            },
                            "end": {
                              "type": "string",
                              "format": "date-time"
                            }
                          },
                          "additionalProperties": true
                        }
                      },
                      "additionalProperties": true
                    },
                    "features": {
                      "type": "object",
                      "properties": {
                        "analytics": {
                          "type": "boolean"
                        },
                        "analyticsAddon": {
                          "type": "boolean"
                        },
                        "prioritySupport": {
                          "type": "boolean"
                        },
                        "customBranding": {
                          "type": "boolean"
                        },
                        "discordSupport": {
                          "type": "boolean"
                        }
                      },
                      "additionalProperties": true
                    },
                    "dailyPostLimits": {
                      "description": "Per-day post limits for the plan."
                    },
                    "scaleUnits": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Number of Accelerate units, only set on the api_scale plan."
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Schedulala API key sent as a Bearer token: Authorization: Bearer sk_live_... or sk_test_... Keys are created in the developer dashboard at https://schedulala.com/developers. sk_test_ keys simulate posting without publishing."
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "Optional unique key. Repeating a request with the same key returns the stored response instead of creating a duplicate.",
        "schema": {
          "type": "string"
        }
      },
      "Page": {
        "name": "page",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "default": 1
        }
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 20
        }
      },
      "PostId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "The post id.",
        "schema": {
          "type": "string"
        }
      },
      "ThreadId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "The thread id.",
        "schema": {
          "type": "string"
        }
      },
      "AccountIdPath": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "The account id.",
        "schema": {
          "type": "string"
        }
      },
      "AnalyticsPlatform": {
        "name": "platform",
        "in": "query",
        "description": "Filter to one analytics platform.",
        "schema": {
          "type": "string",
          "enum": [
            "instagram",
            "twitter",
            "bluesky",
            "threads",
            "facebook",
            "linkedin",
            "pinterest",
            "tiktok",
            "youtube",
            "mastodon"
          ]
        }
      },
      "FeedsPlatform": {
        "name": "platform",
        "in": "query",
        "required": true,
        "schema": {
          "type": "string",
          "enum": [
            "bluesky",
            "threads"
          ]
        }
      },
      "EngagementAccountId": {
        "name": "accountId",
        "in": "query",
        "description": "Account id from listAccounts. Defaults to the first connected account on the platform.",
        "schema": {
          "type": "string"
        }
      },
      "EngagementCursor": {
        "name": "cursor",
        "in": "query",
        "description": "Pagination cursor from a previous response.",
        "schema": {
          "type": "string"
        }
      },
      "EngagementLimit": {
        "name": "limit",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 50,
          "default": 25
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Validation error (validation_error or invalid_request). The error.details field may include per-platform validation results.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing or invalid API key (unauthorized).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Forbidden": {
        "description": "The API key lacks the required permission, or the plan does not include this feature (forbidden or plan_limit_exceeded).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found (not_found).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit or quota exceeded (rate_limit_exceeded, quota_exceeded, or rate_limited). Check the X-RateLimit-Remaining and X-RateLimit-Reset response headers, and the Retry-After header when present, before retrying.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "PlatformError": {
        "description": "The upstream social platform request failed (platform_error) or denied permission (platform_permission_error, status 403).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "PlatformName": {
        "type": "string",
        "enum": [
          "twitter",
          "instagram",
          "linkedin",
          "facebook",
          "youtube",
          "bluesky",
          "tiktok",
          "telegram",
          "threads",
          "pinterest",
          "mastodon",
          "google-business"
        ]
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "description": "Error envelope. Most endpoints also include success: false alongside the error object.",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "False on errors when present."
          },
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "description": "Machine-readable error code, for example validation_error, unauthorized, forbidden, not_found, quota_exceeded, rate_limit_exceeded, platform_error."
              },
              "message": {
                "type": "string"
              },
              "details": {
                "description": "Optional structured details, for example validation results or quota numbers."
              }
            },
            "additionalProperties": true
          }
        },
        "additionalProperties": true
      },
      "PlatformSelector": {
        "description": "A platform name string, or an object selecting a specific connected account and optional per-platform content. A bare platform string works only when exactly ONE account is connected on that platform — with multiple accounts, omitting accountId returns a 400 validation error listing the choices (the API never silently picks an account for a write).",
        "oneOf": [
          {
            "$ref": "#/components/schemas/PlatformName"
          },
          {
            "type": "object",
            "required": [
              "platform"
            ],
            "properties": {
              "platform": {
                "$ref": "#/components/schemas/PlatformName"
              },
              "accountId": {
                "type": "string",
                "description": "Account id from listAccounts. Required when more than one account is connected on the platform (400 listing the choices otherwise); may be omitted only for single-account platforms."
              },
              "content": {
                "type": "string",
                "description": "Per-platform content override."
              }
            },
            "additionalProperties": true
          }
        ]
      },
      "MediaItemInput": {
        "type": "object",
        "required": [
          "url"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "image",
              "video"
            ],
            "default": "image"
          },
          "url": {
            "type": "string",
            "description": "Publicly reachable media URL, typically from the media endpoints."
          },
          "caption": {
            "type": "string"
          },
          "mediaId": {
            "type": "string",
            "description": "Optional id from the media endpoints; lets the API attach stored dimensions and duration."
          }
        },
        "additionalProperties": true
      },
      "PostCreateRequest": {
        "type": "object",
        "required": [
          "platforms"
        ],
        "properties": {
          "content": {
            "type": "string",
            "description": "The post text."
          },
          "platforms": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/PlatformSelector"
            }
          },
          "mediaItems": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MediaItemInput"
            }
          },
          "scheduledFor": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 schedule time. At least 5 minutes and at most 90 days in the future."
          },
          "publishNow": {
            "type": "boolean",
            "description": "Publish immediately."
          },
          "queue": {
            "type": "boolean",
            "description": "Auto-schedule into the next available queue slot."
          },
          "status": {
            "type": "string",
            "enum": [
              "draft"
            ],
            "description": "Set to draft to save without scheduling."
          },
          "brandId": {
            "type": "string",
            "description": "Brand (profile) id."
          },
          "timezone": {
            "type": "string",
            "description": "IANA timezone name, for example America/Toronto."
          },
          "platformSettings": {
            "type": "object",
            "description": "Per-platform settings keyed by platform name. A content field overrides the post text for that platform; other fields configure platform-specific options.",
            "additionalProperties": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "externalId": {
            "type": "string",
            "description": "Your own reference id, returned on the post object."
          },
          "firstComment": {
            "type": "string",
            "description": "Shorthand: auto-post this text as the first comment on the main post right after publishing (equivalent to firstComments: [{target: 0, text}]). Supported platforms: linkedin, facebook, instagram, youtube, twitter, threads, bluesky, mastodon. Text and links only, no media."
          },
          "firstComments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FirstCommentInput"
            },
            "description": "Auto-posted first comments. Limits: instagram 1000 UTF-8 bytes, linkedin 1500, twitter 280, bluesky 300, threads 500, youtube 10000 characters; facebook/mastodon no fixed limit. Not supported on tiktok, pinterest, telegram, google-business (selecting only unsupported platforms is a validation error). A failed comment never fails the post — check platforms[].firstComments on the post for delivery status."
          }
        },
        "additionalProperties": true
      },
      "PostPlatformResult": {
        "type": "object",
        "properties": {
          "platform": {
            "type": "string"
          },
          "accountId": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "description": "pending, posted, failed, or cancelled."
          },
          "postId": {
            "type": "string",
            "description": "Platform post id, present once posted."
          },
          "postUrl": {
            "type": "string",
            "description": "Public URL of the published post, when available."
          },
          "error": {
            "type": "string"
          },
          "postedAt": {
            "type": "string",
            "format": "date-time"
          },
          "firstComments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FirstCommentResult"
            }
          }
        },
        "additionalProperties": true
      },
      "Post": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "content": {
            "type": "string"
          },
          "platformContent": {
            "type": "object",
            "description": "Per-platform content overrides, present when set.",
            "additionalProperties": {
              "type": "string"
            }
          },
          "mediaItems": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                }
              },
              "additionalProperties": true
            }
          },
          "mediaType": {
            "type": "string",
            "enum": [
              "none",
              "image",
              "video",
              "carousel"
            ]
          },
          "status": {
            "type": "string",
            "description": "Post status, for example draft, scheduled, immediate, processing, posted, failed, cancelled."
          },
          "platforms": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PostPlatformResult"
            }
          },
          "scheduledFor": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "timezone": {
            "type": [
              "string",
              "null"
            ]
          },
          "isApiPost": {
            "type": "boolean"
          },
          "brandId": {
            "type": [
              "string",
              "null"
            ]
          },
          "externalId": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "firstComments": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "target": {
                  "type": "integer"
                },
                "text": {
                  "type": "string"
                }
              }
            },
            "description": "Composed first-comment intent (target 0 = main post). Delivery state is on platforms[].firstComments."
          }
        },
        "additionalProperties": true
      },
      "Thread": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "platforms": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "status": {
            "type": "string",
            "description": "scheduled, processing, posted, failed, cancelled, or draft."
          },
          "scheduledFor": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "posts": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "postId": {
                  "type": "string"
                },
                "position": {
                  "type": "integer"
                },
                "status": {
                  "type": "string"
                },
                "platformPostId": {
                  "type": "string"
                },
                "error": {
                  "type": "string"
                },
                "postedAt": {
                  "type": "string",
                  "format": "date-time"
                },
                "post": {
                  "$ref": "#/components/schemas/Post"
                }
              },
              "additionalProperties": true
            }
          },
          "config": {
            "type": "object",
            "properties": {
              "postDelay": {
                "type": "integer"
              },
              "continueOnError": {
                "type": "boolean"
              },
              "maxRetries": {
                "type": "integer"
              }
            },
            "additionalProperties": true
          },
          "isApiPost": {
            "type": "boolean"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": true
      },
      "Account": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Use as accountId in other endpoints."
          },
          "brandId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Workspace id. For source:\"api\" accounts this is the API Profile id (see /profiles). For source:\"dashboard\" accounts this is the dashboard Brand (workspace) id the account is assigned to (default brand first when assigned to several). Dashboard brand ids are informational for grouping — create_post brand targeting applies to API Profiles only."
          },
          "brandName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Dashboard accounts only: display name of the brand in brandId."
          },
          "brandIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Dashboard accounts only, present when the account is assigned to MULTIPLE brands."
          },
          "platform": {
            "type": "string"
          },
          "platformUserId": {
            "type": [
              "string",
              "null"
            ]
          },
          "username": {
            "type": [
              "string",
              "null"
            ]
          },
          "displayName": {
            "type": [
              "string",
              "null"
            ]
          },
          "profilePicture": {
            "type": [
              "string",
              "null"
            ]
          },
          "isConnected": {
            "type": "boolean",
            "description": "false when the platform revoked or expired the account's credentials (an open reconnect alert) — do not post to it; ask the user to reconnect in the dashboard."
          },
          "connectedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "lastValidated": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Last moment the connection state was verified: a platform validation stamp where one exists (pinterest token refresh, telegram session validation), or the failure-detection time for broken accounts. Null when never verified — most dashboard-connected platforms report null."
          },
          "needsReconnect": {
            "type": "boolean",
            "description": "Present and true when an open reconnect alert marks this account's token dead (revoked/expired). The account is not publishable until reconnected in the dashboard."
          },
          "reconnectReason": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "revoked",
              "expired",
              "password_changed",
              "identity_gate",
              "restricted",
              null
            ],
            "description": "Why the account needs reconnecting (present only with needsReconnect)."
          },
          "capabilities": {
            "type": "object",
            "additionalProperties": true
          },
          "platformData": {
            "type": "object",
            "additionalProperties": true
          },
          "source": {
            "type": "string",
            "enum": [
              "api",
              "dashboard"
            ],
            "description": "How the account was connected. Present on listAccounts."
          }
        },
        "additionalProperties": true
      },
      "AccountHealth": {
        "type": "object",
        "properties": {
          "accountId": {
            "type": "string"
          },
          "platform": {
            "type": "string"
          },
          "username": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "healthy",
              "expired",
              "error",
              "disconnected"
            ]
          },
          "tokenExpiresAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "error": {
            "type": "string"
          },
          "reconnectUrl": {
            "type": "string",
            "description": "Present on single-account checks when the connection is broken."
          },
          "checkedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": true
      },
      "Media": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "description": "CDN URL to use in mediaItems when creating posts."
          },
          "status": {
            "type": "string",
            "description": "ready once usable."
          },
          "type": {
            "type": "string",
            "enum": [
              "image",
              "video"
            ]
          },
          "mimeType": {
            "type": "string"
          },
          "size": {
            "type": "integer",
            "description": "Size in bytes."
          },
          "width": {
            "type": "integer"
          },
          "height": {
            "type": "integer"
          },
          "altText": {
            "type": [
              "string",
              "null"
            ]
          },
          "sourceUrl": {
            "type": "string",
            "description": "Original URL, present on from-url imports."
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": true
      },
      "ValidationResult": {
        "type": "object",
        "properties": {
          "isValid": {
            "type": "boolean"
          },
          "platforms": {
            "type": "object",
            "description": "Per-platform results keyed by platform name, each with a valid flag plus errors and warnings arrays when present.",
            "additionalProperties": {
              "type": "object",
              "properties": {
                "valid": {
                  "type": "boolean"
                },
                "errors": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "warnings": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              },
              "additionalProperties": true
            }
          }
        },
        "additionalProperties": true
      },
      "PagePagination": {
        "type": "object",
        "properties": {
          "total": {
            "type": "integer"
          },
          "page": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "pages": {
            "type": "integer"
          },
          "hasMore": {
            "type": "boolean"
          }
        },
        "additionalProperties": true
      },
      "PostMetrics": {
        "type": [
          "object",
          "null"
        ],
        "description": "Unified metrics. Fields a platform does not support are null.",
        "properties": {
          "likes": {
            "type": [
              "integer",
              "null"
            ]
          },
          "comments": {
            "type": [
              "integer",
              "null"
            ]
          },
          "replies": {
            "type": [
              "integer",
              "null"
            ]
          },
          "shares": {
            "type": [
              "integer",
              "null"
            ]
          },
          "saves": {
            "type": [
              "integer",
              "null"
            ]
          },
          "impressions": {
            "type": [
              "integer",
              "null"
            ]
          },
          "reach": {
            "type": [
              "integer",
              "null"
            ]
          },
          "views": {
            "type": [
              "integer",
              "null"
            ]
          },
          "quotes": {
            "type": [
              "integer",
              "null"
            ]
          },
          "reposts": {
            "type": [
              "integer",
              "null"
            ]
          },
          "bookmarks": {
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "additionalProperties": true
      },
      "AnalyticsPost": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "platform": {
            "type": "string"
          },
          "platformPostId": {
            "type": [
              "string",
              "null"
            ]
          },
          "postUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "content": {
            "type": [
              "string",
              "null"
            ]
          },
          "contentType": {
            "type": [
              "string",
              "null"
            ]
          },
          "postedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "metrics": {
            "$ref": "#/components/schemas/PostMetrics"
          },
          "engagementRate": {
            "type": [
              "number",
              "null"
            ],
            "description": "Engagement as a percentage of impressions, views, or reach. Always null for YouTube posts; YouTube API policy does not allow derived metrics."
          },
          "lastFetchedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "additionalProperties": true
      },
      "EngagementBucket": {
        "type": "object",
        "properties": {
          "avgEngagement": {
            "type": "number"
          },
          "postCount": {
            "type": "integer"
          }
        },
        "additionalProperties": true
      },
      "EngagementAccount": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "username": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "additionalProperties": true
      },
      "Comment": {
        "type": "object",
        "properties": {
          "id": {
            "type": [
              "string",
              "null"
            ]
          },
          "text": {
            "type": [
              "string",
              "null"
            ]
          },
          "author": {
            "type": "object",
            "properties": {
              "username": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "displayName": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "profilePicture": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "additionalProperties": true
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ]
          },
          "likeCount": {
            "type": [
              "integer",
              "null"
            ]
          },
          "post": {
            "description": "The parent post, when available."
          },
          "platform": {
            "type": "string"
          },
          "platformData": {
            "type": "object",
            "description": "Platform-specific fields, including reply target references for replyToComment.",
            "additionalProperties": true
          }
        },
        "additionalProperties": true
      },
      "KeywordsResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "platform": {
            "type": "string"
          },
          "keywords": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "keyword": {
                  "type": "string"
                },
                "createdAt": {
                  "type": "string",
                  "format": "date-time"
                }
              },
              "additionalProperties": true
            }
          }
        },
        "additionalProperties": true
      },
      "FirstCommentInput": {
        "type": "object",
        "required": [
          "text"
        ],
        "properties": {
          "target": {
            "type": "integer",
            "minimum": 0,
            "default": 0,
            "description": "0 = the main post (default); i = thread entry i+1 (threads on twitter/threads/bluesky only)."
          },
          "text": {
            "type": "string",
            "description": "Comment text — links allowed, no media."
          }
        }
      },
      "FirstCommentResult": {
        "type": "object",
        "description": "Per-account first-comment delivery state, written after the post publishes.",
        "properties": {
          "target": {
            "type": "integer",
            "description": "0 = main post; i = thread entry i+1."
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "processing",
              "posted",
              "failed",
              "skipped"
            ]
          },
          "commentId": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "description": "Direct link to the published comment (where the platform provides one)."
          },
          "error": {
            "type": "string"
          },
          "skipReason": {
            "type": "string",
            "enum": [
              "unsupported",
              "no_post_id",
              "story",
              "no_thread_entry"
            ]
          },
          "postedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      }
    }
  }
}
