{
  "openapi": "3.0.3",
  "info": {
    "title": "PostEverywhere API",
    "description": "Schedule and publish social media content across multiple platforms from a single API. Manage accounts, create posts, upload media, and monitor publishing results.\n\n## Quick Start\n\n**Node.js SDK:**\n```bash\nnpm install @posteverywhere/sdk\n```\n\n**Claude Code (MCP):**\n```json\n{\n  \"mcpServers\": {\n    \"posteverywhere\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@posteverywhere/mcp\"],\n      \"env\": { \"POSTEVERYWHERE_API_KEY\": \"pe_live_...\" }\n    }\n  }\n}\n```\n\n**CLI Setup:**\n```bash\nnpx posteverywhere init\n```\n\n## Authentication\n\nAll endpoints require a Bearer token. Generate an API key from **Developers** in the PostEverywhere dashboard.\n\n```\nAuthorization: Bearer pe_live_abc123...\n```\n\n[Sign up for a free 14-day trial](https://app.posteverywhere.ai/signup) (no credit card required).\n\n## Response Format\n\nAll responses follow a consistent envelope:\n\n```json\n{\n  \"data\": { ... },\n  \"error\": null,\n  \"meta\": { \"request_id\": \"a1b2c3d4\", \"timestamp\": \"2026-03-01T12:00:00.000Z\" }\n}\n```\n\nOn error, `data` is `null` and `error` contains `{ \"message\": \"...\", \"code\": \"error_code\", \"details\": ... }`.\n\n## Rate Limits\n\nAPI keys are rate-limited per minute and per hour. When exceeded, the API returns `429 Too Many Requests` with `Retry-After` and `X-RateLimit-*` headers.\n\n## Platform Guides\n\nUse the `platform_content` parameter on `POST /posts` to customise content per platform.\n\n### Instagram\n\nSupports Feed Posts, Reels, Stories, and Trial Reels.\n\n```json\n{\n  \"platform_content\": {\n    \"instagram\": {\n      \"content\": \"Instagram-specific caption\",\n      \"contentType\": \"Story\",\n      \"settings\": {\n        \"altText\": \"Image description for accessibility\",\n        \"firstComment\": \"First comment text\"\n      }\n    }\n  }\n}\n```\n\n- `contentType`: `\"Post\"` (default for images), `\"Reels\"` (default for videos), `\"Story\"`, `\"Trial Reel\"`\n- `altText`: max 100 characters\n- `firstComment`: auto-posted as first comment after publishing\n- Media (image or video) is required\n\n### TikTok\n\n```json\n{\n  \"platform_content\": {\n    \"tiktok\": {\n      \"content\": \"TikTok caption (max 150 chars)\",\n      \"settings\": {\n        \"privacyLevel\": \"PUBLIC_TO_EVERYONE\",\n        \"allowComments\": true,\n        \"allowDuet\": true,\n        \"allowStitch\": true,\n        \"videoCoverTimestamp\": 1000\n      }\n    }\n  }\n}\n```\n\n- `privacyLevel`: `\"PUBLIC_TO_EVERYONE\"` | `\"MUTUAL_FOLLOW_FRIENDS\"` | `\"FOLLOWER_OF_CREATOR\"` | `\"SELF_ONLY\"`\n- `videoCoverTimestamp`: thumbnail frame in milliseconds (default 1000)\n- Video is required for TikTok\n\n### YouTube\n\n```json\n{\n  \"platform_content\": {\n    \"youtube\": {\n      \"content\": \"Video description\",\n      \"settings\": {\n        \"title\": \"My Video Title (required)\",\n        \"privacyStatus\": \"public\",\n        \"tags\": [\"tag1\", \"tag2\"],\n        \"categoryId\": \"22\",\n        \"madeForKids\": false\n      }\n    }\n  }\n}\n```\n\n- `title`: required for YouTube, max 100 characters\n- `privacyStatus`: `\"public\"` | `\"unlisted\"` | `\"private\"`\n- `categoryId`: YouTube category (e.g. `\"22\"` = People & Blogs)\n- Video is required for YouTube\n\n### LinkedIn\n\n```json\n{\n  \"platform_content\": {\n    \"linkedin\": {\n      \"content\": \"LinkedIn-specific text (max 3000 chars)\",\n      \"settings\": {\n        \"visibility\": \"PUBLIC\",\n        \"firstComment\": \"First comment text\"\n      }\n    }\n  }\n}\n```\n\n- `visibility`: `\"PUBLIC\"` | `\"CONNECTIONS\"` | `\"LOGGED_IN_MEMBERS\"`\n- Supports images, videos, and PDF documents\n\n### X (Twitter)\n\n```json\n{\n  \"platform_content\": {\n    \"x\": {\n      \"content\": \"Tweet text (max 280 chars)\",\n      \"settings\": {\n        \"replySettings\": \"everyone\",\n        \"mediaAltText\": \"Image description\",\n        \"firstComment\": \"Reply text\"\n      }\n    }\n  }\n}\n```\n\n- `replySettings`: `\"everyone\"` | `\"following\"` | `\"mentionedUsers\"`\n- `mediaAltText`: max 1000 characters\n- Threads: pass `threadPosts` array at top level for multi-tweet threads\n\n### Facebook\n\n```json\n{\n  \"platform_content\": {\n    \"facebook\": {\n      \"content\": \"Facebook post text\",\n      \"settings\": {\n        \"videoType\": \"reel\",\n        \"link\": \"https://example.com\",\n        \"firstComment\": \"First comment text\"\n      }\n    }\n  }\n}\n```\n\n- `videoType`: `\"reel\"` (9:16, default) | `\"video\"` (any aspect ratio)\n- `link`: URL to share as link preview\n\n### Threads\n\n```json\n{\n  \"platform_content\": {\n    \"threads\": {\n      \"content\": \"Threads post (max 500 chars)\",\n      \"settings\": {\n        \"replyControl\": \"everyone\",\n        \"firstComment\": \"First comment text\"\n      }\n    }\n  }\n}\n```\n\n- `replyControl`: `\"everyone\"` | `\"following\"` | `\"mentioned\"`\n\n## Resources\n\n- [Node.js SDK](https://www.npmjs.com/package/@posteverywhere/sdk)\n- [MCP Server for Claude Code](https://www.npmjs.com/package/@posteverywhere/mcp)\n- [Help Center](https://posteverywhere.ai/docs)\n- [Pricing](https://posteverywhere.ai/pricing)",
    "version": "1.0.0",
    "contact": {
      "name": "PostEverywhere Support",
      "url": "https://posteverywhere.ai",
      "email": "support@posteverywhere.ai"
    }
  },
  "servers": [
    {
      "url": "https://app.posteverywhere.ai/api/v1",
      "description": "Production"
    }
  ],
  "security": [
    {
      "BearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Accounts",
      "description": "Manage connected social media accounts"
    },
    {
      "name": "Posts",
      "description": "Create, schedule, update, and delete posts"
    },
    {
      "name": "Media",
      "description": "Upload and manage media files"
    },
    {
      "name": "AI",
      "description": "Generate images using AI models. Requires the `ai` scope on your API key. Generated images are stored in your media library and can be attached to posts via `media_ids`."
    }
  ],
  "paths": {
    "/accounts": {
      "get": {
        "operationId": "listAccounts",
        "summary": "List accounts",
        "description": "Returns all connected social media accounts for your organization, including platform, username, and health status.",
        "tags": [
          "Accounts"
        ],
        "responses": {
          "200": {
            "description": "List of accounts",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "accounts": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/Account"
                              }
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/accounts/{id}": {
      "get": {
        "operationId": "getAccount",
        "summary": "Get account",
        "description": "Returns detailed information about a specific connected social media account, including its health status and whether it can currently post.",
        "tags": [
          "Accounts"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Account ID"
          }
        ],
        "responses": {
          "200": {
            "description": "Account details",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Account"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/posts": {
      "get": {
        "operationId": "listPosts",
        "summary": "List posts",
        "description": "Returns scheduled, published, or draft posts. Supports filtering by status and platform, with pagination.",
        "tags": [
          "Posts"
        ],
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "scheduled",
                "published",
                "draft"
              ]
            },
            "description": "Filter by post status"
          },
          {
            "name": "platform",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filter by destination platform (e.g. instagram, linkedin, x)"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            },
            "description": "Number of posts to return"
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            },
            "description": "Number of posts to skip"
          }
        ],
        "responses": {
          "200": {
            "description": "List of posts",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "posts": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/Post"
                              }
                            },
                            "pagination": {
                              "$ref": "#/components/schemas/Pagination"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "operationId": "createPost",
        "summary": "Create post",
        "description": "Create and schedule a social media post. Specify content, target accounts, and optionally a schedule time. If no `scheduled_at` is provided, the post is published immediately.\n\nUse `platform_content` to customise content per platform \u2014 override captions, set Instagram content type (Story, Reels, Post), configure TikTok privacy, set YouTube titles, and more.\n\n**Instagram Stories example:**\n```json\n{\n  \"content\": \"Check this out!\",\n  \"account_ids\": [123],\n  \"media_ids\": [\"uuid\"],\n  \"platform_content\": {\n    \"instagram\": { \"contentType\": \"Story\" }\n  }\n}\n```",
        "tags": [
          "Posts"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePostRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Post created",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/CreatePostResponse"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/posts/{id}": {
      "get": {
        "operationId": "getPost",
        "summary": "Get post",
        "description": "Returns detailed information about a specific post, including content, media, schedule, and per-platform destination statuses.",
        "tags": [
          "Posts"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Post UUID"
          }
        ],
        "responses": {
          "200": {
            "description": "Post details",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Post"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "operationId": "updatePost",
        "summary": "Update post",
        "description": "Update a scheduled or draft post. You can change content, schedule, target accounts, or media. Published posts cannot be modified.",
        "tags": [
          "Posts"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Post UUID"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdatePostRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated post",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Post"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "deletePost",
        "summary": "Delete post",
        "description": "Permanently delete a scheduled or draft post. Published posts cannot be deleted. This action cannot be undone.",
        "tags": [
          "Posts"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Post UUID"
          }
        ],
        "responses": {
          "200": {
            "description": "Post deleted",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "deleted": {
                              "type": "boolean",
                              "example": true
                            },
                            "id": {
                              "type": "string",
                              "format": "uuid"
                            },
                            "message": {
                              "type": "string",
                              "example": "Post permanently deleted."
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/posts/{id}/results": {
      "get": {
        "operationId": "getPostResults",
        "summary": "Get post results",
        "description": "Returns per-platform publishing results for a post, including published URLs, error messages, attempt counts, and retry schedules.",
        "tags": [
          "Posts"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Post UUID"
          }
        ],
        "responses": {
          "200": {
            "description": "Post results",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "post_id": {
                              "type": "string",
                              "format": "uuid"
                            },
                            "post_status": {
                              "type": "string"
                            },
                            "results": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/PostResult"
                              }
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/posts/{id}/retry": {
      "post": {
        "operationId": "retryFailedPost",
        "summary": "Retry failed post",
        "description": "Retry all failed platform destinations for a post. Resets failed destinations to queued status for re-publishing. Use after resolving temporary issues like rate limits or expired tokens.",
        "tags": [
          "Posts"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Post UUID"
          }
        ],
        "responses": {
          "200": {
            "description": "Destinations retried",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "retried_count": {
                              "type": "integer",
                              "description": "Number of failed destinations being retried"
                            },
                            "message": {
                              "type": "string",
                              "example": "2 failed destinations re-queued for publishing. Check GET /posts/{id}/results for updated statuses."
                            },
                            "destinations": {
                              "type": "array",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "id": {
                                    "type": "string",
                                    "format": "uuid"
                                  },
                                  "platform": {
                                    "type": "string"
                                  },
                                  "new_status": {
                                    "type": "string",
                                    "example": "queued"
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/media": {
      "get": {
        "operationId": "listMedia",
        "summary": "List media",
        "description": "Returns media files in your library. Supports filtering by type and pagination.",
        "tags": [
          "Media"
        ],
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "image",
                "video",
                "document"
              ]
            },
            "description": "Filter by media type"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            },
            "description": "Number of items to return"
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            },
            "description": "Number of items to skip"
          }
        ],
        "responses": {
          "200": {
            "description": "List of media",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "media": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/MediaItem"
                              }
                            },
                            "pagination": {
                              "$ref": "#/components/schemas/Pagination"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/media/{id}": {
      "get": {
        "operationId": "getMedia",
        "summary": "Get media",
        "description": "Returns detailed information about a specific media file, including type, dimensions, file size, and processing status.",
        "tags": [
          "Media"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Media UUID"
          }
        ],
        "responses": {
          "200": {
            "description": "Media details",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/MediaDetail"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "deleteMedia",
        "summary": "Delete media",
        "description": "Permanently delete a media file from your library and its backing storage. This action cannot be undone.",
        "tags": [
          "Media"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Media UUID"
          }
        ],
        "responses": {
          "200": {
            "description": "Media deleted",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "deleted": {
                              "type": "boolean",
                              "example": true
                            },
                            "id": {
                              "type": "string",
                              "format": "uuid"
                            },
                            "message": {
                              "type": "string",
                              "example": "Media file permanently deleted from library and storage."
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/media/upload": {
      "post": {
        "operationId": "uploadMedia",
        "summary": "Upload media",
        "description": "Request a presigned upload URL for a media file. This is step 1 of the 3-step upload flow:\n\n1. **POST /media/upload** \u2014 Get a presigned upload URL (this endpoint)\n2. **Upload your file** to the returned `upload_url` using the method specified in `upload_method`\n3. **POST /media/{id}/complete** \u2014 Signal that the upload is finished\n\n**Upload methods by type:**\n- **Images** \u2014 POST multipart-form to Cloudflare Images (field name: `file`)\n- **Videos** \u2014 PUT to R2 presigned URL with `Content-Type` header\n- **Documents** \u2014 PUT to R2 presigned URL with `Content-Type` header",
        "tags": [
          "Media"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UploadMediaRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Upload URL generated",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/UploadMediaResponse"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/media/{id}/complete": {
      "post": {
        "operationId": "completeMediaUpload",
        "summary": "Complete media upload",
        "description": "Signal that a file upload is finished. Call this after uploading your file to the presigned URL returned by `POST /media/upload`.\n\n**Processing by type:**\n- **Images** \u2014 Marked as ready immediately (Cloudflare Images handles optimization)\n- **Videos** \u2014 File verified in storage, thumbnail generated in background, marked as ready\n- **Documents (PDF)** \u2014 File verified in storage, marked as ready\n\nThe response includes the final status and a `message` with next steps.",
        "tags": [
          "Media"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Media UUID returned by POST /media/upload"
          }
        ],
        "responses": {
          "200": {
            "description": "Media upload completed",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/CompleteMediaResponse"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Upload not ready or file verification failed",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "nullable": true
                        },
                        "error": {
                          "type": "object",
                          "properties": {
                            "message": {
                              "type": "string",
                              "example": "File not found in storage. Make sure you uploaded the file to the upload_url returned by POST /media/upload."
                            },
                            "code": {
                              "type": "string",
                              "enum": [
                                "file_not_uploaded",
                                "file_type_mismatch",
                                "upload_failed"
                              ]
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/ai/generate-image": {
      "post": {
        "operationId": "generateImage",
        "summary": "Generate image",
        "description": "Generate an AI image from a text prompt. The image is stored in your media library and can be attached to posts via `media_ids`.\n\nRequires the `ai` scope on your API key.\n\n**Available models:**\n| Model | Credits | Speed | Best for |\n|-------|---------|-------|----------|\n| `nano-banana-pro` | 15 | ~5s | Best overall quality |\n| `ideogram-v2` | 8 | ~5s | Text in images |\n| `gemini-3-pro` | 5 | ~8s | Smart/contextual |\n| `flux-schnell` | 1 | ~2s | Fast drafts |",
        "tags": [
          "AI"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GenerateImageRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Image generated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/GenerateImageResponse"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid prompt, aspect ratio, or model",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "nullable": true
                        },
                        "error": {
                          "type": "object",
                          "properties": {
                            "message": {
                              "type": "string"
                            },
                            "code": {
                              "type": "string",
                              "enum": [
                                "validation_error",
                                "prompt_rejected"
                              ]
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "402": {
            "description": "Insufficient AI credits",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "nullable": true
                        },
                        "error": {
                          "type": "object",
                          "properties": {
                            "message": {
                              "type": "string",
                              "example": "Not enough credits. This model costs 15 credits but you have 3 available."
                            },
                            "code": {
                              "type": "string",
                              "example": "insufficient_credits"
                            },
                            "details": {
                              "type": "object",
                              "properties": {
                                "credits_required": {
                                  "type": "integer"
                                },
                                "credits_available": {
                                  "type": "integer"
                                },
                                "model": {
                                  "type": "string"
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "API key from Developers. Format: `pe_live_` followed by 32 hex characters."
      }
    },
    "schemas": {
      "ApiEnvelope": {
        "type": "object",
        "properties": {
          "data": {},
          "error": {
            "nullable": true,
            "type": "object",
            "properties": {
              "message": {
                "type": "string",
                "description": "Human-readable error description"
              },
              "code": {
                "type": "string",
                "description": "Machine-readable error code (e.g. validation_error, not_found, invalid_api_key)",
                "example": "validation_error"
              },
              "details": {
                "description": "Additional context about the error (varies by error type)"
              }
            }
          },
          "meta": {
            "type": "object",
            "properties": {
              "request_id": {
                "type": "string",
                "example": "a1b2c3d4"
              },
              "timestamp": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        }
      },
      "Account": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "platform": {
            "type": "string",
            "enum": [
              "x",
              "instagram",
              "facebook",
              "linkedin",
              "youtube",
              "tiktok",
              "threads",
              "pinterest"
            ]
          },
          "account_name": {
            "type": "string"
          },
          "avatar_url": {
            "type": "string",
            "nullable": true
          },
          "is_active": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "health": {
            "type": "object",
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "healthy",
                  "expired",
                  "unhealthy"
                ]
              },
              "can_post": {
                "type": "boolean"
              }
            }
          }
        }
      },
      "Post": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "content": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "scheduled",
              "published"
            ]
          },
          "account_ids": {
            "type": "array",
            "items": {
              "type": "integer"
            },
            "description": "Account IDs this post targets. Same name as the create-post request field \u2014 round-trip safe."
          },
          "scheduled_for": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "UTC ISO 8601 when the post will publish (or did publish). Same name as the request field. Always Z-suffixed UTC."
          },
          "timezone": {
            "type": "string",
            "description": "IANA timezone for display. Defaults to \"UTC\"."
          },
          "media_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Attached media IDs. Same name as the create-post request field."
          },
          "media": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "object"
            },
            "description": "Hydrated media objects with full metadata (url, type, dimensions, etc.). For round-trip, use `media_ids` instead \u2014 it has the same names as the request field."
          },
          "destinations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PostDestination"
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When this post record was created (UTC). NOT the same as `scheduled_for`."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PostDestination": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "platform": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "account_name": {
            "type": "string",
            "nullable": true
          },
          "account_id": {
            "type": "integer",
            "nullable": true
          },
          "published_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "platform_post_id": {
            "type": "string",
            "nullable": true
          },
          "error": {
            "type": "string",
            "nullable": true
          },
          "attempts": {
            "type": "integer"
          }
        }
      },
      "PostResult": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "platform": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "account_name": {
            "type": "string",
            "nullable": true
          },
          "account_id": {
            "type": "integer",
            "nullable": true
          },
          "published_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "platform_post_id": {
            "type": "string",
            "nullable": true
          },
          "platform_post_url": {
            "type": "string",
            "nullable": true
          },
          "error": {
            "type": "string",
            "nullable": true
          },
          "attempts": {
            "type": "integer"
          },
          "next_retry_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "MediaItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "type": {
            "type": "string",
            "enum": [
              "image",
              "video",
              "document"
            ]
          },
          "mime_type": {
            "type": "string",
            "example": "image/jpeg"
          },
          "file_size": {
            "type": "integer",
            "description": "File size in bytes"
          },
          "status": {
            "type": "string"
          },
          "original_name": {
            "type": "string",
            "nullable": true
          },
          "url": {
            "type": "string",
            "nullable": true
          },
          "thumbnail_url": {
            "type": "string",
            "nullable": true
          },
          "dimensions": {
            "nullable": true,
            "type": "object",
            "properties": {
              "width": {
                "type": "integer"
              },
              "height": {
                "type": "integer"
              }
            }
          },
          "aspect_ratio": {
            "type": "number",
            "nullable": true
          },
          "orientation": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "MediaDetail": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "type": {
            "type": "string",
            "enum": [
              "image",
              "video",
              "document"
            ]
          },
          "mime_type": {
            "type": "string"
          },
          "file_size": {
            "type": "integer",
            "description": "File size in bytes"
          },
          "status": {
            "type": "string",
            "enum": [
              "uploading",
              "ready",
              "failed"
            ],
            "description": "Current upload/processing status"
          },
          "dimensions": {
            "nullable": true,
            "type": "object",
            "properties": {
              "width": {
                "type": "integer"
              },
              "height": {
                "type": "integer"
              }
            }
          },
          "aspect_ratio": {
            "type": "number",
            "nullable": true
          },
          "orientation": {
            "type": "string",
            "nullable": true,
            "enum": [
              "portrait",
              "landscape",
              "square"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "message": {
            "type": "string",
            "nullable": true,
            "description": "Guidance message when status is 'uploading' \u2014 tells you to call POST /media/{id}/complete"
          }
        }
      },
      "Pagination": {
        "type": "object",
        "properties": {
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          }
        }
      },
      "CreatePostRequest": {
        "type": "object",
        "required": [
          "content",
          "account_ids"
        ],
        "properties": {
          "content": {
            "type": "string",
            "description": "The text content of the post"
          },
          "account_ids": {
            "type": "array",
            "items": {
              "type": "integer"
            },
            "minItems": 1,
            "description": "Array of social account IDs to post to (from list accounts)"
          },
          "scheduled_for": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 datetime in UTC when the post should publish, e.g. \"2026-04-15T14:30:00Z\". Always send UTC \u2014 convert from local time in your code if needed. Omit to publish immediately. The `timezone` field is metadata for display only and does NOT change when the post fires."
          },
          "scheduled_at": {
            "type": "string",
            "format": "date-time",
            "deprecated": true,
            "description": "DEPRECATED: use `scheduled_for` instead. `scheduled_at` was a misnamed legacy field \u2014 semantically `scheduled_for` is the correct English name for \"the time the post is scheduled FOR\". Still accepted for back-compat but will be removed in v2."
          },
          "timezone": {
            "type": "string",
            "default": "UTC",
            "description": "IANA timezone (e.g. \"UTC\", \"America/New_York\"). Defaults to \"UTC\". This is metadata for display in the PostEverywhere web UI \u2014 it does NOT change when the post fires. The actual fire time is always determined by `scheduled_for`. The web app uses this to render schedules in the user's preferred local time."
          },
          "media_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Array of media UUIDs to attach (from upload media)"
          },
          "platform_content": {
            "type": "object",
            "description": "Platform-specific content overrides and settings. Use the platform key (instagram, tiktok, youtube, linkedin, x, facebook, threads) as the object key.",
            "properties": {
              "instagram": {
                "type": "object",
                "properties": {
                  "content": {
                    "type": "string",
                    "description": "Override caption for Instagram"
                  },
                  "contentType": {
                    "type": "string",
                    "enum": [
                      "Post",
                      "Story",
                      "Reels",
                      "Trial Reel"
                    ],
                    "description": "Instagram content type. Videos default to Reels if not specified."
                  },
                  "settings": {
                    "type": "object",
                    "properties": {
                      "altText": {
                        "type": "string",
                        "description": "Accessibility alt text for the image"
                      },
                      "coverPhotoTimestamp": {
                        "type": "number",
                        "description": "Timestamp in seconds for video cover photo"
                      }
                    }
                  }
                }
              },
              "tiktok": {
                "type": "object",
                "properties": {
                  "content": {
                    "type": "string",
                    "description": "Override caption for TikTok"
                  },
                  "settings": {
                    "type": "object",
                    "properties": {
                      "privacyLevel": {
                        "type": "string",
                        "enum": [
                          "PUBLIC_TO_EVERYONE",
                          "MUTUAL_FOLLOW_FRIENDS",
                          "FOLLOWER_OF_CREATOR",
                          "SELF_ONLY"
                        ],
                        "description": "TikTok video privacy level"
                      },
                      "allowComments": {
                        "type": "boolean",
                        "default": true
                      },
                      "allowDuet": {
                        "type": "boolean",
                        "default": true
                      },
                      "allowStitch": {
                        "type": "boolean",
                        "default": true
                      }
                    }
                  }
                }
              },
              "youtube": {
                "type": "object",
                "properties": {
                  "content": {
                    "type": "string",
                    "description": "Override description for YouTube"
                  },
                  "settings": {
                    "type": "object",
                    "properties": {
                      "title": {
                        "type": "string",
                        "description": "YouTube video title (required for YouTube)"
                      },
                      "privacyStatus": {
                        "type": "string",
                        "enum": [
                          "public",
                          "unlisted",
                          "private"
                        ],
                        "default": "public"
                      },
                      "tags": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        },
                        "description": "YouTube video tags"
                      }
                    }
                  }
                }
              },
              "linkedin": {
                "type": "object",
                "properties": {
                  "content": {
                    "type": "string",
                    "description": "Override text for LinkedIn"
                  }
                }
              },
              "x": {
                "type": "object",
                "properties": {
                  "content": {
                    "type": "string",
                    "description": "Override text for X (Twitter). Max 280 characters."
                  }
                }
              },
              "facebook": {
                "type": "object",
                "properties": {
                  "content": {
                    "type": "string",
                    "description": "Override text for Facebook"
                  }
                }
              },
              "threads": {
                "type": "object",
                "properties": {
                  "content": {
                    "type": "string",
                    "description": "Override text for Threads"
                  }
                }
              }
            }
          }
        }
      },
      "CreatePostResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The created post ID"
          },
          "post_id": {
            "type": "string",
            "format": "uuid",
            "description": "Alias for `id`. Kept for back-compat."
          },
          "status": {
            "type": "string",
            "enum": [
              "publishing",
              "scheduled"
            ]
          },
          "content": {
            "type": "string",
            "description": "Echoes the content you sent"
          },
          "account_ids": {
            "type": "array",
            "items": {
              "type": "integer"
            },
            "description": "Echoes the account IDs you sent. Same name as the request field \u2014 round-trip safe."
          },
          "scheduled_for": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "UTC ISO 8601 timestamp when the post will publish. Same name as the request field. Always Z-suffixed UTC."
          },
          "timezone": {
            "type": "string",
            "description": "IANA timezone for display purposes. Always present, defaults to \"UTC\"."
          },
          "media_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Echoes the media IDs you attached. Same name as the request field."
          },
          "platform_content": {
            "type": "object",
            "nullable": true,
            "description": "Echoes the platform_content you sent."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When this post record was created (UTC). NOT the same as `scheduled_for` \u2014 `created_at` is when you scheduled it, `scheduled_for` is when it will fire."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "accounts_count": {
            "type": "integer"
          },
          "message": {
            "type": "string"
          },
          "results": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Per-platform publishing results (for immediate publishes)"
          }
        }
      },
      "UpdatePostRequest": {
        "type": "object",
        "properties": {
          "content": {
            "type": "string",
            "description": "New text content"
          },
          "scheduled_for": {
            "type": "string",
            "format": "date-time",
            "description": "New schedule time in UTC ISO 8601, e.g. \"2026-04-15T14:30:00Z\"."
          },
          "scheduled_at": {
            "type": "string",
            "format": "date-time",
            "deprecated": true,
            "description": "DEPRECATED: use `scheduled_for` instead."
          },
          "timezone": {
            "type": "string",
            "description": "New IANA timezone"
          },
          "account_ids": {
            "type": "array",
            "items": {
              "type": "integer"
            },
            "description": "New target account IDs (replaces existing)"
          },
          "media_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "New media attachments (replaces existing)"
          }
        }
      },
      "UploadMediaRequest": {
        "type": "object",
        "required": [
          "filename",
          "content_type",
          "size"
        ],
        "properties": {
          "filename": {
            "type": "string",
            "maxLength": 255,
            "description": "Original filename with extension",
            "example": "photo.jpg"
          },
          "content_type": {
            "type": "string",
            "enum": [
              "image/jpeg",
              "image/png",
              "image/gif",
              "image/webp",
              "image/heic",
              "image/heif",
              "video/mp4",
              "application/pdf"
            ],
            "description": "MIME type of the file. Only MP4 is accepted for video."
          },
          "size": {
            "type": "integer",
            "minimum": 1,
            "description": "File size in bytes. Max 20MB for images/documents, 500MB for video."
          },
          "width": {
            "type": "integer",
            "description": "Image/video width in pixels (optional, used for aspect ratio)"
          },
          "height": {
            "type": "integer",
            "description": "Image/video height in pixels (optional, used for aspect ratio)"
          },
          "duration": {
            "type": "number",
            "description": "Video duration in seconds (optional)"
          }
        }
      },
      "UploadMediaResponse": {
        "type": "object",
        "properties": {
          "media_id": {
            "type": "string",
            "format": "uuid",
            "description": "The single media UUID for THIS upload. Use it as part of `media_ids` (plural array) when attaching to a post via POST /posts. The post-create field is `media_ids`, never `media_id`."
          },
          "upload_url": {
            "type": "string",
            "description": "Presigned URL to upload your file to"
          },
          "upload_method": {
            "type": "object",
            "description": "How to upload the file to upload_url",
            "properties": {
              "method": {
                "type": "string",
                "enum": [
                  "PUT",
                  "POST"
                ],
                "description": "HTTP method to use"
              },
              "content_type": {
                "type": "string",
                "description": "Content-Type header to set (for PUT) or multipart/form-data (for POST)"
              },
              "field_name": {
                "type": "string",
                "description": "Form field name (only for POST/multipart uploads)"
              },
              "headers": {
                "type": "object",
                "additionalProperties": {
                  "type": "string"
                },
                "description": "Headers to include with the upload request (for PUT uploads)"
              }
            }
          },
          "provider": {
            "type": "string",
            "enum": [
              "cloudflare_images",
              "r2"
            ],
            "description": "Storage provider used for this upload"
          },
          "expires_in": {
            "type": "integer",
            "description": "Seconds until the upload URL expires",
            "example": 3600
          },
          "max_size": {
            "type": "integer",
            "description": "Maximum file size in bytes"
          },
          "next_step": {
            "type": "string",
            "description": "Instructions for what to do after uploading",
            "example": "Upload your file to upload_url, then call POST /media/{media_id}/complete to finalize."
          }
        }
      },
      "CompleteMediaResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "ready"
            ],
            "description": "Upload status after completion"
          },
          "type": {
            "type": "string",
            "enum": [
              "image",
              "video",
              "document"
            ]
          },
          "thumbnail_url": {
            "type": "string",
            "nullable": true,
            "description": "Thumbnail URL (images get it immediately, videos get it async)"
          },
          "message": {
            "type": "string",
            "description": "Human-readable next steps",
            "example": "Image is ready. You can now attach it to a post using media_ids."
          }
        }
      },
      "GenerateImageRequest": {
        "type": "object",
        "required": [
          "prompt"
        ],
        "properties": {
          "prompt": {
            "type": "string",
            "maxLength": 2000,
            "description": "Text description of the image to generate",
            "example": "A professional photo of a modern office workspace with natural lighting"
          },
          "aspect_ratio": {
            "type": "string",
            "enum": [
              "1:1",
              "16:9",
              "9:16",
              "4:3",
              "3:4",
              "4:5",
              "5:4"
            ],
            "default": "1:1",
            "description": "Output image aspect ratio"
          },
          "model": {
            "type": "string",
            "enum": [
              "nano-banana-pro",
              "ideogram-v2",
              "gemini-3-pro",
              "flux-schnell"
            ],
            "default": "nano-banana-pro",
            "description": "AI model to use. nano-banana-pro (15 credits, best quality), ideogram-v2 (8 credits, best text), gemini-3-pro (5 credits), flux-schnell (1 credit, fast)"
          }
        }
      },
      "GenerateImageResponse": {
        "type": "object",
        "properties": {
          "media_id": {
            "type": "string",
            "format": "uuid",
            "description": "Use this ID to attach the image to a post via media_ids"
          },
          "model": {
            "type": "string",
            "description": "Model that was used",
            "example": "nano-banana-pro"
          },
          "aspect_ratio": {
            "type": "string",
            "example": "16:9"
          },
          "credits_used": {
            "type": "integer",
            "description": "Credits consumed by this generation",
            "example": 15
          },
          "credits_remaining": {
            "type": "integer",
            "description": "Credits remaining after this generation",
            "example": 485
          },
          "message": {
            "type": "string",
            "example": "Image generated successfully. Attach it to a post using media_ids."
          }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Invalid request parameters",
        "content": {
          "application/json": {
            "schema": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/ApiEnvelope"
                },
                {
                  "type": "object",
                  "properties": {
                    "data": {
                      "nullable": true
                    },
                    "error": {
                      "type": "object",
                      "properties": {
                        "message": {
                          "type": "string",
                          "example": "Missing required fields: content and account_ids are required."
                        },
                        "code": {
                          "type": "string",
                          "example": "validation_error"
                        },
                        "details": {
                          "description": "Additional context (e.g. which fields are missing, current vs max values)"
                        }
                      }
                    }
                  }
                }
              ]
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing or invalid API key",
        "content": {
          "application/json": {
            "schema": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/ApiEnvelope"
                },
                {
                  "type": "object",
                  "properties": {
                    "data": {
                      "nullable": true
                    },
                    "error": {
                      "type": "object",
                      "properties": {
                        "message": {
                          "type": "string",
                          "example": "Invalid API key. Check that the key is correct and has not been deleted."
                        },
                        "code": {
                          "type": "string",
                          "enum": [
                            "invalid_api_key",
                            "api_key_revoked",
                            "api_key_expired",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  }
                }
              ]
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found",
        "content": {
          "application/json": {
            "schema": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/ApiEnvelope"
                },
                {
                  "type": "object",
                  "properties": {
                    "data": {
                      "nullable": true
                    },
                    "error": {
                      "type": "object",
                      "properties": {
                        "message": {
                          "type": "string",
                          "example": "Resource not found. Verify the ID belongs to your workspace."
                        },
                        "code": {
                          "type": "string",
                          "example": "not_found"
                        }
                      }
                    }
                  }
                }
              ]
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded",
        "headers": {
          "X-RateLimit-Limit": {
            "schema": {
              "type": "integer"
            },
            "description": "Request limit per window"
          },
          "X-RateLimit-Remaining": {
            "schema": {
              "type": "integer"
            },
            "description": "Remaining requests in current window"
          },
          "X-RateLimit-Reset": {
            "schema": {
              "type": "integer"
            },
            "description": "Unix timestamp when the window resets"
          },
          "Retry-After": {
            "schema": {
              "type": "integer"
            },
            "description": "Seconds to wait before retrying"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/ApiEnvelope"
                },
                {
                  "type": "object",
                  "properties": {
                    "data": {
                      "nullable": true
                    },
                    "error": {
                      "type": "object",
                      "properties": {
                        "message": {
                          "type": "string",
                          "example": "Rate limit exceeded. Check Retry-After header for wait time."
                        },
                        "code": {
                          "type": "string",
                          "example": "rate_limit_exceeded"
                        },
                        "details": {
                          "type": "object",
                          "properties": {
                            "retry_after": {
                              "type": "integer",
                              "description": "Seconds to wait"
                            },
                            "limit": {
                              "type": "integer"
                            },
                            "window": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              ]
            }
          }
        }
      },
      "InternalError": {
        "description": "Internal server error",
        "content": {
          "application/json": {
            "schema": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/ApiEnvelope"
                },
                {
                  "type": "object",
                  "properties": {
                    "data": {
                      "nullable": true
                    },
                    "error": {
                      "type": "object",
                      "properties": {
                        "message": {
                          "type": "string",
                          "example": "An internal error occurred. Please try again."
                        },
                        "code": {
                          "type": "string",
                          "example": "internal_error"
                        }
                      }
                    }
                  }
                }
              ]
            }
          }
        }
      }
    }
  }
}
