openapi: 3.1.0
info:
  title: WoopSocial API
  version: 1.0.0
  summary: API for WoopSocial integrations.
  description: |
    This is the public-facing OpenAPI contract for WoopSocial. It supports scheduling posts for various social media platforms such as Facebook, Instagram, LinkedIn, LinkedIn Pages, Pinterest, Threads, TikTok, X (formerly Twitter) and YouTube.
servers:
  - url: https://api.woopsocial.com/v1
    description: WoopSocial API URL.
  - url: http://localhost:9123/api/external/v1
    description: Local WoopSocial API URL.
tags:
  - name: Posts
    description: Post scheduling endpoints.
  - name: Projects
    description: Project discovery endpoints.
  - name: Social Accounts
    description: Connected social account discovery endpoints.
  - name: Media
    description: Media upload endpoints.
  - name: Webhooks
    description: Webhook endpoint management.
  - name: Health
    description: Basic connectivity endpoints.
paths:
  /posts:
    post:
      operationId: createPost
      x-woopsocial-mcp-tool-name: posts_create
      x-woopsocial-mcp-simple-alias: posts_create_simple
      tags:
        - Posts
      security:
        - ApiKey: []
      summary: Create post
      description: |
        Creates a (parent) post with one or more social account (child) posts.

        All referenced social accounts must belong to the same project.

        The request is validated atomically. If any social account fails validation, the post is not created.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreatePostRequest"
            examples:
              Facebook:
                summary: Facebook post
                description: Create a Facebook image post and publish it immediately.
                value:
                  content:
                    - text: "<string>"
                      media:
                        - type: MEDIA_LIBRARY
                          mediaId: "<string>"
                  schedule:
                    type: PUBLISH_NOW
                  socialAccounts:
                    - platform: FACEBOOK
                      socialAccountId: "<string>"
                      postType: IMAGE
              Instagram:
                summary: Instagram post
                description: Create an Instagram feed post and publish it immediately.
                value:
                  content:
                    - text: "<string>"
                      media:
                        - type: MEDIA_LIBRARY
                          mediaId: "<string>"
                  schedule:
                    type: PUBLISH_NOW
                  socialAccounts:
                    - platform: INSTAGRAM
                      socialAccountId: "<string>"
                      postType: POST              
              Linkedin:
                summary: LinkedIn post
                description: Create a LinkedIn post and publish it immediately.
                value:
                  content:
                    - text: "<string>"
                      media:
                        - type: MEDIA_LIBRARY
                          mediaId: "<string>"
                  schedule:
                    type: PUBLISH_NOW
                  socialAccounts:
                    - platform: LINKEDIN
                      socialAccountId: "<string>"
              Linkedin Pages:
                summary: LinkedIn Page post
                description: Create a LinkedIn Page post and publish it immediately.
                value:
                  content:
                    - text: "<string>"
                      media:
                        - type: MEDIA_LIBRARY
                          mediaId: "<string>"
                  schedule:
                    type: PUBLISH_NOW
                  socialAccounts:
                    - platform: LINKEDIN_PAGES
                      socialAccountId: "<string>"
              Pinterest:
                summary: Pinterest pin
                description: Create a Pinterest pin and publish it immediately.
                value:
                  content:
                    - text: "<string>"
                      media:
                        - type: MEDIA_LIBRARY
                          mediaId: "<string>"
                  schedule:
                    type: PUBLISH_NOW
                  socialAccounts:
                    - platform: PINTEREST
                      socialAccountId: "<string>"
                      pinterestBoardId: "<string>"
              Threads:
                summary: Threads post
                description: Create a Threads post and publish it immediately.
                value:
                  content:
                    - text: "<string>"
                      media:
                        - type: MEDIA_LIBRARY
                          mediaId: "<string>"
                  schedule:
                    type: PUBLISH_NOW
                  socialAccounts:
                    - platform: THREADS
                      socialAccountId: "<string>"
              Tiktok:
                summary: TikTok post
                description: Create a TikTok photo post and publish it immediately.
                value:
                  content:
                    - text: "<string>"
                      media:
                        - type: MEDIA_LIBRARY
                          mediaId: "<string>"
                  schedule:
                    type: PUBLISH_NOW
                  socialAccounts:
                    - platform: TIKTOK
                      socialAccountId: "<string>"
                      postType: PHOTO
                      postMode: DIRECT_POST
                      privacyLevel: PUBLIC_TO_EVERYONE
                      allowComment: true
                      allowDuet: false
                      allowStitch: false
                      isYourBrand: false
                      isBrandedContent: false
                      autoAddMusic: true
              X:
                summary: X post
                description: Create an X post and publish it immediately.
                value:
                  content:
                    - text: "<string>"
                      media:
                        - type: MEDIA_LIBRARY
                          mediaId: "<string>"
                  schedule:
                    type: PUBLISH_NOW
                  socialAccounts:
                    - platform: X
                      socialAccountId: "<string>"
              Youtube:
                summary: YouTube post
                description: Create a YouTube video post and publish it immediately.
                value:
                  content:
                    - text: "<string>"
                      media:
                        - type: MEDIA_LIBRARY
                          mediaId: "<string>"
                  schedule:
                    type: PUBLISH_NOW
                  socialAccounts:
                    - platform: YOUTUBE
                      socialAccountId: "<string>"
                      title: "<string>"
                      privacy: public
              Woop Test:
                summary: WoopTest post
                description: Create a sandbox WoopTest post and publish it immediately.
                value:
                  content:
                    - text: "<string>"
                      media:
                        - type: MEDIA_LIBRARY
                          mediaId: "<string>"
                  schedule:
                    type: PUBLISH_NOW
                  socialAccounts:
                    - platform: WOOPTEST
                      socialAccountId: "<string>"
                      shouldSucceed: true
      responses:
        "201":
          description: Post created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Post"
        "422":
          description: Request validation failed.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreatePostErrorResponse"
        "429":
          $ref: "#/components/responses/TooManyConcurrentRequests"
        "500":
          description: Internal server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreatePostErrorResponse"
  /posts/validate:
    post:
      operationId: validatePost
      x-woopsocial-mcp-tool-name: posts_validate
      x-woopsocial-mcp-simple-alias: posts_validate_simple
      tags:
        - Posts
      security:
        - ApiKey: []
      summary: Validate post
      description: |
        Validates a post without creating one.

        This endpoint applies the same validation rules as `POST /posts`,
        including social account resolution, platform-specific validation, and
        media validation for referenced media library items.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreatePostRequest"
      responses:
        "200":
          description: Validation result.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ValidatePostResponse"
        "429":
          $ref: "#/components/responses/TooManyConcurrentRequests"
        "500":
          description: Internal server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreatePostErrorResponse"
  /posts/{postId}:
    get:
      operationId: getPost
      x-woopsocial-mcp-tool-name: posts_get
      tags:
        - Posts
      security:
        - ApiKey: []
      summary: Get post
      description: |
        Returns one post with its social account posts inline.
      parameters:
        - $ref: "#/components/parameters/PostId"
      responses:
        "200":
          description: Post found.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Post"
        "404":
          description: Post not found.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GetPostErrorResponse"
        "429":
          $ref: "#/components/responses/TooManyConcurrentRequests"
        "500":
          description: Internal server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GetPostErrorResponse"
    delete:
      operationId: deletePost
      x-woopsocial-mcp-tool-name: posts_delete
      tags:
        - Posts
      security:
        - ApiKey: []
      summary: Delete post
      description: |
        Deletes one scheduled post by post ID.

        A post can only be deleted when all of its social account deliveries are
        still `NOT_STARTED`.
      parameters:
        - $ref: "#/components/parameters/PostId"
      responses:
        "204":
          description: Post deleted.
        "404":
          description: Post not found.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletePostErrorResponse"
        "409":
          description: Post cannot be deleted in its current state.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletePostErrorResponse"
        "429":
          $ref: "#/components/responses/TooManyConcurrentRequests"
        "500":
          description: Internal server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletePostErrorResponse"
  /social-account-posts:
    get:
      operationId: listSocialAccountPosts
      x-woopsocial-mcp-tool-name: social_account_posts_list
      tags:
        - Posts
      security:
        - ApiKey: []
      summary: List social account posts
      description: |
        Returns standalone social account posts for the API key's organization.

        Each item includes the content, schedule, delivery status and latest stored analytics for a given social account target.
      parameters:
        - $ref: "#/components/parameters/ProjectIdsFilter"
        - $ref: "#/components/parameters/SocialAccountIdsFilter"
        - $ref: "#/components/parameters/PostIdsFilter"
        - $ref: "#/components/parameters/SocialPlatformFilter"
        - $ref: "#/components/parameters/DeliveryStatusFilter"
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/Cursor"
      responses:
        "200":
          description: Social account posts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListSocialAccountPostsResponse"
        "429":
          $ref: "#/components/responses/TooManyConcurrentRequests"
  /social-account-posts/{socialAccountPostId}:
    delete:
      operationId: deleteSocialAccountPost
      x-woopsocial-mcp-tool-name: social_account_posts_delete
      tags:
        - Posts
      security:
        - ApiKey: []
      summary: Delete social account post
      description: |
        Deletes one social account post by ID.
      parameters:
        - $ref: "#/components/parameters/SocialAccountPostId"
      responses:
        "204":
          description: Social account post deleted.
        "404":
          description: Social account post not found.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeleteSocialAccountPostErrorResponse"
        "429":
          $ref: "#/components/responses/TooManyConcurrentRequests"
        "500":
          description: Internal server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeleteSocialAccountPostErrorResponse"
  /projects:
    post:
      operationId: createProject
      x-woopsocial-mcp-tool-name: projects_create
      tags:
        - Projects
      security:
        - ApiKey: []
      summary: Create project
      description: |
        Creates a project in the API key's organization.

        Use projects to isolate social accounts, media, and posts for a
        specific user, client, or brand in your application.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateProjectRequest"
      responses:
        "201":
          description: Project created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Project"
        "429":
          $ref: "#/components/responses/TooManyConcurrentRequests"
    get:
      operationId: listProjects
      x-woopsocial-mcp-tool-name: projects_list
      tags:
        - Projects
      security:
        - ApiKey: []
      summary: List projects
      description: Returns projects that belong to the same organization as the API key. A project corresponds to a "Business Profile" in the UI.
      responses:
        "200":
          description: Projects available for media upload and post creation.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Project"
        "429":
          $ref: "#/components/responses/TooManyConcurrentRequests"
  /projects/{projectId}:
    delete:
      operationId: deleteProject
      x-woopsocial-mcp-tool-name: projects_delete
      tags:
        - Projects
      security:
        - ApiKey: []
      summary: Delete project
      description: |
        Permanently deletes one project and its related data, including
        connected social accounts, OAuth grants, media references, and posts.
      parameters:
        - $ref: "#/components/parameters/ProjectIdPath"
      responses:
        "204":
          description: Project deleted.
        "429":
          $ref: "#/components/responses/TooManyConcurrentRequests"
  /social-accounts/authorization-url:
    post:
      operationId: createOAuthAuthorization
      x-woopsocial-mcp-tool-name: oauth_create_authorization
      tags:
        - Social Accounts
      security:
        - ApiKey: []
      summary: Generate OAuth URL
      description: |
        Generates a browser authorization URL for connecting a new social account to a project.

        This endpoint is useful for multi-user integrations where your application lets
        your own users, clients, or brands connect their social accounts to WoopSocial
        without giving them access to your WoopSocial account.

        A common flow is:
        1. Create or select a WoopSocial project for your user, client, or brand.
        2. Call this endpoint from your backend with that `projectId`, the target `platform`,
           and a `redirectUrl` in your application.
        3. Open the returned `url` in your user's browser.
        4. After OAuth completes, WoopSocial redirects the browser back to `redirectUrl`
           with result query parameters.
        5. Use `projectId` and `socialAccountIds` from the redirect, or call
           `GET /social-accounts?projectId=...`, to store or confirm the connected account
           in your application.

        When `redirectUrl` is provided, the browser is redirected back to that URL after
        the OAuth callback is handled.

        For Facebook, WoopSocial shows a page-selection screen after authorization
        because Facebook may return more pages than the user appeared to select in
        the Facebook dialog in cases where the user has authorized with WoopSocial previously. The selected pages are connected to the single
        `projectId` from this request, then WoopSocial redirects back to
        `redirectUrl` when one was provided.

        When `redirectUrl` is provided, WoopSocial appends these query parameters on success:
        - `status=success`
        - `projectId`: the project identifier from the request
        - `platform`: the connected social platform
        - `socialAccountIds`: comma-separated connected social account identifiers. This may contain one or more IDs depending on the platform OAuth flow.

        When `redirectUrl` is provided, WoopSocial appends these query parameters on failure:
        - `status=error`
        - `projectId`: the project identifier from the request
        - `platform`: the requested social platform
        - `error`: an OAuth callback error code

        If the OAuth callback state is missing or expired, WoopSocial cannot safely
        determine the original `redirectUrl`, so the callback returns an HTTP error
        instead of redirecting.

        The redirect never includes OAuth tokens or credentials.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateOAuthAuthorizationRequest"
      responses:
        "200":
          description: OAuth authorization URL created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreateOAuthAuthorizationResponse"
        "429":
          $ref: "#/components/responses/TooManyConcurrentRequests"
  /social-accounts:
    get:
      operationId: listSocialAccounts
      x-woopsocial-mcp-tool-name: social_accounts_list
      tags:
        - Social Accounts
      security:
        - ApiKey: []
      summary: List social accounts
      description: |
        Returns social accounts that can currently be used for publishing.

        When `projectId` is provided, only social accounts usable within that project are returned.
      parameters:
        - $ref: "#/components/parameters/ProjectIdFilter"
      responses:
        "200":
          description: Social accounts.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/SocialAccount"
        "429":
          $ref: "#/components/responses/TooManyConcurrentRequests"
  /social-accounts/{socialAccountId}:
    delete:
      operationId: deleteSocialAccount
      x-woopsocial-mcp-tool-name: social_accounts_delete
      tags:
        - Social Accounts
      security:
        - ApiKey: []
      summary: Delete social account
      description: |
        Deletes one social account connection by ID.

        This removes the associated OAuth grant, including stored access and
        refresh tokens, so the account no longer counts toward the
        organization's active connected-account allowance.
      parameters:
        - $ref: "#/components/parameters/SocialAccountId"
      responses:
        "204":
          description: Social account connection deleted.
        "404":
          description: Social account not found.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeleteSocialAccountErrorResponse"
        "429":
          $ref: "#/components/responses/TooManyConcurrentRequests"
        "500":
          description: Internal server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeleteSocialAccountErrorResponse"
  /social-accounts/{socialAccountId}/platform-inputs:
    get:
      operationId: getSocialAccountPlatformInputs
      x-woopsocial-mcp-tool-name: social_accounts_get_platform_inputs
      tags:
        - Social Accounts
      security:
        - ApiKey: []
      summary: Get platform-specific input options
      description: |
        Returns platform-specific input options for a connected social account.

        Use this endpoint to discover valid values for flattened post-target
        fields such as `pinterestBoardId` and `privacyLevel`.
      parameters:
        - $ref: "#/components/parameters/SocialAccountId"
      responses:
        "200":
          description: Social account-specific platform input options.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SocialAccountPlatformInputs"
        "429":
          $ref: "#/components/responses/TooManyConcurrentRequests"
  /media:
    get:
      operationId: listMedia
      x-woopsocial-mcp-tool-name: media_list
      tags:
        - Media
      security:
        - ApiKey: []
      summary: List media
      description: |
        Returns media library items that belong to the API key's organization.

        When `projectId` is provided, only media in that project is returned.
      parameters:
        - $ref: "#/components/parameters/ProjectIdFilter"
        - $ref: "#/components/parameters/MediaIdsFilter"
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/Cursor"
      responses:
        "200":
          description: Media items.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListMediaResponse"
        "429":
          $ref: "#/components/responses/TooManyConcurrentRequests"
    post:
      operationId: createMedia
      x-woopsocial-mcp-exclude: true
      tags:
        - Media
      security:
        - ApiKey: []
      summary: Upload media
      description: |
        Uploads a media file in a single request and creates a media item.

        The server determines the media MIME type from the uploaded file.
        Send a `multipart/form-data` request with the file in the `file` field.

        Example:

        ```bash
        curl -X POST 'https://api.woopsocial.com/v1/media?projectId=YOUR_PROJECT_ID' \
          -H 'Authorization: Bearer YOUR_API_KEY' \
          -F 'file=@/path/to/image.jpg'
        ```

        Use this endpoint for straightforward uploads up to 100 MB.

        For larger files, or when you need presigned part URLs, use the upload
        session flow under `/media/upload-sessions`.
      parameters:
        - $ref: "#/components/parameters/ProjectId"
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
                - file
              properties:
                file:
                  type: string
                  format: binary
              description: |
                Media file to upload. Maximum size is 100 MB.
      responses:
        "200":
          description: Media uploaded.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreateMediaResponse"
        "429":
          $ref: "#/components/responses/TooManyConcurrentRequests"
  /media/{mediaId}:
    delete:
      operationId: deleteMedia
      x-woopsocial-mcp-tool-name: media_delete
      tags:
        - Media
      security:
        - ApiKey: []
      summary: Delete media
      description: |
        Deletes one media item by ID.
      parameters:
        - $ref: "#/components/parameters/MediaId"
      responses:
        "204":
          description: Media deleted.
        "404":
          description: Media not found.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeleteMediaErrorResponse"
        "429":
          $ref: "#/components/responses/TooManyConcurrentRequests"
        "500":
          description: Internal server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeleteMediaErrorResponse"
  /media/upload-sessions:
    post:
      operationId: createUploadSession
      x-woopsocial-mcp-tool-name: media_uploads_create_session
      x-woopsocial-mcp-description: |
        This endpoint can be used to upload both smaller and larger files (up to 5GB) in a chunked manner.
        Calling this creates an upload session and returns presigned URLs for uploading the file in parts.
        
        The presigned URLs are hosted on Cloudflare. If your MCP client has restrictions on which domains it is allowed to send data to, you will need to add `r2.cloudflarestorage.com` to your MCP client's approved domains.
        On Claude Desktop, you can do this in Settings > Capabilities > Scroll Down > Domain Allowlist > enter 'r2.cloudflarestorage.com'.
      tags:
        - Media
      security:
        - ApiKey: []
      summary: Start media upload session
      description: |
        This endpoint can be used to upload both smaller and larger files (up to 5GB) in a chunked manner.
        Calling this creates an upload session and returns presigned URLs for uploading the file in parts.

        Upload the file in `partCount` parts, using the matching
        `parts[n].uploadUrl` for each part number.

        Every part except the last must be exactly `partSizeInBytes` bytes. The
        last part may be smaller. After all parts have been uploaded, call
        `/media/upload-sessions/{uploadSessionId}/complete` to finalize the upload
        and create the media item.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateUploadSessionRequest"
      responses:
        "200":
          description: Upload session created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UploadSession"
        "429":
          $ref: "#/components/responses/TooManyConcurrentRequests"
  /media/upload-sessions/{uploadSessionId}:
    get:
      operationId: getUploadSession
      x-woopsocial-mcp-tool-name: media_uploads_get_session
      tags:
        - Media
      security:
        - ApiKey: []
      summary: Get media upload session status
      parameters:
        - $ref: "#/components/parameters/UploadSessionId"
      responses:
        "200":
          description: Current upload status.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UploadSessionStatus"
        "429":
          $ref: "#/components/responses/TooManyConcurrentRequests"
  /media/upload-sessions/{uploadSessionId}/complete:
    post:
      operationId: completeUploadSession
      x-woopsocial-mcp-tool-name: media_uploads_complete_session
      x-woopsocial-mcp-description: Completes the upload session started by `createUploadSession`.
      tags:
        - Media
      security:
        - ApiKey: []
      summary: Complete media upload session
      description: Call this to finalize the upload started by calling [`/media/upload-sessions`](/api-reference/media/start-media-upload-session).
      parameters:
        - $ref: "#/components/parameters/UploadSessionId"
      responses:
        "200":
          description: Upload completion accepted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UploadSessionStatus"
        "429":
          $ref: "#/components/responses/TooManyConcurrentRequests"
  /webhooks:
    post:
      operationId: createWebhookEndpoint
      x-woopsocial-mcp-tool-name: webhooks_create_endpoint
      tags:
        - Webhooks
      security:
        - ApiKey: []
      summary: Register webhook endpoint
      description: |
        Registers a URL to receive webhook events for the specified event types.

        The response includes a `signingSecret` (base64-encoded, 32 bytes) that is
        returned **once only**. Use it to verify the `X-Woop-Signature` header on
        incoming webhook payloads.

        **Signature format:** `t=<unix>,v1=<hmac-sha256-hex>`
        **Signed payload:** `<unix>.<json-body>`
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateWebhookEndpointRequest"
      responses:
        "201":
          description: Webhook endpoint registered.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookEndpoint"
        "422":
          description: Validation error (unknown event type, empty url, etc.).
        "429":
          $ref: "#/components/responses/TooManyConcurrentRequests"
    get:
      operationId: listWebhookEndpoints
      x-woopsocial-mcp-tool-name: webhooks_list_endpoints
      tags:
        - Webhooks
      security:
        - ApiKey: []
      summary: List webhook endpoints
      description: Returns all registered webhook endpoints for the organization. The `signingSecret` field is omitted.
      responses:
        "200":
          description: Webhook endpoints.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/WebhookEndpoint"
        "429":
          $ref: "#/components/responses/TooManyConcurrentRequests"
  /webhooks/{webhookId}:
    delete:
      operationId: deleteWebhookEndpoint
      x-woopsocial-mcp-tool-name: webhooks_delete_endpoint
      tags:
        - Webhooks
      security:
        - ApiKey: []
      summary: Delete webhook endpoint
      parameters:
        - name: webhookId
          in: path
          description: Webhook endpoint identifier.
          required: true
          schema:
            type: string
      responses:
        "204":
          description: Endpoint deleted.
        "404":
          description: Endpoint not found.
        "429":
          $ref: "#/components/responses/TooManyConcurrentRequests"
  /health:
    get:
      operationId: getHealth
      x-woopsocial-mcp-tool-name: health_get
      tags:
        - Health
      security:
        - ApiKey: []
      summary: Health check 
      description: Returns a minimal response proving the WoopSocial API is reachable.
      responses:
        "200":
          description: Successful response.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HealthResponse"
        "429":
          $ref: "#/components/responses/TooManyConcurrentRequests"
components:
  parameters:
    ProjectIdFilter:
      name: projectId
      in: query
      required: false
      schema:
        type: string
      description: Filter to resources that belong to a specific project.
    ProjectIdsFilter:
      name: projectId
      in: query
      required: false
      style: form
      explode: false
      schema:
        type: array
        items:
          type: string
      description: Filter to one or more projects. Provide a comma-separated list of project IDs.
    SocialAccountIdsFilter:
      name: socialAccountId
      in: query
      required: false
      style: form
      explode: false
      schema:
        type: array
        items:
          type: string
      description: Filter to one or more connected social accounts. Provide a comma-separated list of social account IDs.
    PostIdsFilter:
      name: postId
      in: query
      required: false
      style: form
      explode: false
      schema:
        type: array
        items:
          type: string
      description: Filter to one or more parent posts. Provide a comma-separated list of post IDs.
    MediaIdsFilter:
      name: mediaId
      in: query
      required: false
      style: form
      explode: false
      schema:
        type: array
        items:
          type: string
      description: Filter to one or more media items. Provide a comma-separated list of media IDs.
    SocialPlatformFilter:
      name: platform
      in: query
      required: false
      style: form
      explode: false
      schema:
        type: array
        items:
          $ref: "#/components/schemas/SocialPlatform"
      description: Filter to one or more social platforms. Provide a comma-separated list of platform values.
    DeliveryStatusFilter:
      name: deliveryStatus
      in: query
      required: false
      style: form
      explode: false
      schema:
        type: array
        items:
          $ref: "#/components/schemas/DeliveryStatus"
      description: Filter to one or more delivery statuses. Provide a comma-separated list of delivery status values.
    Limit:
      name: limit
      in: query
      required: false
      schema:
        type: integer
        format: int32
        minimum: 1
        maximum: 100
        default: 50
      description: Number of items to return.
    Cursor:
      name: cursor
      in: query
      required: false
      schema:
        type: string
      description: Opaque cursor for the next page of results.
    ProjectId:
      name: projectId
      in: query
      required: true
      schema:
        type: string
      description: Project that will own the uploaded media.
    ProjectIdPath:
      name: projectId
      in: path
      required: true
      schema:
        type: string
      description: Project identifier.
    SocialAccountId:
      name: socialAccountId
      in: path
      required: true
      schema:
        type: string
      description: Connected social account identifier.
    SocialAccountPostId:
      name: socialAccountPostId
      in: path
      required: true
      schema:
        type: string
      description: Social account post identifier.
    PostId:
      name: postId
      in: path
      required: true
      schema:
        type: string
      description: Post identifier.
    UploadSessionId:
      name: uploadSessionId
      in: path
      required: true
      schema:
        type: string
      description: Media upload session identifier.
    MediaId:
      name: mediaId
      in: path
      required: true
      schema:
        type: string
      description: Media identifier.
  securitySchemes:
    ApiKey:
      type: http
      scheme: bearer
      bearerFormat: API key
  responses:
    TooManyConcurrentRequests:
      description: Too many requests are already in progress for this API key.
      headers:
        Retry-After:
          description: Number of seconds to wait before retrying.
          required: true
          schema:
            type: integer
            format: int32
            minimum: 1
            example: 1
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ConcurrencyLimitErrorResponse"
  schemas:
    ConcurrencyLimitErrorCode:
      type: string
      enum:
        - TOO_MANY_CONCURRENT_REQUESTS
    ConcurrencyLimitErrorResponse:
      type: object
      additionalProperties: false
      required:
        - code
        - message
      properties:
        code:
          $ref: "#/components/schemas/ConcurrencyLimitErrorCode"
        message:
          type: string
    WebhookEventType:
      type: string
      enum:
        - socialAccountPost.delivery.published
        - socialAccountPost.delivery.failed
    CreateWebhookEndpointRequest:
      type: object
      required:
        - url
        - eventTypes
      properties:
        url:
          type: string
          description: The HTTPS URL to POST webhook events to.
        eventTypes:
          type: array
          description: Webhook event types this endpoint should receive.
          items:
            $ref: "#/components/schemas/WebhookEventType"
          minItems: 1
    WebhookEndpoint:
      type: object
      required:
        - id
        - url
        - eventTypes
        - createdAt
      properties:
        id:
          type: string
          description: Webhook endpoint identifier (int64 as string).
        url:
          type: string
        eventTypes:
          type: array
          items:
            $ref: "#/components/schemas/WebhookEventType"
        createdAt:
          type: string
          format: date-time
        signingSecret:
          type: string
          description: Base64-encoded 32-byte signing secret. Returned once on creation only.
    WebhookEvent:
      type: object
      description: Payload delivered to registered webhook URLs.
      required:
        - eventType
        - occurredAt
        - postId
        - socialAccountPostId
        - socialAccountId
        - platform
      properties:
        eventType:
          $ref: "#/components/schemas/WebhookEventType"
        occurredAt:
          type: string
          format: date-time
        postId:
          type: string
        socialAccountPostId:
          type: string
        socialAccountId:
          type: string
        platform:
          $ref: "#/components/schemas/SocialPlatform"
        externalPostId:
          type: string
          description: Platform's own post identifier. Present on succeeded events.
        externalPostUrl:
          type: string
          description: Public URL of the post on the platform. Present on succeeded events.
        errorMessage:
          type: string
          description: Error detail. Present on failed events.
    Project:
      type: object
      additionalProperties: false
      required:
        - id
        - name
      properties:
        id:
          type: string
          description: Project identifier.
        name:
          type: string
          description: Human-readable project name.
    CreateProjectRequest:
      type: object
      additionalProperties: false
      required:
        - name
      properties:
        name:
          type: string
          description: Human-readable project name.
    SocialAccountStatus:
      type: string
      enum:
        - CONNECTED
        - DISCONNECTED
    SocialPlatform:
      type: string
      enum:
        - PINTEREST
        - LINKEDIN
        - LINKEDIN_PAGES
        - INSTAGRAM
        - FACEBOOK
        - THREADS
        - TIKTOK
        - X
        - YOUTUBE
        - WOOPTEST
      description: |
        Identifies which social media platform this data structure targets.
    CreateOAuthAuthorizationRequest:
      type: object
      additionalProperties: false
      required:
        - projectId
        - platform
      properties:
        projectId:
          type: string
          description: Project identifier.
        platform:
          $ref: "#/components/schemas/SocialPlatform"
        redirectUrl:
          type: string
          format: uri
          example: "https://app.example.com/oauth/complete"
          description: |
            Optional URL in your application to return the browser to after OAuth completes.

            Use this for multi-user integrations where your users connect their own social
            accounts through your app. WoopSocial appends OAuth result query parameters to
            this URL so your app can finish the connection flow.

            The redirect does not include tokens or credentials.
    OAuthCallbackErrorCode:
      type: string
      enum:
        - access_denied
        - provider_error
        - missing_code
        - oauth_client_unavailable
        - token_exchange_failed
        - no_tokens
        - account_persistence_failed
        - grant_persistence_failed
        - connection_session_persistence_failed
      description: |
        Error code appended to `redirectUrl` as the `error` query parameter when
        OAuth fails after WoopSocial has recovered the callback state.
    CreateOAuthAuthorizationResponse:
      type: object
      additionalProperties: false
      required:
        - url
      properties:
        url:
          type: string
          format: uri
          description: Browser URL that starts the OAuth authorization flow.
    SocialAccount:
      type: object
      additionalProperties: false
      required:
        - id
        - externalAccountId
        - platform
        - username
        - imageUrl
        - status
      properties:
        id:
          type: string
          description: Social account identifier.
        externalAccountId:
          type: string
          description: Platform-specific account, page, profile, or channel identifier.
        platform:
          $ref: "#/components/schemas/SocialPlatform"
          description: Social platform for the connected account.
        username:
          type: string
          description: Display name or username for the connected account.
        imageUrl:
          type: string
          format: uri
          description: Profile image URL for the connected account.
        status:
          $ref: "#/components/schemas/SocialAccountStatus"
          description: Connection status of the account.
    SocialAccountPlatformInputs:
      type: object
      additionalProperties: false
      required:
        - socialAccountId
        - platformSpecificInputs
      properties:
        socialAccountId:
          type: string
          description: Connected social account identifier.
        platformSpecificInputs:
          $ref: "#/components/schemas/SocialAccountPlatformSpecificInputs"
    SocialAccountPlatformSpecificInputs:
      oneOf:
        - $ref: "#/components/schemas/PinterestSocialAccountPlatformSpecificInputs"
        - $ref: "#/components/schemas/TikTokSocialAccountPlatformSpecificInputs"
      discriminator:
        propertyName: platform
        mapping:
          PINTEREST: "#/components/schemas/PinterestSocialAccountPlatformSpecificInputs"
          TIKTOK: "#/components/schemas/TikTokSocialAccountPlatformSpecificInputs"
    PinterestSocialAccountPlatformSpecificInputs:
      title: Pinterest Social Account Platform Specific Inputs
      type: object
      additionalProperties: false
      required:
        - platform
        - boards
      properties:
        platform:
          allOf:
            - $ref: "#/components/schemas/SocialPlatform"
            - type: string
              enum:
                - PINTEREST
        boards:
          type: array
          description: |
            Available Pinterest boards for this account.

            Each `id` value can be sent as `pinterestBoardId` when creating a
            post for a Pinterest social account target.
          items:
            $ref: "#/components/schemas/PinterestBoardOption"
    TikTokSocialAccountPlatformSpecificInputs:
      title: TikTok Social Account Platform Specific Inputs
      type: object
      additionalProperties: false
      required:
        - platform
        - privacyLevelOptions
        - commentAvailable
        - duetAvailable
        - stitchAvailable
        - maxVideoPostDurationSec
      properties:
        platform:
          allOf:
            - $ref: "#/components/schemas/SocialPlatform"
            - type: string
              enum:
                - TIKTOK
        privacyLevelOptions:
          type: array
          description: |
            Privacy values currently allowed for this TikTok account.

            Each value can be sent as `privacyLevel` when creating a post for a
            TikTok social account target.
          items:
            $ref: "#/components/schemas/TikTokPrivacyLevel"
        commentAvailable:
          type: boolean
          description: Whether comments can currently be allowed for this TikTok account.
        duetAvailable:
          type: boolean
          description: Whether duets can currently be allowed for video posts on this TikTok account.
        stitchAvailable:
          type: boolean
          description: Whether stitches can currently be allowed for video posts on this TikTok account.
        maxVideoPostDurationSec:
          type: integer
          format: int32
          minimum: 1
          description: Maximum TikTok video duration currently allowed for this account.
    PinterestBoardOption:
      type: object
      additionalProperties: false
      required:
        - id
        - name
      properties:
        id:
          type: string
          description: Pinterest board identifier.
        name:
          type: string
          description: Human-readable Pinterest board name.
    DeliveryStatus:
      type: string
      description: |
        Delivery lifecycle status for a post.

        `NOT_STARTED`: The post exists and is scheduled, but delivery has not started yet.
        `SENDING`: Delivery is currently in progress.
        `PUBLISHED`: Delivery completed successfully.
        `FAILED`: Delivery completed unsuccessfully.
      enum:
        - NOT_STARTED
        - SENDING
        - PUBLISHED
        - FAILED
    PostContentMediaInput:
      description: |
        Content media reference.
      oneOf:
        - $ref: "#/components/schemas/MediaLibraryPostContentMediaInput"
      discriminator:
        propertyName: type
        mapping:
          MEDIA_LIBRARY: "#/components/schemas/MediaLibraryPostContentMediaInput"
    MediaLibraryPostContentMediaInput:
      title: Media Library Input
      type: object
      additionalProperties: false
      required:
        - type
        - mediaId
      properties:
        type:
          type: string
          enum:
            - MEDIA_LIBRARY
        mediaId:
          type: string
          description: Media identifier create by calling [`POST /media/upload-sessions`](/api-reference/media/start-media-upload-session) or [`POST /media`](/api-reference/media/upload-media).
    PostContentMedia:
      description: |
        Resolved content media.
      oneOf:
        - $ref: "#/components/schemas/MediaLibraryPostContentMedia"
      discriminator:
        propertyName: type
        mapping:
          MEDIA_LIBRARY: "#/components/schemas/MediaLibraryPostContentMedia"
    MediaLibraryPostContentMedia:
      title: Media Library Post Content Media
      type: object
      additionalProperties: false
      required:
        - type
        - mediaId
        - mediaType
        - url
        - thumbnailUrl
      properties:
        type:
          type: string
          enum:
            - MEDIA_LIBRARY
        mediaId:
          type: string
          description: Media identifier from the media library.
        mediaType:
          $ref: "#/components/schemas/MediaType"
        url:
          type: string
          format: uri
          description: Canonical media URL.
        thumbnailUrl:
          type: string
          format: uri
          description: Thumbnail or preview URL for the media item.
    PostContentItemInput:
      type: object
      additionalProperties: false
      minProperties: 1
      properties:
        text:
          type: string
          description: Text content for this thread item.
        media:
          type: array
          items:
            $ref: "#/components/schemas/PostContentMediaInput"
    PostContentItem:
      type: object
      additionalProperties: false
      minProperties: 1
      properties:
        text:
          type: string
          description: Text content for this thread item.
        media:
          type: array
          items:
            $ref: "#/components/schemas/PostContentMedia"
    PostContentInput:
      type: array
      minItems: 1
      maxItems: 1
      description: |
        Post content expressed as thread items.

        The array exists for future thread support. Currently exactly one item
        is supported.
      items:
        $ref: "#/components/schemas/PostContentItemInput"
    PostContent:
      type: array
      minItems: 1
      maxItems: 1
      description: |
        Post content expressed as thread items.

        The array exists for future thread support. Currently exactly one item
        is returned.
      items:
        $ref: "#/components/schemas/PostContentItem"
    PostSchedule:
      description: When the post should be published.
      oneOf:
        - $ref: "#/components/schemas/DraftPostSchedule"
        - $ref: "#/components/schemas/PublishNowPostSchedule"
        - $ref: "#/components/schemas/ScheduleForLaterPostSchedule"
      discriminator:
        propertyName: type
        mapping:
          DRAFT: "#/components/schemas/DraftPostSchedule"
          PUBLISH_NOW: "#/components/schemas/PublishNowPostSchedule"
          SCHEDULE_FOR_LATER: "#/components/schemas/ScheduleForLaterPostSchedule"
    DraftPostSchedule:
      title: Draft
      type: object
      additionalProperties: false
      required:
        - type
      properties:
        type:
          type: string
          enum:
            - DRAFT
    PublishNowPostSchedule:
      title: Publish Now
      type: object
      additionalProperties: false
      required:
        - type
      properties:
        type:
          type: string
          enum:
            - PUBLISH_NOW
    ScheduleForLaterPostSchedule:
      title: Schedule For Later
      type: object
      additionalProperties: false
      required:
        - type
        - scheduledFor
      properties:
        type:
          type: string
          enum:
            - SCHEDULE_FOR_LATER
        scheduledFor:
          type: string
          format: date-time
          description: |
            UTC time (ISO 8601) when the post should be published. The value must be in the future.
    PostRetryPolicy:
      type: object
      additionalProperties: false
      description: |
        Automatically creates a scheduled retry post for each failed target.
        Successful targets are never copied. Known failures requiring user
        intervention, such as insufficient credits or disconnected accounts,
        are not automatically retried. If the platform published a post but
        its response was lost, retrying may produce a duplicate.
      required:
        - maxRetries
        - delaySeconds
      properties:
        maxRetries:
          type: integer
          minimum: 1
          maximum: 5
          description: Maximum automatic retries per target, excluding the original attempt.
        delaySeconds:
          type: integer
          minimum: 60
          maximum: 86400
          description: Minimum wait after each failure before the retry is published.
    CreatePostRequest:
      type: object
      additionalProperties: false
      required:
        - content
        - schedule
        - socialAccounts
      properties:
        content:
          $ref: "#/components/schemas/PostContentInput"
        schedule:
          $ref: "#/components/schemas/PostSchedule"
        autoDeleteMediaAfterPublish:
          type: boolean
          default: false
          description: |
            When true, all media referenced by this post is automatically
            deleted from the media library after all social account deliveries
            for the post have published successfully.
        retryPolicy:
          $ref: "#/components/schemas/PostRetryPolicy"
          description: Automatic retry policy. Omit to disable automatic retries.
        socialAccounts:
          type: array
          minItems: 1
          description: |
            Social account targets for this post.

            All referenced social accounts must belong to the same project.
            Duplicate `socialAccountId` values are invalid.
          items:
            $ref: "#/components/schemas/SocialAccountInput"
    Post:
      type: object
      additionalProperties: false
      required:
        - id
        - projectId
        - content
        - schedule
        - autoDeleteMediaAfterPublish
        - socialAccountPosts
        - createdAt
        - updatedAt
      properties:
        id:
          type: string
          description: Post identifier.
        projectId:
          type: string
          description: Project identifier that the selected social accounts belong to.
        content:
          $ref: "#/components/schemas/PostContent"
        schedule:
          $ref: "#/components/schemas/PostSchedule"
        autoDeleteMediaAfterPublish:
          type: boolean
          description: Whether media auto-delete-after-publish is enabled for this post.
        retryPolicy:
          $ref: "#/components/schemas/PostRetryPolicy"
        socialAccountPosts:
          type: array
          minItems: 1
          description: Platform-specific post instances.
          items:
            $ref: "#/components/schemas/SocialAccountPost"
        createdAt:
          type: string
          format: date-time
          description: UTC time when the post was created.
        updatedAt:
          type: string
          format: date-time
          description: UTC time when the post was last updated.
    ListSocialAccountPostsResponse:
      type: object
      additionalProperties: false
      required:
        - socialAccountPosts
      properties:
        socialAccountPosts:
          type: array
          items:
            $ref: "#/components/schemas/SocialAccountPost"
        nextCursor:
          type: string
          description: Opaque cursor for the next page of results. Omitted when there are no more results.
    MediaType:
      type: string
      enum:
        - IMAGE
        - VIDEO
    Media:
      type: object
      additionalProperties: false
      required:
        - id
        - projectId
        - mediaType
        - url
        - createdAt
      properties:
        id:
          type: string
          description: Media identifier.
        projectId:
          type: string
          description: Project identifier.
        mediaType:
          $ref: "#/components/schemas/MediaType"
        url:
          type: string
          format: uri
          description: Canonical media URL.
        createdAt:
          type: string
          format: date-time
          description: UTC time when the media item was created.
    ListMediaResponse:
      type: object
      additionalProperties: false
      required:
        - media
      properties:
        media:
          type: array
          items:
            $ref: "#/components/schemas/Media"
        nextCursor:
          type: string
          description: Opaque cursor for the next page of results. Omitted when there are no more results.
    CreateMediaResponse:
      type: object
      additionalProperties: false
      required:
        - mediaId
      properties:
        mediaId:
          type: string
          description: Media identifier.
    CreateUploadSessionRequest:
      type: object
      additionalProperties: false
      required:
        - projectId
        - fileSizeInBytes
      properties:
        projectId:
          type: string
          description: Project that will own the uploaded media.
        fileSizeInBytes:
          type: integer
          format: int64
          minimum: 1
          maximum: 5368709120
          description: Total size of the file that will be uploaded.
    UploadSessionPart:
      type: object
      additionalProperties: false
      required:
        - partNumber
        - uploadUrl
      properties:
        partNumber:
          type: integer
          format: int32
          minimum: 1
          description: 1-based part number to upload to this URL.
        uploadUrl:
          type: string
          format: uri
          description: Presigned URL that accepts the bytes for `partNumber`.
    UploadSession:
      type: object
      additionalProperties: false
      required:
        - uploadSessionId
        - partSizeInBytes
        - partCount
        - expiresAt
        - parts
      properties:
        uploadSessionId:
          type: string
          description: Media upload session identifier.
        partSizeInBytes:
          type: integer
          format: int64
          description: |
            Maximum chunk size to use when slicing the file into parts.

            For uploads with more than one part, every part except the last must
            be exactly this size. The last part may be smaller. For a single-part
            upload, the only part may be smaller than this size.
        partCount:
          type: integer
          format: int32
          description: Number of parts that must be uploaded before completion.
        expiresAt:
          type: string
          format: date-time
          description: Time when the presigned part upload URLs expire.
        parts:
          type: array
          description: Presigned upload targets, in part-number order.
          items:
            $ref: "#/components/schemas/UploadSessionPart"
            description: Presigned URL and part number for uploading one chunk of the file.
    UploadSessionStatus:
      oneOf:
        - $ref: "#/components/schemas/UploadSessionInitiatedStatus"
        - $ref: "#/components/schemas/UploadSessionUploadedStatus"
        - $ref: "#/components/schemas/UploadSessionReadyStatus"
        - $ref: "#/components/schemas/UploadSessionFailedStatus"
        - $ref: "#/components/schemas/UploadSessionAbortedStatus"
      discriminator:
        propertyName: status
        mapping:
          INITIATED: "#/components/schemas/UploadSessionInitiatedStatus"
          UPLOADED: "#/components/schemas/UploadSessionUploadedStatus"
          READY: "#/components/schemas/UploadSessionReadyStatus"
          FAILED: "#/components/schemas/UploadSessionFailedStatus"
          ABORTED: "#/components/schemas/UploadSessionAbortedStatus"
    UploadSessionInitiatedStatus:
      title: Upload Session Initiated Status
      type: object
      additionalProperties: false
      required:
        - uploadSessionId
        - status
      properties:
        uploadSessionId:
          type: string
          description: Media upload session identifier.
        status:
          type: string
          enum:
            - INITIATED
    UploadSessionUploadedStatus:
      title: Upload Session Uploaded Status
      type: object
      additionalProperties: false
      required:
        - uploadSessionId
        - status
      properties:
        uploadSessionId:
          type: string
          description: Media upload session identifier.
        status:
          type: string
          enum:
            - UPLOADED
    UploadSessionReadyStatus:
      title: Upload Session Ready Status
      type: object
      additionalProperties: false
      required:
        - uploadSessionId
        - status
        - mediaId
      properties:
        uploadSessionId:
          type: string
          description: Media upload session identifier.
        status:
          type: string
          enum:
            - READY
        mediaId:
          type: string
          description: Media identifier created by the completed upload.
    UploadSessionFailedStatus:
      title: Upload Session Failed Status
      type: object
      additionalProperties: false
      required:
        - uploadSessionId
        - status
        - failureCode
        - failureMessage
      properties:
        uploadSessionId:
          type: string
          description: Media upload session identifier.
        status:
          type: string
          enum:
            - FAILED
        failureCode:
          type: string
          description: Machine-readable failure code for the upload session.
        failureMessage:
          type: string
          description: Human-readable summary of why the upload session failed.
    UploadSessionAbortedStatus:
      title: Upload Session Aborted Status
      type: object
      additionalProperties: false
      required:
        - uploadSessionId
        - status
      properties:
        uploadSessionId:
          type: string
          description: Media upload session identifier.
        status:
          type: string
          enum:
            - ABORTED
    SocialAccountInput:
      oneOf:
        - $ref: "#/components/schemas/FacebookInput"
        - $ref: "#/components/schemas/InstagramInput"
        - $ref: "#/components/schemas/LinkedInInput"
        - $ref: "#/components/schemas/LinkedInPagesInput"
        - $ref: "#/components/schemas/PinterestInput"
        - $ref: "#/components/schemas/ThreadsInput"
        - $ref: "#/components/schemas/TikTokInput"
        - $ref: "#/components/schemas/WoopTestInput"
        - $ref: "#/components/schemas/XInput"
        - $ref: "#/components/schemas/YouTubeInput"
      discriminator:
        propertyName: platform
        mapping:
          PINTEREST: "#/components/schemas/PinterestInput"
          INSTAGRAM: "#/components/schemas/InstagramInput"
          FACEBOOK: "#/components/schemas/FacebookInput"
          THREADS: "#/components/schemas/ThreadsInput"
          TIKTOK: "#/components/schemas/TikTokInput"
          YOUTUBE: "#/components/schemas/YouTubeInput"
          X: "#/components/schemas/XInput"
          LINKEDIN: "#/components/schemas/LinkedInInput"
          LINKEDIN_PAGES: "#/components/schemas/LinkedInPagesInput"
          WOOPTEST: "#/components/schemas/WoopTestInput"
    SocialAccountInputBase:
      type: object
      required:
        - socialAccountId
      properties:
        socialAccountId:
          type: string
          description: Connected social account identifier.
        contentOverride:
          $ref: "#/components/schemas/PostContentInput"
    SocialAccountPostBase:
      type: object
      additionalProperties: false
      required:
        - socialAccountPostId
        - postId
        - projectId
        - socialAccountId
        - deliveryStatus
        - content
        - schedule
        - createdAt
        - updatedAt
        - analytics
      properties:
        socialAccountPostId:
          type: string
          description: Social account post identifier.
        postId:
          type: string
          description: Parent post identifier.
        projectId:
          type: string
          description: Project identifier.
        socialAccountId:
          type: string
          description: Connected social account identifier.
        deliveryStatus:
          $ref: "#/components/schemas/DeliveryStatus"
        content:
          $ref: "#/components/schemas/PostContent"
          description: Content for this social account post.
        schedule:
          $ref: "#/components/schemas/PostSchedule"
          description: Schedule for this social account post.
        createdAt:
          type: string
          format: date-time
          description: UTC time when the social account post was created.
        updatedAt:
          type: string
          format: date-time
          description: UTC time when the social account post was last updated.
        deliveryCompletedAt:
          type: string
          format: date-time
          description: UTC time when the latest delivery attempt for this social account post completed both for successful (`PUBLISHED`) and unsuccessful (`FAILED`) terminal `deliveryStatus` values.
        externalPostId:
          type: string
        externalPostUrl:
          type: string
          format: uri
        errorMessage:
          type: string
        analytics:
          $ref: "#/components/schemas/PostAnalytics"
    PostAnalytics:
      type: object
      additionalProperties: false
      description: |
        Latest stored lifetime metrics for this social account post. Each supported
        metric contains its latest snapshot, or an empty array if no observation
        exists. Unsupported metrics are omitted; unsupported platforms and post
        types return an empty object. Zero is a recorded value, not missing data.

        Metrics are collected asynchronously and can have different snapshot dates.
        Reading posts does not refresh analytics. Arrays allow future snapshot
        ranges without changing the response shape; date-range queries are not
        currently supported.
      properties:
        views:
          $ref: "#/components/schemas/PostAnalyticsSnapshots"
        impressions:
          $ref: "#/components/schemas/PostAnalyticsSnapshots"
        uniqueImpressions:
          $ref: "#/components/schemas/PostAnalyticsSnapshots"
        likes:
          $ref: "#/components/schemas/PostAnalyticsSnapshots"
        reactions:
          $ref: "#/components/schemas/PostAnalyticsSnapshots"
        comments:
          $ref: "#/components/schemas/PostAnalyticsSnapshots"
        shares:
          $ref: "#/components/schemas/PostAnalyticsSnapshots"
        saves:
          $ref: "#/components/schemas/PostAnalyticsSnapshots"
        clicks:
          $ref: "#/components/schemas/PostAnalyticsSnapshots"
        engagementRate:
          $ref: "#/components/schemas/PostAnalyticsSnapshots"
          description: LinkedIn Pages provider-reported engagement ratio; 0.03 means 3 percent.
    PostAnalyticsSnapshots:
      type: array
      items:
        $ref: "#/components/schemas/PostAnalyticsSnapshot"
    PostAnalyticsSnapshot:
      type: object
      additionalProperties: false
      required:
        - date
        - collectedAt
        - value
      properties:
        date:
          type: string
          format: date
          description: UTC date of the snapshot, not the day the engagement occurred.
        collectedAt:
          type: string
          format: date-time
          description: UTC collection timestamp for this observation.
        value:
          type: number
          format: double
          description: Observed lifetime count, or a ratio for engagementRate.
    PinterestPlatform:
      type: object
      required:
        - platform
      properties:
        platform:
          allOf:
            - $ref: "#/components/schemas/SocialPlatform"
            - type: string
              enum:
                - PINTEREST
    InstagramPlatform:
      type: object
      required:
        - platform
      properties:
        platform:
          allOf:
            - $ref: "#/components/schemas/SocialPlatform"
            - type: string
              enum:
                - INSTAGRAM
    FacebookPlatform:
      type: object
      required:
        - platform
      properties:
        platform:
          allOf:
            - $ref: "#/components/schemas/SocialPlatform"
            - type: string
              enum:
                - FACEBOOK
    TikTokPlatform:
      type: object
      required:
        - platform
      properties:
        platform:
          allOf:
            - $ref: "#/components/schemas/SocialPlatform"
            - type: string
              enum:
                - TIKTOK
    ThreadsPlatform:
      type: object
      required:
        - platform
      properties:
        platform:
          allOf:
            - $ref: "#/components/schemas/SocialPlatform"
            - type: string
              enum:
                - THREADS
    YouTubePlatform:
      type: object
      required:
        - platform
      properties:
        platform:
          allOf:
            - $ref: "#/components/schemas/SocialPlatform"
            - type: string
              enum:
                - YOUTUBE
    XPlatform:
      type: object
      required:
        - platform
      properties:
        platform:
          allOf:
            - $ref: "#/components/schemas/SocialPlatform"
            - type: string
              enum:
                - X
    LinkedInPlatform:
      type: object
      required:
        - platform
      properties:
        platform:
          allOf:
            - $ref: "#/components/schemas/SocialPlatform"
            - type: string
              enum:
                - LINKEDIN
    LinkedInPagesPlatform:
      type: object
      required:
        - platform
      properties:
        platform:
          allOf:
            - $ref: "#/components/schemas/SocialPlatform"
            - type: string
              enum:
                - LINKEDIN_PAGES
    PinterestFields:
      type: object
      required:
        - pinterestBoardId
      properties:
        pinterestBoardId:
          type: string
          description: Identifier of the board to pin to.
        title:
          type: string
        link:
          type: string
          maxLength: 2048
          description: Destination URL to attach to the Pin.
    InstagramFields:
      type: object
      required:
        - postType
      properties:
        postType:
          $ref: "#/components/schemas/InstagramPostType"
        cover:
          $ref: "#/components/schemas/PostContentMediaInput"
          description: |
            Optional image media reference to use as the cover image for an Instagram Reel.

            This field applies only to `postType=REEL`.
        trialParams:
          $ref: "#/components/schemas/InstagramTrialParams"
          description: |
            Optional Trial Reel settings. When provided, `postType` must be `REEL`.

            Omit this field to publish a regular Reel.
    FacebookFields:
      type: object
      required:
        - postType
      properties:
        link:
          type: string
        locationId:
          type: string
          description: Facebook place ID to attach to the post.
        postType:
          $ref: "#/components/schemas/FacebookPostType"
    LinkedInFields:
      type: object
      properties:
        link:
          type: string
          description: URL to publish as a LinkedIn link preview card.
    TikTokFields:
      type: object
      required:
        - postType
        - allowComment
        - allowDuet
        - allowStitch
        - isYourBrand
        - isBrandedContent
        - autoAddMusic
      properties:
        postType:
          $ref: "#/components/schemas/TikTokPostType"
        cover:
          $ref: "#/components/schemas/PostContentMediaInput"
          description: |
            Optional image media reference to use as a custom cover for
            `postType=VIDEO`. WoopSocial stitches the image as a single
            frame at the beginning of the video before sending it to TikTok.
        postMode:
          $ref: "#/components/schemas/TikTokPostMode"
          default: DIRECT_POST
          description: |
            TikTok posting mode.

            Defaults to `DIRECT_POST` when omitted.

            `DIRECT_POST` publishes directly to TikTok.

            `MEDIA_UPLOAD` uploads the media to TikTok so the creator can
            finish and publish it in TikTok. Users will receive an inbox notification.

            Fields not used by the selected mode may be ignored by TikTok.
        privacyLevel:
          $ref: "#/components/schemas/TikTokPrivacyLevel"
          description: Required when `postMode=DIRECT_POST`. Not used for `MEDIA_UPLOAD`.
        allowComment:
          type: boolean
          description: Whether comments should be allowed for this TikTok post.
        allowDuet:
          type: boolean
          description: |
            Whether duets should be allowed for this TikTok post.

            This field applies to `postType=VIDEO`.

            When `postType=PHOTO`, this field is required by the API contract
            but is not used by TikTok.
        allowStitch:
          type: boolean
          description: |
            Whether stitches should be allowed for this TikTok post.

            This field applies to `postType=VIDEO`.

            When `postType=PHOTO`, this field is required by the API contract
            but is not used by TikTok.
        isYourBrand:
          type: boolean
          description: Whether the post should be disclosed as "Your brand" content on TikTok.
        isBrandedContent:
          type: boolean
          description: Whether the post should be disclosed as branded content on TikTok.
        isAiGeneratedContent:
          type: boolean
          default: false
          description: |
            Whether the TikTok post contains AI-generated content. This field applies to video posts.
        autoAddMusic:
          type: boolean
          description: |
            Whether TikTok should automatically add music to this post.

            This field applies to `postType=PHOTO`.

            When `postType=VIDEO`, this field is required by the API contract
            but is not used by TikTok.
    YouTubeFields:
      type: object
      required:
        - title
        - privacy
      properties:
        title:
          type: string
        privacy:
          $ref: "#/components/schemas/YouTubePrivacy"
        category:
          type: string
        tags:
          type: array
          items:
            type: string
        madeForKids:
          type: boolean
    PinterestInput:
      title: Pinterest
      allOf:
        - $ref: "#/components/schemas/PinterestPlatform"
        - $ref: "#/components/schemas/SocialAccountInputBase"
        - $ref: "#/components/schemas/PinterestFields"
    InstagramInput:
      title: Instagram
      allOf:
        - $ref: "#/components/schemas/InstagramPlatform"
        - $ref: "#/components/schemas/SocialAccountInputBase"
        - $ref: "#/components/schemas/InstagramFields"
    FacebookInput:
      title: Facebook
      allOf:
        - $ref: "#/components/schemas/FacebookPlatform"
        - $ref: "#/components/schemas/SocialAccountInputBase"
        - $ref: "#/components/schemas/FacebookFields"
    TikTokInput:
      title: TikTok
      allOf:
        - $ref: "#/components/schemas/TikTokPlatform"
        - $ref: "#/components/schemas/SocialAccountInputBase"
        - $ref: "#/components/schemas/TikTokFields"
    ThreadsInput:
      title: Threads
      allOf:
        - $ref: "#/components/schemas/ThreadsPlatform"
        - $ref: "#/components/schemas/SocialAccountInputBase"
    YouTubeInput:
      title: YouTube
      allOf:
        - $ref: "#/components/schemas/YouTubePlatform"
        - $ref: "#/components/schemas/SocialAccountInputBase"
        - $ref: "#/components/schemas/YouTubeFields"
    XInput:
      title: X
      allOf:
        - $ref: "#/components/schemas/XPlatform"
        - $ref: "#/components/schemas/SocialAccountInputBase"
    LinkedInInput:
      title: LinkedIn
      allOf:
        - $ref: "#/components/schemas/LinkedInPlatform"
        - $ref: "#/components/schemas/SocialAccountInputBase"
        - $ref: "#/components/schemas/LinkedInFields"
    LinkedInPagesInput:
      title: LinkedIn Pages
      allOf:
        - $ref: "#/components/schemas/LinkedInPagesPlatform"
        - $ref: "#/components/schemas/SocialAccountInputBase"
        - $ref: "#/components/schemas/LinkedInFields"
    SocialAccountPost:
      oneOf:
        - $ref: "#/components/schemas/PinterestPost"
        - $ref: "#/components/schemas/InstagramPost"
        - $ref: "#/components/schemas/FacebookPost"
        - $ref: "#/components/schemas/ThreadsPost"
        - $ref: "#/components/schemas/TikTokPost"
        - $ref: "#/components/schemas/YouTubePost"
        - $ref: "#/components/schemas/XPost"
        - $ref: "#/components/schemas/LinkedInPost"
        - $ref: "#/components/schemas/LinkedInPagesPost"
        - $ref: "#/components/schemas/WoopTestPost"
      discriminator:
        propertyName: platform
        mapping:
          PINTEREST: "#/components/schemas/PinterestPost"
          INSTAGRAM: "#/components/schemas/InstagramPost"
          FACEBOOK: "#/components/schemas/FacebookPost"
          THREADS: "#/components/schemas/ThreadsPost"
          TIKTOK: "#/components/schemas/TikTokPost"
          YOUTUBE: "#/components/schemas/YouTubePost"
          X: "#/components/schemas/XPost"
          LINKEDIN: "#/components/schemas/LinkedInPost"
          LINKEDIN_PAGES: "#/components/schemas/LinkedInPagesPost"
          WOOPTEST: "#/components/schemas/WoopTestPost"
    PinterestPost:
      title: Pinterest
      allOf:
        - $ref: "#/components/schemas/PinterestPlatform"
        - $ref: "#/components/schemas/SocialAccountPostBase"
        - $ref: "#/components/schemas/PinterestFields"
    InstagramPost:
      title: Instagram
      allOf:
        - $ref: "#/components/schemas/InstagramPlatform"
        - $ref: "#/components/schemas/SocialAccountPostBase"
        - $ref: "#/components/schemas/InstagramFields"
    FacebookPost:
      title: Facebook
      allOf:
        - $ref: "#/components/schemas/FacebookPlatform"
        - $ref: "#/components/schemas/SocialAccountPostBase"
        - $ref: "#/components/schemas/FacebookFields"
    TikTokPost:
      title: TikTok
      allOf:
        - $ref: "#/components/schemas/TikTokPlatform"
        - $ref: "#/components/schemas/SocialAccountPostBase"
        - $ref: "#/components/schemas/TikTokFields"
    ThreadsPost:
      title: Threads
      allOf:
        - $ref: "#/components/schemas/ThreadsPlatform"
        - $ref: "#/components/schemas/SocialAccountPostBase"
    YouTubePost:
      title: YouTube
      allOf:
        - $ref: "#/components/schemas/YouTubePlatform"
        - $ref: "#/components/schemas/SocialAccountPostBase"
        - $ref: "#/components/schemas/YouTubeFields"
    XPost:
      title: X
      allOf:
        - $ref: "#/components/schemas/XPlatform"
        - $ref: "#/components/schemas/SocialAccountPostBase"
    LinkedInPost:
      title: LinkedIn
      allOf:
        - $ref: "#/components/schemas/LinkedInPlatform"
        - $ref: "#/components/schemas/SocialAccountPostBase"
        - $ref: "#/components/schemas/LinkedInFields"
    LinkedInPagesPost:
      title: LinkedIn Pages
      allOf:
        - $ref: "#/components/schemas/LinkedInPagesPlatform"
        - $ref: "#/components/schemas/SocialAccountPostBase"
        - $ref: "#/components/schemas/LinkedInFields"
    WoopTestPlatform:
      type: object
      required:
        - platform
      properties:
        platform:
          allOf:
            - $ref: "#/components/schemas/SocialPlatform"
            - type: string
              enum:
                - WOOPTEST
    WoopTestFields:
      type: object
      properties:
        shouldSucceed:
          type: boolean
          default: true
          description: |
            Whether the simulated delivery should succeed or fail.

            Defaults to `true`. Set to `false` to simulate a delivery failure.
    WoopTestInput:
      title: WoopTest
      description: WoopTest is a sandbox platform for testing and debugging. Posts targeting `WOOPTEST` go through the full scheduling pipeline but are never published to any real social network.
      allOf:
        - $ref: "#/components/schemas/WoopTestPlatform"
        - $ref: "#/components/schemas/SocialAccountInputBase"
        - $ref: "#/components/schemas/WoopTestFields"
    WoopTestPost:
      title: WoopTest
      allOf:
        - $ref: "#/components/schemas/WoopTestPlatform"
        - $ref: "#/components/schemas/SocialAccountPostBase"
        - $ref: "#/components/schemas/WoopTestFields"
    ValidationError:
      type: object
      additionalProperties: false
      required:
        - path
        - message
      properties:
        path:
          type: string
          description: JSON path to the invalid input.
        field:
          $ref: "#/components/schemas/ValidationField"
          description: |
            Machine-readable validation field identifier.
        message:
          type: string
    ValidationWarning:
      type: object
      additionalProperties: false
      required:
        - path
        - message
      properties:
        path:
          type: string
          description: JSON path related to the warning.
        field:
          $ref: "#/components/schemas/ValidationField"
          description: |
            Machine-readable validation field identifier.
        message:
          type: string
    ValidationField:
      type: string
      enum:
        - DESCRIPTION
        - MEDIA
        - LINK
        - PINTEREST_BOARD
        - CONTENT
        - TITLE
        - FIRST_COMMENT
        - INSTAGRAM_TRIAL_PARAMS
        - TIKTOK_POST_MODE
        - TIKTOK_PRIVACY_LEVEL
        - TIKTOK_CONTENT_DISCLOSURE
        - YOUTUBE_PRIVACY
        - YOUTUBE_CATEGORY
        - YOUTUBE_TAGS
        - SCHEDULE
    CreatePostErrorCode:
      type: string
      enum:
        - INVALID_SOCIAL_ACCOUNT_ID
        - SOCIAL_ACCOUNT_NOT_CONNECTED
        - SOCIAL_ACCOUNTS_MUST_BELONG_TO_SAME_PROJECT
        - DUPLICATE_SOCIAL_ACCOUNT_ID
        - INVALID_MEDIA_ID
        - INVALID_PLATFORM_SPECIFIC_DATA
        - VALIDATION_FAILED
        - INTERNAL_ERROR
    CreatePostErrorResponse:
      type: object
      additionalProperties: false
      required:
        - code
        - message
      properties:
        code:
          $ref: "#/components/schemas/CreatePostErrorCode"
        message:
          type: string
        validationErrors:
          type: array
          items:
            $ref: "#/components/schemas/ValidationError"
          description: Field-level validation failures. Omitted when not applicable.
        conflictingSocialAccountIds:
          type: array
          minItems: 2
          maxItems: 2
          description: |
            Two social account IDs that belong to different projects.

            Returned when `code` is
            `SOCIAL_ACCOUNTS_MUST_BELONG_TO_SAME_PROJECT`.
          items:
            type: string
    ValidatePostResponse:
      type: object
      additionalProperties: false
      required:
        - isValid
        - errors
        - warnings
      properties:
        isValid:
          type: boolean
          description: Whether the request passed validation.
        errors:
          type: array
          description: Blocking validation errors. Empty when the request is valid.
          items:
            $ref: "#/components/schemas/ValidationError"
        warnings:
          type: array
          description: Non-blocking validation warnings. Empty when there are no warnings.
          items:
            $ref: "#/components/schemas/ValidationWarning"
    GetPostErrorCode:
      type: string
      enum:
        - POST_NOT_FOUND
        - INTERNAL_ERROR
    GetPostErrorResponse:
      type: object
      additionalProperties: false
      required:
        - code
        - message
      properties:
        code:
          $ref: "#/components/schemas/GetPostErrorCode"
        message:
          type: string
    DeletePostErrorCode:
      type: string
      enum:
        - POST_NOT_FOUND
        - POST_NOT_DELETABLE
        - INTERNAL_ERROR
    DeletePostErrorResponse:
      type: object
      additionalProperties: false
      required:
        - code
        - message
      properties:
        code:
          $ref: "#/components/schemas/DeletePostErrorCode"
        message:
          type: string
    DeleteSocialAccountPostErrorCode:
      type: string
      enum:
        - SOCIAL_ACCOUNT_POST_NOT_FOUND
        - INTERNAL_ERROR
    DeleteSocialAccountPostErrorResponse:
      type: object
      additionalProperties: false
      required:
        - code
        - message
      properties:
        code:
          $ref: "#/components/schemas/DeleteSocialAccountPostErrorCode"
        message:
          type: string
    DeleteSocialAccountErrorCode:
      type: string
      enum:
        - SOCIAL_ACCOUNT_NOT_FOUND
        - INTERNAL_ERROR
    DeleteSocialAccountErrorResponse:
      type: object
      additionalProperties: false
      required:
        - code
        - message
      properties:
        code:
          $ref: "#/components/schemas/DeleteSocialAccountErrorCode"
        message:
          type: string
    DeleteMediaErrorCode:
      type: string
      enum:
        - MEDIA_NOT_FOUND
        - INTERNAL_ERROR
    DeleteMediaErrorResponse:
      type: object
      additionalProperties: false
      required:
        - code
        - message
      properties:
        code:
          $ref: "#/components/schemas/DeleteMediaErrorCode"
        message:
          type: string
    InstagramPostType:
      type: string
      enum:
        - POST
        - REEL
        - STORY
    InstagramTrialParams:
      type: object
      additionalProperties: false
      required:
        - graduationStrategy
      properties:
        graduationStrategy:
          $ref: "#/components/schemas/InstagramTrialGraduationStrategy"
    InstagramTrialGraduationStrategy:
      type: string
      description: |
        Controls how a Trial Reel graduates to followers. `MANUAL` leaves graduation
        to the user in Instagram. `SS_PERFORMANCE` lets Instagram graduate the Reel
        automatically based on its performance.
      enum:
        - MANUAL
        - SS_PERFORMANCE
    FacebookPostType:
      type: string
      enum:
        - TEXT_ONLY
        - IMAGE
        - VIDEO
        - REEL
        - STORY
    TikTokPrivacyLevel:
      type: string
      enum:
        - PUBLIC_TO_EVERYONE
        - SELF_ONLY
        - MUTUAL_FOLLOW_FRIENDS
        - FOLLOWER_OF_CREATOR
    TikTokPostType:
      type: string
      enum:
        - VIDEO
        - PHOTO
    TikTokPostMode:
      type: string
      enum:
        - DIRECT_POST
        - MEDIA_UPLOAD
    YouTubePrivacy:
      type: string
      enum:
        - public
        - private
        - unlisted
    HealthResponse:
      type: object
      additionalProperties: false
      required:
        - message
        - timestamp
        - version
      properties:
        message:
          type: string
          description: Human-readable message.
          examples:
            - Hello from WoopSocial API
        timestamp:
          type: string
          format: date-time
          description: Current server time in UTC (ISO 8601) with millisecond precision.
          examples:
            - "2026-03-28T09:30:26.123Z"
        version:
          type: string
          description: API version that produced the response.
          examples:
            - v1
