{
  "openapi": "3.1.0",
  "info": {
    "title": "Lowtato API",
    "version": "1.0.0",
    "description": "Social media automation API built for AI agents. Schedule, publish, and manage posts across 9 platforms (Twitter, Instagram, LinkedIn, TikTok, Facebook, Threads, Bluesky, YouTube, Pinterest) with a single API key. No browser required.\n\n## Quick Start for Agents\n1. `GET /api/v1/accounts` to discover your connected social account IDs\n2. `POST /api/v1/posts` to create and schedule a post\n3. `GET /api/v1/posts/{postId}` to poll until status is `published` or `failed`\n\n## Authentication\nPass your API key as `x-api-key` header or `Authorization: Bearer <key>`.\n\n## Machine-Readable Docs\n- llms.txt: https://lowtato.com/llms.txt\n- OpenAPI: https://lowtato-web-production.up.railway.app/openapi.json",
    "contact": {
      "name": "Lowtato Support",
      "url": "https://lowtato.com"
    }
  },
  "servers": [
    {
      "url": "https://www.lowtato.com",
      "description": "Production"
    }
  ],
  "security": [
    {
      "ApiKeyHeader": []
    }
  ],
  "paths": {
    "/api/health": {
      "get": {
        "tags": [
          "System"
        ],
        "summary": "Health check",
        "description": "Returns the current health status of the API and its dependencies. No authentication required. Agents should call this before starting multi-step workflows to verify API availability.",
        "security": [],
        "responses": {
          "200": {
            "description": "Readiness probe. `status` is `healthy`/`degraded`/`unhealthy`; per-dependency results are under `checks`. Use `?live` for a dependency-free liveness probe. Returns 503 only when the database is down.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/accounts": {
      "get": {
        "tags": [
          "Accounts"
        ],
        "summary": "List connected accounts",
        "description": "Returns all social media accounts connected to your Lowtato workspace. Call this first to discover account IDs needed for creating posts. Cache the results \u2014 account IDs rarely change.",
        "responses": {
          "200": {
            "description": "List of accounts. Use `id` as `accountId` when creating posts. Filter by `platform` and `is_active` to find the right account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountsListResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/accounts/{accountId}/subaccounts": {
      "get": {
        "tags": [
          "Accounts"
        ],
        "summary": "List subaccounts",
        "description": "Returns subaccounts (Facebook Pages, LinkedIn company pages, etc.) for a given social account. Some platforms require posting to a subaccount rather than the main account.",
        "parameters": [
          {
            "name": "accountId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Social account ID from GET /api/v1/accounts"
          }
        ],
        "responses": {
          "200": {
            "description": "List of subaccounts for the specified account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubaccountsListResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/v1/accounts/connect": {
      "post": {
        "tags": [
          "Accounts"
        ],
        "summary": "Connect a social account via API",
        "description": "Programmatically connect a social media account using OAuth tokens. No browser required. The token is validated against the platform API before saving. Supports all 9 platforms.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConnectAccountRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Account connected successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConnectAccountResponse"
                }
              }
            }
          },
          "400": {
            "description": "Missing required fields",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "description": "Token validation failed against the platform API",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/posts": {
      "post": {
        "tags": [
          "Posts"
        ],
        "summary": "Create a post",
        "description": "Create a new social media post. Three scheduling modes:\n- Set `scheduledTime` (ISO 8601) to schedule for a specific time\n- Set `useNextFreeSlot: true` to auto-schedule into the next available recurring slot\n- Omit both to save as a draft\n\nThe response includes the post `id` \u2014 store it to poll for publish status later via GET /api/v1/posts/{postId}.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePostRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Post created. `status` is `scheduled` or `draft`. Store the `id` to poll for publish status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatePostResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request body \u2014 check that accountId exists and content.text is non-empty",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/posts/{postId}": {
      "get": {
        "tags": [
          "Posts"
        ],
        "summary": "Get a post",
        "description": "Retrieve a single post by ID. Use this to poll for publish status after scheduling. Status lifecycle: draft -> scheduled -> publishing -> published | failed. When `published`, check `public_url` for the live link. When `failed`, check `error_message` for details. Recommended polling interval: every 5 seconds.",
        "parameters": [
          {
            "name": "postId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Post ID returned from POST /api/v1/posts"
          }
        ],
        "responses": {
          "200": {
            "description": "Post details including current status, public_url (if published), and error_message (if failed)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PostDetail"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/api/v1/posts/{postId}/publish": {
      "post": {
        "tags": [
          "Posts"
        ],
        "summary": "Publish a post immediately",
        "description": "Immediately publish a draft or scheduled post without waiting for the scheduled time. The post is sent to the platform synchronously. Returns 409 if already published or currently publishing.",
        "parameters": [
          {
            "name": "postId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Post ID to publish immediately"
          }
        ],
        "responses": {
          "200": {
            "description": "Post published successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "example": "published"
                    },
                    "public_url": {
                      "type": "string",
                      "format": "uri",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Post is already published or currently publishing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "description": "Publishing failed at the platform level",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/media/upload": {
      "post": {
        "tags": [
          "Media"
        ],
        "summary": "Upload media file (API key auth)",
        "description": "Upload an image or video. Accepts multipart/form-data (field: 'file') or JSON with base64 data (fields: data, mime_type). Returns a URL for use in mediaUrls when creating posts. Rate limit: 10 per minute.",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "Image (JPEG, PNG, GIF, WebP) or video (MP4, WebM) file"
                  }
                }
              }
            },
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data",
                  "mime_type"
                ],
                "properties": {
                  "data": {
                    "type": "string",
                    "description": "Base64-encoded file content"
                  },
                  "mime_type": {
                    "type": "string",
                    "description": "MIME type (e.g. image/png, video/mp4)"
                  },
                  "filename": {
                    "type": "string",
                    "description": "Optional filename"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "File uploaded. Use the returned url in mediaUrls when creating posts.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "mime_type": {
                      "type": "string"
                    },
                    "size": {
                      "type": "integer",
                      "description": "File size in bytes"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid file type, missing fields, or file too large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/marketplace": {
      "get": {
        "tags": [
          "Marketplace"
        ],
        "summary": "Browse available social accounts",
        "description": "Browse pre-configured social media accounts available for purchase. Filter by platform. Usernames are partially masked until purchase. Accounts come ready for agent use.",
        "parameters": [
          {
            "name": "platform",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "twitter",
                "instagram",
                "linkedin",
                "tiktok",
                "facebook",
                "threads",
                "bluesky",
                "youtube",
                "pinterest"
              ]
            },
            "description": "Filter by platform"
          }
        ],
        "responses": {
          "200": {
            "description": "List of available accounts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "accounts": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "platform": {
                            "type": "string"
                          },
                          "username_masked": {
                            "type": "string",
                            "description": "Partially masked username"
                          },
                          "price_cents": {
                            "type": "integer"
                          },
                          "price_display": {
                            "type": "string",
                            "example": "$25.00"
                          },
                          "metadata": {
                            "type": "object"
                          }
                        }
                      }
                    },
                    "count": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/marketplace/purchase": {
      "post": {
        "tags": [
          "Marketplace"
        ],
        "summary": "Purchase a social account",
        "description": "Purchase a social media account from the marketplace. Pays with credits if balance is sufficient, otherwise charges card on file. Set auto_connect: true to automatically connect the account to your Lowtato workspace.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "account_id"
                ],
                "properties": {
                  "account_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Account ID from GET /api/v1/marketplace"
                  },
                  "auto_connect": {
                    "type": "boolean",
                    "description": "If true, auto-connect the purchased account to your workspace"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Account purchased. Full credentials returned.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "account": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "platform": {
                          "type": "string"
                        },
                        "username": {
                          "type": "string"
                        },
                        "email": {
                          "type": "string"
                        },
                        "password": {
                          "type": "string"
                        },
                        "totp_secret": {
                          "type": "string",
                          "nullable": true
                        }
                      }
                    },
                    "paid_with": {
                      "type": "string",
                      "enum": [
                        "credits",
                        "card"
                      ]
                    },
                    "connected": {
                      "type": "boolean"
                    },
                    "social_account_id": {
                      "type": "string",
                      "nullable": true,
                      "description": "If auto_connect succeeded, the connected account ID"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "Payment failed or no payment method on file",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Account not available or already sold",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/webhooks": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Register a webhook",
        "description": "Register a webhook URL to receive event notifications. URL must use HTTPS. Valid events: post.published, post.failed, post.scheduled. Returns a signing secret for verifying payloads.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateWebhookRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Webhook registered. Store the secret for payload verification.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid URL or events",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "List webhooks",
        "description": "List all registered webhooks for your account.",
        "responses": {
          "200": {
            "description": "List of webhooks",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhooks": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "url": {
                            "type": "string",
                            "format": "uri"
                          },
                          "events": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "is_active": {
                            "type": "boolean"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Delete a webhook",
        "description": "Delete a webhook by ID.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Webhook ID to delete"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "type": "boolean",
                      "example": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/v1/schedules": {
      "get": {
        "tags": [
          "Schedules"
        ],
        "summary": "List scheduled posts",
        "description": "Returns posts with status `scheduled`, ordered by scheduled time ascending. Use this to see the current publishing queue.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 20,
              "minimum": 1,
              "maximum": 100
            },
            "description": "Maximum number of posts to return (default: 20, max: 100)"
          }
        ],
        "responses": {
          "200": {
            "description": "List of scheduled posts ordered by scheduled_time ascending",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScheduledPostsResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/v1/schedules/{scheduleId}": {
      "get": {
        "tags": [
          "Schedules"
        ],
        "summary": "Get a scheduled post",
        "description": "Retrieve full details of a single scheduled post including content, target platform, media URLs, and schedule time.",
        "parameters": [
          {
            "name": "scheduleId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Full post details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PostFull"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "tags": [
          "Schedules"
        ],
        "summary": "Update a scheduled post",
        "description": "Update the content text or scheduled time of a post. Only posts with status `scheduled` can be updated. Both fields are optional \u2014 send only what you want to change.",
        "parameters": [
          {
            "name": "scheduleId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateScheduledPostRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated post with new values reflected",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PostFull"
                }
              }
            }
          },
          "400": {
            "description": "Invalid update \u2014 post may not be in `scheduled` status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "delete": {
        "tags": [
          "Schedules"
        ],
        "summary": "Delete a scheduled post",
        "description": "Delete a scheduled post. Only posts with status `scheduled` can be deleted. Returns `{ deleted: true }` on success.",
        "parameters": [
          {
            "name": "scheduleId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Post deleted successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "type": "boolean",
                      "example": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/v1/schedule/slots": {
      "get": {
        "tags": [
          "Schedule Slots"
        ],
        "summary": "List schedule slots",
        "description": "Returns all recurring time slots configured for auto-scheduling. These define when posts created with `useNextFreeSlot: true` will be published. Day values: 0=Sunday through 6=Saturday.",
        "responses": {
          "200": {
            "description": "List of recurring schedule slots",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SlotsListResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "tags": [
          "Schedule Slots"
        ],
        "summary": "Create schedule slots",
        "description": "Create one or more recurring time slots for auto-scheduling posts. Day values: 0=Sunday through 6=Saturday. Optionally assign specific social account IDs via `selectedTargets`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateSlotsRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Slots created. Returns the count of slots created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "created": {
                      "type": "integer",
                      "description": "Number of slots created"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid slot data \u2014 check day (0-6), hour (0-23), minute (0-59) ranges",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/v1/sources": {
      "post": {
        "tags": [
          "Sources"
        ],
        "summary": "Create a source for AI extraction",
        "description": "Submit a URL or raw text for AI-powered content extraction. Processing begins immediately in the background. Poll GET /api/v1/sources/{sourceId} to check when extraction is complete (status: `ready`). Useful for agents that need to generate post content from articles or notes.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateSourceRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Source created and queued. Store the `id` to poll for extraction results.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateSourceResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid source data \u2014 check sourceType and required fields",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/sources/{sourceId}": {
      "get": {
        "tags": [
          "Sources"
        ],
        "summary": "Get source extraction status",
        "description": "Check the processing status of a source and retrieve extracted content once ready. Status lifecycle: `queued` -> `processing` -> `ready` | `failed`. When `ready`, `extracted_title` and `extracted_content` are populated. Recommended polling interval: every 3 seconds.",
        "parameters": [
          {
            "name": "sourceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Source ID returned from POST /api/v1/sources"
          }
        ],
        "responses": {
          "200": {
            "description": "Source details with extraction status and content (when ready)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SourceDetail"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "summary": "Delete a source",
        "tags": [
          "Sources"
        ],
        "parameters": [
          {
            "name": "sourceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Source deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "402": {
            "description": "API access not in plan",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Source not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/profile": {
      "get": {
        "tags": [
          "Profile"
        ],
        "summary": "Get current user profile",
        "description": "Returns the authenticated user's profile including display name, email, plan, credit balance, timezone, and connected account count. Useful for agents to check their current state before performing actions.",
        "responses": {
          "200": {
            "description": "User profile data",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProfileResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "tags": [
          "Profile"
        ],
        "summary": "Update user profile",
        "description": "Update profile fields like display name, timezone, and notification preferences. All fields are optional \u2014 send only what you want to change.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateProfileRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Profile updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "updated": {
                      "type": "boolean",
                      "example": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/brand-kit": {
      "get": {
        "tags": [
          "Brand Kit"
        ],
        "summary": "Get brand settings",
        "description": "Returns the brand kit configuration including name, colors, tone, topics, and logo. Used by AI generation to personalize content. Returns empty defaults if no brand kit has been configured.",
        "responses": {
          "200": {
            "description": "Brand kit data",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BrandKitResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "put": {
        "tags": [
          "Brand Kit"
        ],
        "summary": "Update brand settings",
        "description": "Create or update the brand kit. All fields are optional \u2014 send only what you want to set. The brand kit is used by AI generation to personalize post content.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BrandKitRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Brand kit updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "updated": {
                      "type": "boolean",
                      "example": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/onboarding": {
      "post": {
        "tags": [
          "Onboarding"
        ],
        "summary": "Complete onboarding in one call",
        "description": "Replaces the 5-step onboarding wizard. Sets up brand info, timezone, platform preferences, and auto-generates an optimal posting schedule. Ideal for agents that need to configure a new account programmatically.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OnboardingRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Onboarding completed successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OnboardingResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/schedule/auto-configure": {
      "post": {
        "tags": [
          "Schedule Slots"
        ],
        "summary": "Auto-generate optimal posting schedule",
        "description": "Automatically generates an optimal posting schedule based on platform best practices and your preferred frequency. Replaces all existing schedule slots. Use this instead of manually creating slots one by one.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AutoConfigureScheduleRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Schedule auto-configured",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AutoConfigureScheduleResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/analytics/summary": {
      "get": {
        "tags": [
          "Analytics"
        ],
        "summary": "Get aggregated analytics summary",
        "description": "Returns aggregated engagement stats across all connected accounts for a given period. Includes total posts, engagement metrics, top platform, best posting time, and engagement trend direction.",
        "parameters": [
          {
            "name": "period",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "7d",
                "30d",
                "90d"
              ],
              "default": "30d"
            },
            "description": "Time period for analytics aggregation"
          }
        ],
        "responses": {
          "200": {
            "description": "Analytics summary",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalyticsSummaryResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/ai/generate": {
      "post": {
        "tags": [
          "AI"
        ],
        "summary": "Generate post content via AI",
        "description": "Generate platform-optimized post content using AI. Uses your brand kit for personalization. Costs 3 credits per generation. Content is automatically stripped of markdown formatting.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AIGenerateRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Content generated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AIGenerateResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "Insufficient credits",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/posts/bulk": {
      "post": {
        "tags": [
          "Posts"
        ],
        "summary": "Create multiple posts at once",
        "description": "Bulk-create up to 50 posts in a single request. Each post can target multiple platforms. Posts are created as drafts or scheduled if `scheduled_at` is provided. Returns counts of created and failed posts.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BulkPostsRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Bulk creation results",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BulkPostsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/credits": {
      "get": {
        "tags": [
          "Credits"
        ],
        "summary": "Check credit balance and usage",
        "description": "Returns the current credit balance, usage this month, plan limit, and when credits reset. Agents should check this before operations that consume credits (AI generation, marketplace purchases).",
        "responses": {
          "200": {
            "description": "Credit balance and usage",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreditsResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/webhooks/{webhookId}": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Get a webhook by ID",
        "description": "Retrieve details of a single webhook by ID.",
        "parameters": [
          {
            "name": "webhookId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Webhook ID"
          }
        ],
        "responses": {
          "200": {
            "description": "Webhook details",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "events": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "is_active": {
                      "type": "boolean"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "put": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Update a webhook",
        "description": "Update a webhook's URL, subscribed events, or active status. All fields are optional \u2014 send only what you want to change.",
        "parameters": [
          {
            "name": "webhookId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Webhook ID to update"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateWebhookRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "events": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "is_active": {
                      "type": "boolean"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "summary": "Delete a webhook",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "name": "webhookId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Webhook deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "402": {
            "description": "API access not in plan",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "API key missing the webhooks:write scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Webhook not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyHeader": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "API key from your Lowtato dashboard (Settings page). Format: `lt_live_xxx`. Can also be passed as `Authorization: Bearer <key>`. Keys may carry scopes (posts:read, posts:write, accounts:read, accounts:write, analytics:read, ai:generate, webhooks:write); a key with NO scopes has full access. A request with a key that lacks the required scope returns HTTP 403 with error.code 'missing_scope'. A Free-tier key (no API access in plan) returns HTTP 402 with error.code 'api_access_not_in_plan'."
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Invalid or missing API key. Check that `x-api-key` header is set correctly.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "error": "Unauthorized"
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found. Verify the ID exists and belongs to your account.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "error": "Not found"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded. Wait `retryAfter` seconds before retrying. Limits: GET 60/min, POST/PATCH/DELETE 30/min, Upload 10/min.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "error": {
                  "type": "string",
                  "example": "Rate limit exceeded"
                },
                "retryAfter": {
                  "type": "integer",
                  "description": "Seconds until the rate limit resets. Agent should sleep this duration before retrying."
                }
              }
            }
          }
        }
      }
    },
    "schemas": {
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "description": "Stable machine-readable error code (e.g. unauthorized, payment_required, validation_error, not_found, rate_limited, internal_error)."
              },
              "message": {
                "type": "string",
                "description": "Human-readable description of what went wrong."
              },
              "details": {
                "description": "Optional structured details (e.g. credits_required, field errors). Shape varies by error code.",
                "nullable": true
              }
            }
          }
        },
        "required": [
          "error"
        ]
      },
      "HealthResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "healthy",
              "degraded",
              "unhealthy"
            ],
            "example": "healthy",
            "description": "Overall readiness. 'healthy' = all dependencies up; 'degraded' = a non-critical dependency (redis/r2/stripe) is impaired but the API is serving (still HTTP 200); 'unhealthy' = the critical dependency (db) is down (HTTP 503)."
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "checks": {
            "type": "object",
            "properties": {
              "db": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "up",
                      "down",
                      "degraded",
                      "skipped"
                    ]
                  },
                  "latency_ms": {
                    "type": "integer"
                  },
                  "error": {
                    "type": "string",
                    "description": "Only present for trusted (admin) callers."
                  }
                }
              },
              "redis": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "up",
                      "down",
                      "degraded",
                      "skipped"
                    ]
                  },
                  "latency_ms": {
                    "type": "integer"
                  },
                  "error": {
                    "type": "string",
                    "description": "Only present for trusted (admin) callers."
                  }
                }
              },
              "r2": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "up",
                      "down",
                      "degraded",
                      "skipped"
                    ]
                  },
                  "latency_ms": {
                    "type": "integer"
                  },
                  "error": {
                    "type": "string",
                    "description": "Only present for trusted (admin) callers."
                  }
                }
              },
              "stripe": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "up",
                      "down",
                      "degraded",
                      "skipped"
                    ]
                  },
                  "latency_ms": {
                    "type": "integer"
                  },
                  "error": {
                    "type": "string",
                    "description": "Only present for trusted (admin) callers."
                  }
                }
              }
            }
          }
        }
      },
      "CreatePostRequest": {
        "type": "object",
        "required": [
          "post"
        ],
        "properties": {
          "post": {
            "type": "object",
            "required": [
              "accountId",
              "content"
            ],
            "properties": {
              "accountId": {
                "type": "string",
                "format": "uuid",
                "description": "ID of the social account to post to. Get this from GET /api/v1/accounts."
              },
              "content": {
                "type": "object",
                "properties": {
                  "text": {
                    "type": "string",
                    "description": "Post text content. Respect platform character limits: Twitter 280, LinkedIn 3000, Threads 500."
                  },
                  "mediaUrls": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uri"
                    },
                    "description": "URLs of uploaded media files from POST /api/v1/media/upload (the API-key media endpoint)."
                  },
                  "platform": {
                    "type": "string",
                    "description": "Target platform (used as fallback for targetType)"
                  }
                }
              },
              "target": {
                "type": "object",
                "properties": {
                  "targetType": {
                    "type": "string",
                    "enum": [
                      "twitter",
                      "instagram",
                      "linkedin",
                      "tiktok",
                      "facebook",
                      "threads",
                      "bluesky",
                      "youtube",
                      "pinterest"
                    ],
                    "description": "Platform to publish to. Must match the account's platform."
                  }
                },
                "additionalProperties": true,
                "description": "Target configuration and platform-specific options"
              }
            }
          },
          "scheduledTime": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 timestamp to schedule the post. Must be in the future. Omit to save as draft."
          },
          "useNextFreeSlot": {
            "type": "boolean",
            "description": "If true, auto-schedule the post into the next available recurring time slot. Alternative to specifying scheduledTime."
          }
        }
      },
      "CreatePostResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Post ID. Store this to poll for publish status via GET /api/v1/posts/{postId}."
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "scheduled"
            ],
            "description": "'scheduled' if scheduledTime or useNextFreeSlot was set, 'draft' otherwise"
          }
        }
      },
      "PostDetail": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "scheduled",
              "publishing",
              "published",
              "failed"
            ],
            "description": "Current status. Poll until 'published' or 'failed'."
          },
          "content_text": {
            "type": "string",
            "description": "The post text content"
          },
          "target_type": {
            "type": "string",
            "description": "Target platform for this post"
          },
          "social_account_id": {
            "type": "string",
            "format": "uuid",
            "description": "ID of the social account this post is linked to"
          },
          "media_urls": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Media URLs attached to this post"
          },
          "public_url": {
            "type": "string",
            "format": "uri",
            "nullable": true,
            "description": "Public URL of the published post. Only set when status is 'published'."
          },
          "error_message": {
            "type": "string",
            "nullable": true,
            "description": "Error description if status is 'failed'. Use this for debugging or retry logic."
          },
          "scheduled_time": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When the post is scheduled for. Null if draft."
          },
          "published_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Timestamp when the post was published. Null if not yet published."
          }
        }
      },
      "AccountsListResponse": {
        "type": "object",
        "properties": {
          "accounts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Account"
            }
          }
        }
      },
      "Account": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Account ID. Use this as accountId when creating posts."
          },
          "platform": {
            "type": "string",
            "enum": [
              "twitter",
              "instagram",
              "linkedin",
              "tiktok",
              "facebook",
              "threads",
              "bluesky",
              "youtube",
              "pinterest"
            ],
            "description": "Social media platform this account belongs to"
          },
          "username": {
            "type": "string",
            "description": "Platform username or handle"
          },
          "fullname": {
            "type": "string",
            "nullable": true,
            "description": "Display name on the platform"
          },
          "profile_image_url": {
            "type": "string",
            "format": "uri",
            "nullable": true
          },
          "is_active": {
            "type": "boolean",
            "description": "Whether the account is active and can be posted to. Only use accounts where is_active is true."
          }
        }
      },
      "SubaccountsListResponse": {
        "type": "object",
        "properties": {
          "subaccounts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Subaccount"
            }
          }
        }
      },
      "Subaccount": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "external_id": {
            "type": "string",
            "description": "Platform-specific ID for this subaccount"
          },
          "name": {
            "type": "string",
            "description": "Display name (e.g. Facebook Page name, LinkedIn company name)"
          },
          "type": {
            "type": "string",
            "description": "Subaccount type (e.g. 'page', 'company')"
          }
        }
      },
      "ScheduledPostsResponse": {
        "type": "object",
        "properties": {
          "scheduledPosts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ScheduledPost"
            },
            "description": "Posts ordered by scheduled_time ascending"
          }
        }
      },
      "ScheduledPost": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "content_text": {
            "type": "string"
          },
          "target_type": {
            "type": "string",
            "description": "Target platform for this post"
          },
          "scheduled_time": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "type": "string",
            "example": "scheduled"
          }
        }
      },
      "PostFull": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "content_text": {
            "type": "string"
          },
          "target_type": {
            "type": "string"
          },
          "social_account_id": {
            "type": "string",
            "format": "uuid"
          },
          "media_urls": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "target_options": {
            "type": "object",
            "description": "Platform-specific options"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "scheduled",
              "publishing",
              "published",
              "failed"
            ]
          },
          "scheduled_time": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "published_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "public_url": {
            "type": "string",
            "nullable": true,
            "description": "Public URL of the published post"
          },
          "error_message": {
            "type": "string",
            "nullable": true,
            "description": "Error description if publishing failed"
          }
        }
      },
      "UpdateScheduledPostRequest": {
        "type": "object",
        "properties": {
          "content_text": {
            "type": "string",
            "description": "Updated post text. Only scheduled posts can be updated."
          },
          "scheduled_time": {
            "type": "string",
            "format": "date-time",
            "description": "Updated schedule time (ISO 8601). Must be in the future."
          }
        }
      },
      "SlotsListResponse": {
        "type": "object",
        "properties": {
          "slots": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ScheduleSlot"
            }
          }
        }
      },
      "ScheduleSlot": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "day": {
            "type": "integer",
            "minimum": 0,
            "maximum": 6,
            "description": "Day of week. 0=Sunday, 1=Monday, 2=Tuesday, 3=Wednesday, 4=Thursday, 5=Friday, 6=Saturday."
          },
          "hour": {
            "type": "integer",
            "minimum": 0,
            "maximum": 23,
            "description": "Hour in 24-hour format (UTC)"
          },
          "minute": {
            "type": "integer",
            "minimum": 0,
            "maximum": 59
          },
          "selected_targets": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Account IDs that will be posted to in this slot"
          }
        }
      },
      "CreateSlotsRequest": {
        "type": "object",
        "required": [
          "slots"
        ],
        "properties": {
          "slots": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "day",
                "hour",
                "minute"
              ],
              "properties": {
                "day": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 6,
                  "description": "Day of week. 0=Sunday, 1=Monday ... 6=Saturday."
                },
                "hour": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 23,
                  "description": "Hour in 24-hour format (UTC)"
                },
                "minute": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 59
                },
                "selectedTargets": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "Account IDs to post to in this slot"
                }
              }
            }
          }
        }
      },
      "CreateSourceRequest": {
        "type": "object",
        "properties": {
          "sourceType": {
            "type": "string",
            "enum": [
              "url",
              "text"
            ],
            "description": "Type of source. Use 'url' to extract from a webpage, 'text' for raw content."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "URL to extract content from. Required when sourceType is 'url'."
          },
          "text": {
            "type": "string",
            "description": "Raw text content. Required when sourceType is 'text'."
          },
          "customInstructions": {
            "type": "string",
            "description": "Instructions for AI extraction (e.g. 'Extract key points for a Twitter thread', 'Summarize into 3 bullets')"
          }
        }
      },
      "CreateSourceResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Source ID. Poll GET /api/v1/sources/{sourceId} to check extraction status."
          },
          "status": {
            "type": "string",
            "example": "queued",
            "description": "Initial status is always 'queued'. Will progress to 'processing' then 'ready' or 'failed'."
          }
        }
      },
      "SourceDetail": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "source_type": {
            "type": "string",
            "enum": [
              "url",
              "text"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "processing",
              "ready",
              "failed"
            ],
            "description": "Processing status. Poll until 'ready' or 'failed'."
          },
          "extracted_title": {
            "type": "string",
            "nullable": true,
            "description": "Extracted title. Populated when status is 'ready'."
          },
          "extracted_content": {
            "type": "string",
            "nullable": true,
            "description": "Extracted content. Populated when status is 'ready'."
          },
          "error_message": {
            "type": "string",
            "nullable": true,
            "description": "Error description if extraction failed."
          }
        }
      },
      "ConnectAccountRequest": {
        "type": "object",
        "required": [
          "platform",
          "access_token",
          "platform_user_id"
        ],
        "properties": {
          "platform": {
            "type": "string",
            "enum": [
              "twitter",
              "instagram",
              "linkedin",
              "tiktok",
              "facebook",
              "threads",
              "bluesky",
              "youtube",
              "pinterest"
            ],
            "description": "Social media platform to connect"
          },
          "access_token": {
            "type": "string",
            "description": "Valid OAuth access token for the platform"
          },
          "refresh_token": {
            "type": "string",
            "description": "Optional refresh token for automatic token renewal"
          },
          "platform_user_id": {
            "type": "string",
            "description": "User ID on the platform"
          },
          "platform_username": {
            "type": "string",
            "description": "Display username (e.g. @mybot)"
          }
        }
      },
      "ConnectAccountResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Lowtato account ID. Use this as accountId when creating posts."
          },
          "platform": {
            "type": "string"
          },
          "username": {
            "type": "string",
            "nullable": true
          },
          "connected": {
            "type": "boolean",
            "example": true
          }
        }
      },
      "CreateWebhookRequest": {
        "type": "object",
        "required": [
          "url",
          "events"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "HTTPS URL to receive webhook events"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "post.published",
                "post.failed",
                "post.scheduled",
                "account.connected",
                "account.disconnected",
                "credits.low",
                "schedule.published"
              ]
            },
            "description": "Events to subscribe to. New events: account.connected, account.disconnected, credits.low, schedule.published."
          }
        }
      },
      "WebhookResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "active": {
            "type": "boolean"
          },
          "secret": {
            "type": "string",
            "description": "Signing secret for verifying webhook payloads. Store securely."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ProfileResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "display_name": {
            "type": "string",
            "nullable": true
          },
          "plan": {
            "type": "string",
            "enum": [
              "free",
              "starter",
              "creator",
              "agency"
            ]
          },
          "credits_balance": {
            "type": "number"
          },
          "credits_monthly_allocation": {
            "type": "number"
          },
          "timezone": {
            "type": "string"
          },
          "language": {
            "type": "string"
          },
          "onboarding_completed": {
            "type": "boolean"
          },
          "account_count": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "UpdateProfileRequest": {
        "type": "object",
        "properties": {
          "display_name": {
            "type": "string",
            "maxLength": 100,
            "description": "User's display name"
          },
          "timezone": {
            "type": "string",
            "description": "IANA timezone (e.g. America/New_York)"
          },
          "notification_preferences": {
            "type": "object",
            "properties": {
              "email_post_published": {
                "type": "boolean"
              },
              "email_post_failed": {
                "type": "boolean"
              },
              "email_credits_low": {
                "type": "boolean"
              },
              "email_weekly_report": {
                "type": "boolean"
              }
            },
            "description": "Email notification preferences"
          }
        },
        "description": "At least one field must be provided"
      },
      "BrandKitResponse": {
        "type": "object",
        "properties": {
          "brand_name": {
            "type": "string",
            "nullable": true
          },
          "brand_color": {
            "type": "string",
            "nullable": true,
            "description": "Hex color code (e.g. #8FE000)"
          },
          "tone": {
            "type": "string",
            "nullable": true,
            "description": "Brand voice tone (e.g. professional, casual)"
          },
          "topics": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Content topics"
          },
          "logo_url": {
            "type": "string",
            "format": "uri",
            "nullable": true
          },
          "website": {
            "type": "string",
            "format": "uri",
            "nullable": true
          },
          "what_do_you_do": {
            "type": "string",
            "nullable": true
          },
          "who_do_you_help": {
            "type": "string",
            "nullable": true
          },
          "language": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "BrandKitRequest": {
        "type": "object",
        "properties": {
          "brand_name": {
            "type": "string",
            "maxLength": 200
          },
          "brand_color": {
            "type": "string",
            "pattern": "^#[0-9a-fA-F]{6}$",
            "description": "Hex color code"
          },
          "tone": {
            "type": "string",
            "maxLength": 100,
            "description": "Brand voice tone"
          },
          "topics": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "maxItems": 20
          },
          "logo_url": {
            "type": "string",
            "format": "uri"
          },
          "website": {
            "type": "string",
            "format": "uri"
          },
          "what_do_you_do": {
            "type": "string",
            "maxLength": 500,
            "description": "Products or services description"
          },
          "who_do_you_help": {
            "type": "string",
            "maxLength": 500,
            "description": "Target audience description"
          },
          "language": {
            "type": "string",
            "maxLength": 10
          }
        }
      },
      "OnboardingRequest": {
        "type": "object",
        "required": [
          "brand_name",
          "platforms"
        ],
        "properties": {
          "brand_name": {
            "type": "string",
            "maxLength": 200,
            "description": "Your brand or company name"
          },
          "platforms": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "twitter",
                "instagram",
                "linkedin",
                "tiktok",
                "facebook",
                "threads",
                "bluesky",
                "youtube",
                "pinterest"
              ]
            },
            "minItems": 1,
            "description": "Social platforms to configure"
          },
          "timezone": {
            "type": "string",
            "default": "America/New_York"
          },
          "posting_frequency": {
            "type": "string",
            "enum": [
              "once_daily",
              "daily",
              "twice_daily",
              "three_daily",
              "custom"
            ],
            "default": "daily"
          },
          "topics": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Content topics"
          },
          "what_do_you_do": {
            "type": "string",
            "maxLength": 500
          },
          "who_do_you_help": {
            "type": "string",
            "maxLength": 500
          },
          "user_type": {
            "type": "string",
            "enum": [
              "solo_creator",
              "marketing_team",
              "agency"
            ]
          }
        }
      },
      "OnboardingResponse": {
        "type": "object",
        "properties": {
          "completed": {
            "type": "boolean",
            "example": true
          },
          "brand_name": {
            "type": "string"
          },
          "platforms": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "timezone": {
            "type": "string"
          },
          "schedule_slots_created": {
            "type": "integer",
            "description": "Number of auto-generated schedule slots"
          }
        }
      },
      "AutoConfigureScheduleRequest": {
        "type": "object",
        "required": [
          "platforms"
        ],
        "properties": {
          "platforms": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "twitter",
                "instagram",
                "linkedin",
                "tiktok",
                "facebook",
                "threads",
                "bluesky",
                "youtube",
                "pinterest"
              ]
            },
            "minItems": 1,
            "description": "Platforms to configure schedule for"
          },
          "posts_per_day": {
            "type": "integer",
            "minimum": 1,
            "maximum": 10,
            "default": 2,
            "description": "Number of posts per day per platform"
          },
          "timezone": {
            "type": "string",
            "default": "America/New_York"
          },
          "include_weekends": {
            "type": "boolean",
            "default": false
          }
        }
      },
      "AutoConfigureScheduleResponse": {
        "type": "object",
        "properties": {
          "configured": {
            "type": "boolean",
            "example": true
          },
          "slots_created": {
            "type": "integer"
          },
          "platforms": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "posts_per_day": {
            "type": "integer"
          },
          "timezone": {
            "type": "string"
          },
          "slots": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "day": {
                  "type": "integer",
                  "description": "Day of week (0=Sunday)"
                },
                "hour": {
                  "type": "integer"
                },
                "minute": {
                  "type": "integer"
                }
              }
            }
          }
        }
      },
      "AnalyticsSummaryResponse": {
        "type": "object",
        "properties": {
          "total_posts": {
            "type": "integer",
            "description": "Total published posts in the period"
          },
          "total_engagement": {
            "type": "integer",
            "description": "Sum of likes, comments, and shares"
          },
          "posts_this_week": {
            "type": "integer"
          },
          "top_platform": {
            "type": "string",
            "description": "Platform with the most posts"
          },
          "best_posting_time": {
            "type": "string",
            "example": "10:00",
            "description": "Hour (UTC) with highest average engagement"
          },
          "engagement_trend": {
            "type": "string",
            "enum": [
              "up",
              "down",
              "stable"
            ],
            "description": "Engagement direction compared to previous period"
          },
          "period": {
            "type": "string",
            "enum": [
              "7d",
              "30d",
              "90d"
            ]
          },
          "platform_breakdown": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            },
            "description": "Post counts per platform"
          }
        }
      },
      "AIGenerateRequest": {
        "type": "object",
        "required": [
          "topic",
          "platform"
        ],
        "properties": {
          "topic": {
            "type": "string",
            "maxLength": 500,
            "description": "Topic or subject for the generated post"
          },
          "platform": {
            "type": "string",
            "enum": [
              "twitter",
              "instagram",
              "linkedin",
              "tiktok",
              "facebook",
              "threads",
              "bluesky",
              "youtube",
              "pinterest"
            ]
          },
          "tone": {
            "type": "string",
            "maxLength": 100,
            "description": "Desired tone (e.g. professional, casual, humorous)"
          },
          "include_hashtags": {
            "type": "boolean",
            "default": true
          },
          "custom_instructions": {
            "type": "string",
            "maxLength": 1000,
            "description": "Additional instructions for generation"
          }
        }
      },
      "AIGenerateResponse": {
        "type": "object",
        "properties": {
          "content": {
            "type": "string",
            "description": "Generated post content, markdown-stripped"
          },
          "hashtags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "character_count": {
            "type": "integer"
          },
          "platform": {
            "type": "string"
          },
          "credits_used": {
            "type": "number",
            "description": "Credits consumed for this generation"
          }
        }
      },
      "BulkPostsRequest": {
        "type": "object",
        "required": [
          "posts"
        ],
        "properties": {
          "posts": {
            "type": "array",
            "maxItems": 50,
            "items": {
              "type": "object",
              "required": [
                "content",
                "platforms"
              ],
              "properties": {
                "content": {
                  "type": "string",
                  "description": "Post text content"
                },
                "platforms": {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "enum": [
                      "twitter",
                      "instagram",
                      "linkedin",
                      "tiktok",
                      "facebook",
                      "threads",
                      "bluesky",
                      "youtube",
                      "pinterest"
                    ]
                  },
                  "description": "Target platforms for this post"
                },
                "account_id": {
                  "type": "string",
                  "format": "uuid",
                  "description": "Optional specific account ID"
                },
                "scheduled_at": {
                  "type": "string",
                  "format": "date-time",
                  "description": "ISO 8601 schedule time. Omit for draft."
                },
                "media_urls": {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "format": "uri"
                  },
                  "maxItems": 10
                }
              }
            }
          }
        }
      },
      "BulkPostsResponse": {
        "type": "object",
        "properties": {
          "created": {
            "type": "integer",
            "description": "Number of posts successfully created"
          },
          "failed": {
            "type": "integer",
            "description": "Number of posts that failed"
          },
          "posts": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "platform": {
                  "type": "string"
                },
                "status": {
                  "type": "string"
                }
              }
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "index": {
                  "type": "integer",
                  "description": "Index of the failed post in the input array"
                },
                "error": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "CreditsResponse": {
        "type": "object",
        "properties": {
          "balance": {
            "type": "number",
            "description": "Current credit balance"
          },
          "used_this_month": {
            "type": "number",
            "description": "Credits consumed this billing month"
          },
          "plan_limit": {
            "type": "number",
            "description": "Monthly credit allocation for current plan"
          },
          "plan": {
            "type": "string",
            "enum": [
              "free",
              "starter",
              "creator",
              "agency"
            ]
          },
          "resets_at": {
            "type": "string",
            "description": "Date when credits reset (YYYY-MM-DD)"
          }
        }
      },
      "UpdateWebhookRequest": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "New HTTPS webhook URL"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "post.published",
                "post.failed",
                "post.scheduled",
                "account.connected",
                "account.disconnected",
                "credits.low",
                "schedule.published"
              ]
            },
            "description": "Events to subscribe to"
          },
          "is_active": {
            "type": "boolean",
            "description": "Enable or disable the webhook"
          }
        },
        "description": "At least one field must be provided"
      }
    }
  },
  "tags": [
    {
      "name": "System",
      "description": "Health and status endpoints. Call GET /api/health first to verify API availability."
    },
    {
      "name": "Accounts",
      "description": "List and inspect connected social media accounts. Call GET /api/v1/accounts to discover account IDs needed for creating posts."
    },
    {
      "name": "Posts",
      "description": "Create posts and check publish status. The core workflow: POST to create, GET to poll status."
    },
    {
      "name": "Schedules",
      "description": "View, update, and delete scheduled posts in the publishing queue."
    },
    {
      "name": "Schedule Slots",
      "description": "Configure recurring time slots for auto-scheduling. Used with useNextFreeSlot: true when creating posts."
    },
    {
      "name": "Sources",
      "description": "AI-powered content extraction from URLs and text. Submit content, poll for results, use extracted text to create posts."
    },
    {
      "name": "Media",
      "description": "Upload images and videos. Use returned URLs in mediaUrls when creating posts."
    },
    {
      "name": "Marketplace",
      "description": "Browse and purchase pre-configured social media accounts. Ready for agent use with credentials included."
    },
    {
      "name": "Webhooks",
      "description": "Register webhook URLs to get notified on events. Supported: post.published, post.failed, post.scheduled, account.connected, account.disconnected, credits.low, schedule.published."
    },
    {
      "name": "Profile",
      "description": "View and update your user profile, timezone, and notification preferences. Agents should check profile first to understand account state."
    },
    {
      "name": "Brand Kit",
      "description": "Configure your brand settings (name, colors, tone, topics). Used by AI generation to personalize content."
    },
    {
      "name": "Onboarding",
      "description": "Complete account onboarding in a single API call. Replaces the 5-step UI wizard for programmatic setup."
    },
    {
      "name": "Analytics",
      "description": "Aggregated engagement analytics across all connected accounts. Supports 7d, 30d, and 90d periods."
    },
    {
      "name": "AI",
      "description": "AI-powered content generation. Generates platform-optimized posts using your brand kit for personalization. Costs 3 credits per generation."
    },
    {
      "name": "Credits",
      "description": "Check credit balance and usage. Agents should verify available credits before AI generation or marketplace purchases."
    }
  ]
}