{
  "openapi": "3.1.0",
  "info": {
    "title": "PostZen Public API",
    "version": "1.0.0",
    "description": "The PostZen Public API lets server-side integrations manage profiles, connected social accounts, OAuth connection flows, media uploads, post creation, and outbound webhooks."
  },
  "servers": [
    {
      "url": "https://api.postzen.dev",
      "description": "Production API"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Profiles",
      "description": "List, create, update, and delete PostZen profiles."
    },
    {
      "name": "Accounts",
      "description": "List and disconnect connected social accounts."
    },
    {
      "name": "Connect",
      "description": "Start and complete OAuth account connection flows."
    },
    {
      "name": "Media",
      "description": "Create presigned upload URLs for PostZen-hosted media, or upload a small file directly."
    },
    {
      "name": "Posts",
      "description": "Create drafts, scheduled posts, or immediate posts, and read LinkedIn comments and reactions on published organization posts."
    },
    {
      "name": "Inbox",
      "description": "Read and moderate comments on Instagram, Facebook, and Threads posts. Every endpoint takes the connected account as `accountId` and identifies the post either by its PostZen post id or by the platform's own post/media id. Reads also refresh PostZen's stored copy of the thread. An account on any other platform returns `400 platformUnsupported`, and the write endpoints need the account's comment-management scope — reconnect the account when they answer `403 platformCapabilityMissing`. The conversation endpoints cover Instagram direct messages instead: they read PostZen’s synced copy of each thread, refresh it from the platform as a side effect, and keep read state and archive state locally because Meta exposes neither. Sending is bounded by Meta’s 24-hour reply window, which surfaces as `400 PLATFORM_LIMITATION`."
    },
    {
      "name": "Queues",
      "description": "Manage per-profile posting queues. A queue is a named weekly schedule (`slots` of `{ dayOfWeek, time }`) evaluated in the queue's own timezone. A profile may hold several queues and exactly one is the default. Creating a post with `queuedFromProfile` claims the next free slot inside a single transaction, which is what keeps concurrent creates from landing on the same instant."
    },
    {
      "name": "Analytics",
      "description": "Read post, engagement, follower, and posting-time analytics, and synchronize external posts."
    },
    {
      "name": "API Keys",
      "x-displayName": "API Keys",
      "description": "Manage PostZen API keys programmatically. Creating and deleting keys requires a read-write key with full profile access."
    },
    {
      "name": "Webhooks",
      "description": "Manage outbound webhook endpoints and retained delivery logs. PostZen sends an at-least-once JSON envelope `{ id, type, apiVersion, createdAt, data }` using `POST`; automatic retries and manual redeliveries keep the same event id and raw body. Requests include `X-PostZen-Event`, `X-PostZen-Event-Id`, `X-PostZen-Delivery-Id`, and `X-PostZen-Timestamp`. When a signing secret is configured, `X-PostZen-Signature` is `v1=<hex digest>` where the digest is HMAC-SHA256 over `<timestamp>.<raw request body>`. Production receivers should configure signing, compare signatures in constant time, reject timestamps older than five minutes, and deduplicate by event id."
    }
  ],
  "paths": {
    "/v1/profiles": {
      "get": {
        "tags": [
          "Profiles"
        ],
        "operationId": "listProfiles",
        "summary": "List profiles",
        "description": "Returns profiles available to the API key, sorted by creation date (oldest first). Read-only and read-write API keys are accepted.",
        "responses": {
          "200": {
            "description": "Profiles returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProfilesListResponse"
                },
                "example": {
                  "profiles": [
                    {
                      "_id": "jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e",
                      "userId": "jd7cm2xw9krt4v1n8q6zs0b3hf5yg2pa",
                      "name": "Marketing Team",
                      "description": "Profile for marketing campaigns",
                      "color": "#4caf50",
                      "isDefault": false,
                      "createdAt": "2026-06-13T16:00:00.000Z"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": "Failed to list profiles"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Profiles"
        ],
        "operationId": "createProfile",
        "summary": "Create a profile",
        "description": "Creates a profile. This endpoint requires a read-write API key with access to all profiles.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProfileCreateRequest"
              },
              "example": {
                "name": "Marketing Team",
                "description": "Profile for marketing campaigns",
                "color": "#4caf50"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Profile created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProfileWriteResponse"
                },
                "example": {
                  "message": "Profile created successfully",
                  "profile": {
                    "_id": "jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e",
                    "userId": "jd7cm2xw9krt4v1n8q6zs0b3hf5yg2pa",
                    "name": "Marketing Team",
                    "description": "Profile for marketing campaigns",
                    "color": "#4caf50",
                    "isDefault": false,
                    "createdAt": "2026-06-13T16:00:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/profiles/{profileId}": {
      "parameters": [
        {
          "name": "profileId",
          "in": "path",
          "required": true,
          "description": "PostZen profile id.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Profiles"
        ],
        "operationId": "getProfile",
        "summary": "Get a profile",
        "description": "Returns a single profile visible to the API key.",
        "responses": {
          "200": {
            "description": "Profile returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProfileResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "put": {
        "tags": [
          "Profiles"
        ],
        "operationId": "updateProfile",
        "summary": "Update a profile",
        "description": "Updates one or more profile fields. This endpoint requires a read-write API key.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProfileUpdateRequest"
              },
              "example": {
                "name": "Marketing Team (Updated)",
                "description": "Updated profile description",
                "color": "#2196f3",
                "isDefault": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Profile updated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProfileWriteResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "delete": {
        "tags": [
          "Profiles"
        ],
        "operationId": "deleteProfile",
        "summary": "Delete a profile",
        "description": "Deletes a profile. The default profile cannot be deleted, and profiles with connected accounts must be disconnected first.",
        "responses": {
          "200": {
            "description": "Profile deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                },
                "example": {
                  "message": "Profile deleted successfully"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/accounts": {
      "get": {
        "tags": [
          "Accounts"
        ],
        "operationId": "listAccounts",
        "summary": "List accounts",
        "description": "Returns connected social accounts available to the API key, sorted by connection date (newest first). Read-only and read-write API keys are accepted.",
        "parameters": [
          {
            "name": "profileId",
            "in": "query",
            "description": "Filter accounts by profile id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "platform",
            "in": "query",
            "description": "Filter accounts by platform. `twitter` is accepted as an alias for `x`.",
            "schema": {
              "$ref": "#/components/schemas/PublicPlatformInput"
            }
          },
          {
            "name": "status",
            "in": "query",
            "description": "`connected` returns healthy accounts. `disconnected` returns accounts that need reconnection or are disabled.",
            "schema": {
              "type": "string",
              "enum": [
                "connected",
                "disconnected"
              ]
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "1-based page number. Must be provided with `limit`.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Page size. Must be provided with `page`.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Accounts returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountsListResponse"
                },
                "example": {
                  "accounts": [
                    {
                      "_id": "j97kp4mw2xnv6r1t8c3zq5bs9fh0yg4d",
                      "platform": "twitter",
                      "providerAccountId": "1934567890123456789",
                      "profileId": {
                        "_id": "jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e",
                        "name": "Marketing Team",
                        "slug": "marketing-team",
                        "color": "#4caf50"
                      },
                      "username": "acme",
                      "displayName": "Acme",
                      "profileUrl": "https://x.com/acme",
                      "status": "connected",
                      "isActive": true,
                      "connectedAt": "2026-06-13T16:00:00.000Z"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/accounts/{accountId}": {
      "delete": {
        "tags": [
          "Accounts"
        ],
        "operationId": "disconnectAccount",
        "summary": "Disconnect an account",
        "description": "Disconnects and removes a connected social account. Pending scheduled, queued, and publishing targets for that account are canceled. This endpoint requires a read-write API key.",
        "parameters": [
          {
            "name": "accountId",
            "in": "path",
            "required": true,
            "description": "PostZen account id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Account disconnected.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                },
                "example": {
                  "message": "Account disconnected successfully"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/accounts/{accountId}/pinterest-boards": {
      "parameters": [
        {
          "name": "accountId",
          "in": "path",
          "required": true,
          "description": "PostZen id of a connected Pinterest account.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Accounts"
        ],
        "operationId": "getPinterestBoards",
        "summary": "List Pinterest boards",
        "description": "Lists boards owned by a connected Pinterest account. Read-only and read-write API keys are accepted.",
        "responses": {
          "200": {
            "description": "Pinterest boards returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PinterestBoardsResponse"
                },
                "example": {
                  "boards": [
                    {
                      "id": "123456789012345678",
                      "name": "Product inspiration",
                      "description": "Ideas for upcoming launches",
                      "privacy": "PUBLIC",
                      "pinCount": 42
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "424": {
            "description": "The Pinterest account is no longer connected.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": "not_connected"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "Pinterest could not answer the request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": "request_failed"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Accounts"
        ],
        "operationId": "createPinterestBoard",
        "summary": "Create Pinterest board",
        "description": "Creates a board for the connected Pinterest account. This endpoint requires a read-write API key.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PinterestCreateBoardRequest"
              },
              "example": {
                "name": "Product inspiration",
                "description": "Ideas for upcoming launches",
                "privacy": "PUBLIC"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Pinterest board created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PinterestBoardResponse"
                },
                "example": {
                  "board": {
                    "id": "123456789012345678",
                    "name": "Product inspiration",
                    "description": "Ideas for upcoming launches",
                    "privacy": "PUBLIC"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "424": {
            "description": "The Pinterest account is no longer connected.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": "not_connected"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "Pinterest could not create the board.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": "request_failed"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Accounts"
        ],
        "operationId": "updatePinterestBoards",
        "summary": "Set default Pinterest board",
        "description": "Verifies a Pinterest board and stores it as the account default used when a pin target omits `settings.boardId`. This endpoint requires a read-write API key.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PinterestDefaultBoardRequest"
              },
              "example": {
                "defaultBoardId": "123456789012345678",
                "defaultBoardName": "Product inspiration"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Default Pinterest board updated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PinterestDefaultBoardResponse"
                },
                "example": {
                  "message": "Default Pinterest board updated successfully",
                  "account": {
                    "_id": "j97kp4mw2xnv6r1t8c3zq5bs9fh0yg4d",
                    "platform": "pinterest",
                    "providerAccountId": "987654321098765432",
                    "profileId": {
                      "_id": "jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e",
                      "name": "Marketing Team",
                      "slug": "marketing-team",
                      "color": "#4caf50"
                    },
                    "username": "acme",
                    "displayName": "Acme",
                    "status": "connected",
                    "isActive": true,
                    "connectedAt": "2026-06-19T16:00:00.000Z",
                    "defaultBoardId": "123456789012345678",
                    "defaultBoardName": "Product inspiration"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request or the selected board does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": "board_not_found"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "424": {
            "description": "The Pinterest account is no longer connected.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": "not_connected"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "Pinterest could not verify the board.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": "request_failed"
                }
              }
            }
          }
        }
      }
    },
    "/v1/connect/pinterest/select-board": {
      "get": {
        "tags": [
          "Connect"
        ],
        "operationId": "listPinterestBoardsForSelection",
        "summary": "List Pinterest boards for the connect flow",
        "description": "Lists boards after Pinterest OAuth completes so the client can show a board picker. The state handle remains valid for board selection for 30 minutes from connect-flow creation. This endpoint requires a read-write API key.",
        "parameters": [
          {
            "name": "state",
            "in": "query",
            "required": true,
            "description": "State token returned by `GET /v1/connect/pinterest`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Pinterest boards returned for selection.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PinterestBoardsResponse"
                },
                "example": {
                  "boards": [
                    {
                      "id": "123456789012345678",
                      "name": "Product inspiration",
                      "privacy": "PUBLIC"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "The connect session was not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": "connect_session_not_found"
                }
              }
            }
          },
          "409": {
            "description": "Pinterest OAuth has not completed for this connect session.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": "connect_not_completed"
                }
              }
            }
          },
          "410": {
            "description": "The connect session is too old for board selection.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": "connect_session_expired"
                }
              }
            }
          },
          "424": {
            "description": "The connected Pinterest account is no longer available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": "not_connected"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "Pinterest could not answer the request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": "request_failed"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Connect"
        ],
        "operationId": "selectPinterestBoard",
        "summary": "Select Pinterest board",
        "description": "Verifies and stores the default board for the Pinterest account created by the connect session. This endpoint requires a read-write API key.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PinterestSelectBoardRequest"
              },
              "example": {
                "state": "3q2Xv8Zk1mR5tY7wA9bC4dE6",
                "boardId": "123456789012345678",
                "boardName": "Product inspiration",
                "redirectUrl": "https://example.com/connect/complete"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Pinterest board selected and stored as the account default.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PinterestSelectBoardResponse"
                },
                "example": {
                  "message": "Pinterest connected successfully with default board",
                  "account": {
                    "_id": "j97kp4mw2xnv6r1t8c3zq5bs9fh0yg4d",
                    "platform": "pinterest",
                    "providerAccountId": "987654321098765432",
                    "profileId": {
                      "_id": "jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e",
                      "name": "Marketing Team",
                      "slug": "marketing-team",
                      "color": "#4caf50"
                    },
                    "username": "acme",
                    "displayName": "Acme",
                    "status": "connected",
                    "isActive": true,
                    "connectedAt": "2026-06-19T16:00:00.000Z",
                    "defaultBoardId": "123456789012345678",
                    "defaultBoardName": "Product inspiration"
                  },
                  "redirectUrl": "https://example.com/connect/complete?connected=pinterest&profileId=jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e&board=Product+inspiration"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request or the selected board does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": "board_not_found"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "The connect session was not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": "connect_session_not_found"
                }
              }
            }
          },
          "409": {
            "description": "Pinterest OAuth has not completed for this connect session.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": "connect_not_completed"
                }
              }
            }
          },
          "410": {
            "description": "The connect session is too old for board selection.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": "connect_session_expired"
                }
              }
            }
          },
          "424": {
            "description": "The connected Pinterest account is no longer available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": "not_connected"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "Pinterest could not verify the board.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": "request_failed"
                }
              }
            }
          }
        }
      }
    },
    "/v1/connect/{platform}": {
      "parameters": [
        {
          "name": "platform",
          "in": "path",
          "required": true,
          "description": "Social platform to connect. `twitter` is accepted as an alias for `x`.",
          "schema": {
            "$ref": "#/components/schemas/PublicPlatformInput"
          }
        }
      ],
      "get": {
        "tags": [
          "Connect"
        ],
        "operationId": "createConnectUrl",
        "summary": "Create an OAuth connect URL",
        "description": "Initiates an OAuth connection flow and returns an authorization URL to redirect the user to. The OAuth state expires after 10 minutes. This endpoint requires a read-write API key.\n\nFor `bluesky`, the returned `authUrl` is a PostZen-hosted page where the user enters their Bluesky handle and app password (no developer app or OAuth redirect is required); the connection completes when they submit that form.\n\nFor `telegram`, the returned `authUrl` is a PostZen-hosted page that issues a short-lived access code (there is no OAuth grant and no Telegram authorization screen); the connection completes when the user adds @PostZenScheduleBot as an administrator of a channel or group and sends that code to the bot.",
        "parameters": [
          {
            "name": "profileId",
            "in": "query",
            "required": true,
            "description": "PostZen profile id to attach the connected account to.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "redirectUrl",
            "in": "query",
            "description": "Custom URL PostZen redirects to after the connection completes.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OAuth URL created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConnectStartResponse"
                },
                "example": {
                  "authUrl": "https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id=86xk2ab9cdef01&state=3q2Xv8Zk1mR5tY7wA9bC4dE6&scope=openid%20profile%20w_member_social%20email",
                  "state": "3q2Xv8Zk1mR5tY7wA9bC4dE6"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "Connect"
        ],
        "operationId": "completeConnect",
        "summary": "Complete an OAuth connection",
        "description": "Exchanges an OAuth authorization code for tokens and connects the account to the specified profile. This endpoint requires a read-write API key.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConnectCompleteRequest"
              },
              "example": {
                "code": "AQTF0v6PZnq1yTkGxg3M4S8d",
                "state": "3q2Xv8Zk1mR5tY7wA9bC4dE6",
                "profileId": "jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Account connected.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConnectCompleteResponse"
                },
                "example": {
                  "message": "Account connected successfully",
                  "platform": "twitter",
                  "profileId": "jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e",
                  "status": "connected",
                  "accounts": [
                    {
                      "_id": "j97kp4mw2xnv6r1t8c3zq5bs9fh0yg4d",
                      "platform": "twitter",
                      "providerAccountId": "1934567890123456789",
                      "profileId": {
                        "_id": "jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e",
                        "name": "Marketing Team",
                        "slug": "marketing-team",
                        "color": "#4caf50"
                      },
                      "username": "acme",
                      "displayName": "Acme",
                      "status": "connected",
                      "isActive": true,
                      "connectedAt": "2026-06-19T16:00:00.000Z"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request, expired OAuth state, or a failed platform callback. Callback failures return a camelCase error code with an optional `errorDescription`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConnectCompleteErrorResponse"
                },
                "example": {
                  "error": "oauthCallbackFailed",
                  "errorDescription": "OAuth callback did not complete a matching platform connection"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "description": "The API key does not have access to the profile, or the OAuth state belongs to a different user or profile.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConnectCompleteErrorResponse"
                },
                "example": {
                  "error": "oauthStateUserMismatch"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "description": "The platform token exchange failed or an unexpected server error occurred.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConnectCompleteErrorResponse"
                },
                "example": {
                  "error": "linkedinOauthFailed"
                }
              }
            }
          }
        }
      }
    },
    "/v1/media/presign": {
      "post": {
        "tags": [
          "Media"
        ],
        "operationId": "createMediaPresign",
        "summary": "Create a presigned media upload URL",
        "description": "Creates a presigned URL for uploading an image, video, GIF, or PDF to PostZen-hosted storage. Upload the file with an HTTP `PUT` to `uploadUrl`, then reference `publicUrl` in post `mediaItems`. This endpoint requires a read-write API key.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MediaPresignRequest"
              },
              "example": {
                "filename": "launch-video.mp4",
                "contentType": "video/mp4",
                "size": 10485760
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Presigned upload URL created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MediaPresignResponse"
                },
                "example": {
                  "uploadUrl": "https://postzen-media.a1b2c3d4e5f6a7b8.r2.cloudflarestorage.com/users/jd7cm2xw9krt4v1n8q6zs0b3hf5yg2pa/media/6f0d0296-6a1c-4d5b-9a2e-3f8c1b7d4e90/launch-video.mp4?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=900&X-Amz-Signature=f3ac1e2b",
                  "publicUrl": "https://media.postzen.dev/users/jd7cm2xw9krt4v1n8q6zs0b3hf5yg2pa/media/6f0d0296-6a1c-4d5b-9a2e-3f8c1b7d4e90/launch-video.mp4",
                  "key": "users/jd7cm2xw9krt4v1n8q6zs0b3hf5yg2pa/media/6f0d0296-6a1c-4d5b-9a2e-3f8c1b7d4e90/launch-video.mp4",
                  "type": "video"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/media/upload-direct": {
      "post": {
        "tags": [
          "Media"
        ],
        "operationId": "uploadMediaDirect",
        "summary": "Upload a file directly",
        "description": "Uploads a file in the request itself and returns a public URL, for cases where a two-step presign is inconvenient and the file is small — chiefly direct message attachments, where Meta fetches the URL itself. The stored object is deleted automatically after seven days, so it is not a substitute for `POST /v1/media/presign` when publishing posts. Maximum 25MB. Requires a read-write API key.",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "The file bytes. Image, video, audio, or PDF, up to 25MB."
                  },
                  "contentType": {
                    "type": "string",
                    "description": "MIME type override, for clients that cannot set the part's own Content-Type."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "File stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MediaDirectUploadResponse"
                },
                "example": {
                  "url": "https://media.postzen.dev/direct-uploads/0001787068800000-6f0d0296-6a1c-4d5b-9a2e-3f8c1b7d4e90/receipt.jpg",
                  "filename": "receipt.jpg",
                  "contentType": "image/jpeg",
                  "size": 184320
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/posts/bulk-upload": {
      "post": {
        "tags": [
          "Posts"
        ],
        "operationId": "bulkUploadPosts",
        "summary": "Bulk upload posts from CSV",
        "description": "Validates and creates up to 500 posts from a CSV file. This endpoint requires a read-write API key. Each data row targets every connected account for the selected platforms on one profile. Set `dryRun=true` to validate without checking the Free plan's monthly post allowance, ingesting media, or writing data. A non-dry-run upload is rejected with HTTP 402 before creating any rows if its publishable targets would exceed that allowance. A mixed success/failure response uses HTTP 207; all-success and all-failure responses use HTTP 200.\n\n| CSV column | Purpose |\n| --- | --- |\n| `post_content` | Required base post text. |\n| `platforms` | Required comma-separated platforms: `instagram`, `facebook`, `threads`, `tiktok`, `linkedin`, `x`, `youtube`, `pinterest`, `bluesky`, or `telegram`; `twitter` aliases `x`. |\n| `profiles` | Required single profile id or unique profile name. |\n| `schedule_time` | Required column. Use `YYYY-MM-DD HH:mm[:ss]` in `tz`, or ISO 8601 with an explicit offset. May be empty for draft, publish-now, or queue rows. |\n| `tz` | IANA timezone; defaults to `UTC`. |\n| `media_urls` | Comma-separated HTTP(S) URLs, up to 10 per row and 50 distinct URLs per upload. |\n| `is_draft`, `publish_now`, `use_queue` | Mutually exclusive boolean mode flags. With none set, the row is scheduled. |\n| `title`, `tags`, `hashtags`, `mentions`, `visibility` | General title, comma-separated tags, appended hashtags/mentions, and YouTube visibility. |\n| `custom_content_<platform>` | Platform-specific content override; `custom_content_twitter` targets X. |\n| `youtube_title`, `youtube_description` | YouTube title and description override. |\n| `facebook_first_comment`, `linkedin_first_comment` | First comments. |\n| `instagram_content_type`, `instagram_collaborators`, `instagram_first_comment` | Instagram post type, collaborators, and first comment. |\n| `tiktok_privacy`, `tiktok_allow_comments`, `tiktok_allow_duet`, `tiktok_allow_stitch`, `tiktok_brand_partner`, `tiktok_organic_brand`, `tiktok_draft`, `tiktok_description` | TikTok publishing options. |\n| `telegram_parse_mode`, `telegram_disable_web_page_preview`, `telegram_disable_notification`, `telegram_protect_content` | Telegram publishing options. |\n| `pinterest_title`, `pinterest_link`, `pinterest_board_id`, `pinterest_cover_image_url`, `pinterest_cover_image_key_frame_time` | Pinterest pin options, including alternative video cover selections. |",
        "parameters": [
          {
            "name": "dryRun",
            "in": "query",
            "description": "Validate every row without checking the monthly post allowance, ingesting media, or creating posts.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "x-request-id",
            "in": "header",
            "description": "Optional upload idempotency key. Each row derives the stable key `bulk-api:{x-request-id}:{1-based row index}`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "UTF-8 CSV file, maximum 2 MiB."
                  }
                }
              }
            },
            "text/csv": {
              "schema": {
                "type": "string",
                "description": "Raw UTF-8 CSV body, maximum 2 MiB."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Every row succeeded, or every row failed validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BulkUploadResult"
                }
              }
            }
          },
          "207": {
            "description": "The upload contains both successful and failed rows.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BulkUploadResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/posts": {
      "get": {
        "tags": [
          "Posts"
        ],
        "operationId": "listPosts",
        "summary": "List posts",
        "description": "Returns posts created for the profiles available to the API key, newest first. Read-only and read-write API keys are accepted. Published posts include a `platformPostUrl` for each published platform target. Results are capped to the 1000 most recent matching posts.",
        "parameters": [
          {
            "name": "profileId",
            "in": "query",
            "description": "Filter posts by profile id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "accountId",
            "in": "query",
            "description": "Filter posts to those targeting a specific connected account.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "platform",
            "in": "query",
            "description": "Filter posts to those with a target on this platform. `twitter` is accepted as an alias for `x`.",
            "schema": {
              "$ref": "#/components/schemas/PublicPlatformInput"
            }
          },
          {
            "name": "status",
            "in": "query",
            "description": "Filter posts by status.",
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "scheduled",
                "queued",
                "publishing",
                "published",
                "partially_failed",
                "failed",
                "canceled"
              ]
            }
          },
          {
            "name": "dateFrom",
            "in": "query",
            "description": "Only include posts scheduled on or after this ISO 8601 timestamp. Posts without a scheduled time are excluded when a date filter is supplied.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "dateTo",
            "in": "query",
            "description": "Only include posts scheduled on or before this ISO 8601 timestamp. Posts without a scheduled time are excluded when a date filter is supplied.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "sortBy",
            "in": "query",
            "description": "Sort order. `createdAt` sorts by creation time (newest first). `scheduledFor` sorts by scheduled time (newest first) and excludes posts without a scheduled time.",
            "schema": {
              "type": "string",
              "enum": [
                "createdAt",
                "scheduledFor"
              ],
              "default": "createdAt"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "1-based page number. Defaults to 1.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Page size. Defaults to 20.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Posts returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PostsListResponse"
                },
                "example": {
                  "posts": [
                    {
                      "_id": "jx58t2kqm4wr9v3n7c1zp6bs0dh5fg8y",
                      "title": "Launch post",
                      "content": "We shipped the new release.",
                      "status": "published",
                      "scheduledFor": null,
                      "timezone": "UTC",
                      "platforms": [
                        {
                          "platform": "twitter",
                          "accountId": {
                            "_id": "j97kp4mw2xnv6r1t8c3zq5bs9fh0yg4d",
                            "platform": "twitter",
                            "username": "acme",
                            "displayName": "Acme",
                            "isActive": true
                          },
                          "status": "published",
                          "platformPostUrl": "https://x.com/acme/status/1934567890123456789"
                        }
                      ]
                    }
                  ],
                  "pagination": {
                    "page": 1,
                    "limit": 20,
                    "total": 1,
                    "totalPages": 1
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      },
      "post": {
        "tags": [
          "Posts"
        ],
        "operationId": "createPost",
        "summary": "Create a post",
        "description": "Creates a draft, scheduled post, or immediate post. This endpoint requires a read-write API key. Provide exactly one creation mode: `publishNow`, `scheduledFor`, or `isDraft`.",
        "parameters": [
          {
            "name": "x-request-id",
            "in": "header",
            "description": "Optional idempotency key. Repeating the same value returns the original post instead of creating a new one.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePostRequest"
              },
              "example": {
                "title": "Launch post",
                "content": "We shipped the new release.",
                "publishNow": true,
                "platforms": [
                  {
                    "platform": "twitter",
                    "accountId": "1934567890123456789"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Idempotency replay.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatePostReplayResponse"
                },
                "example": {
                  "existingPost": {
                    "_id": "jx58t2kqm4wr9v3n7c1zp6bs0dh5fg8y",
                    "title": "Launch post",
                    "content": "We shipped the new release.",
                    "status": "published",
                    "scheduledFor": null,
                    "timezone": "UTC",
                    "platforms": []
                  },
                  "message": "Post already exists for this request id"
                }
              }
            }
          },
          "201": {
            "description": "Post created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatePostResponse"
                },
                "example": {
                  "post": {
                    "_id": "jx58t2kqm4wr9v3n7c1zp6bs0dh5fg8y",
                    "title": "Launch post",
                    "content": "We shipped the new release.",
                    "status": "published",
                    "scheduledFor": null,
                    "timezone": "UTC",
                    "platforms": [
                      {
                        "platform": "twitter",
                        "accountId": {
                          "_id": "j97kp4mw2xnv6r1t8c3zq5bs9fh0yg4d",
                          "platform": "twitter",
                          "username": "acme",
                          "displayName": "Acme",
                          "isActive": true
                        },
                        "status": "published",
                        "platformPostUrl": "https://x.com/acme/status/1934567890123456789"
                      }
                    ]
                  },
                  "message": "Post published successfully"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/posts/{postId}": {
      "parameters": [
        {
          "name": "postId",
          "in": "path",
          "required": true,
          "description": "PostZen post id.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Posts"
        ],
        "operationId": "getPost",
        "summary": "Get a post",
        "description": "Returns one post when it belongs to a profile available to the API key. Missing and inaccessible posts both return 404.",
        "responses": {
          "200": {
            "description": "Post returned.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "post"
                  ],
                  "properties": {
                    "post": {
                      "$ref": "#/components/schemas/ApiPost"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      },
      "put": {
        "tags": [
          "Posts"
        ],
        "operationId": "updatePost",
        "summary": "Update a post",
        "description": "Updates an editable post. Every body field is optional; omitted fields keep their current values, and omitting all timing fields keeps the current scheduling and draft status. At most one of `publishNow`, `scheduledFor`, `isDraft`, or `queuedFromProfile` may select a new timing mode.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePostRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Post updated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpdatePostResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      },
      "delete": {
        "tags": [
          "Posts"
        ],
        "operationId": "deletePost",
        "summary": "Delete a post",
        "description": "Deletes a draft, scheduled, queued, failed, partially failed, or canceled post. Published and publishing posts cannot be deleted.",
        "responses": {
          "200": {
            "description": "Post deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "message"
                  ],
                  "properties": {
                    "message": {
                      "type": "string",
                      "const": "Post deleted successfully"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/posts/{postId}/comments": {
      "parameters": [
        {
          "name": "postId",
          "in": "path",
          "required": true,
          "description": "PostZen post id. The LinkedIn post URN is not accepted here.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Posts"
        ],
        "operationId": "listPostComments",
        "summary": "List comments on a LinkedIn post",
        "description": "Returns the comments LinkedIn holds for a post published through PostZen. LinkedIn only, and organization (company page) posts only. The post's LinkedIn connection must hold `r_organization_social_feed`, which LinkedIn ships with its Community Management API product — reconnect the account if it was connected before company-page posting was enabled. A personal (member) post always returns `403 personalPostUnsupported`: reading a member's own comments and reactions needs `r_member_social_feed`, which LinkedIn grants to select developers only. Read-only and read-write API keys are accepted.",
        "parameters": [
          {
            "name": "accountId",
            "in": "query",
            "description": "Restrict the lookup to one LinkedIn target when the post was published to several LinkedIn accounts. PostZen account id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "Opaque pagination cursor. Pass the `nextCursor` from the previous response to fetch the next page.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Page size.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Comments returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinkedInCommentsResponse"
                },
                "example": {
                  "comments": [
                    {
                      "id": "7123456789012345679",
                      "commentUrn": "urn:li:comment:(urn:li:share:7123456789012345678,7123456789012345679)",
                      "text": "Congratulations on the launch!",
                      "authorUrn": "urn:li:person:AbC1dEfGhI",
                      "authorName": "Jane Doe",
                      "createdAt": "2026-07-29T16:04:11.000Z",
                      "likeCount": 4,
                      "replyCount": 1
                    }
                  ],
                  "nextCursor": "25"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The post was authored by a member rather than a company page (`personalPostUnsupported`), the API key cannot access the post's profile (`forbidden`), organization engagement reads are disabled on the deployment (`orgScopesDisabled`), or the connection is missing `r_organization_social_feed` (`platformCapabilityMissing`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinkedInSocialReadErrorResponse"
                },
                "example": {
                  "error": "LinkedIn only exposes comments and reactions for organization-authored posts. Publish with a LinkedIn company page to read engagement back.",
                  "code": "personalPostUnsupported",
                  "platform": "linkedin"
                }
              }
            }
          },
          "404": {
            "description": "No post exists with the given id, it is not visible to this API key, or it has no LinkedIn target.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinkedInSocialReadErrorResponse"
                },
                "example": {
                  "error": "Not found",
                  "code": "notFound",
                  "platform": "linkedin"
                }
              }
            }
          },
          "409": {
            "description": "The post's LinkedIn target has not published yet, so it has no URN to read engagement for.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinkedInSocialReadErrorResponse"
                },
                "example": {
                  "error": "This post has not been published to LinkedIn yet",
                  "code": "postNotPublished",
                  "platform": "linkedin"
                }
              }
            }
          },
          "424": {
            "description": "The LinkedIn account behind the post is no longer connected.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinkedInSocialReadErrorResponse"
                },
                "example": {
                  "error": "The LinkedIn account for this post is no longer connected",
                  "code": "notConnected",
                  "platform": "linkedin"
                }
              }
            }
          },
          "429": {
            "description": "LinkedIn rate limited the upstream request. PostZen's own per-account rate limiter also returns `429`, with `{\"error\":\"rate_limited\"}` and a `Retry-After` header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinkedInSocialReadErrorResponse"
                },
                "example": {
                  "error": "LinkedIn rate limited this request. Retry later.",
                  "code": "rateLimited",
                  "platform": "linkedin"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "LinkedIn could not answer the request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinkedInSocialReadErrorResponse"
                },
                "example": {
                  "error": "LinkedIn could not answer this request",
                  "code": "requestFailed",
                  "platform": "linkedin"
                }
              }
            }
          }
        }
      }
    },
    "/v1/posts/{postId}/reactions": {
      "parameters": [
        {
          "name": "postId",
          "in": "path",
          "required": true,
          "description": "PostZen post id. The LinkedIn post URN is not accepted here.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Posts"
        ],
        "operationId": "listPostReactions",
        "summary": "List reactions on a LinkedIn post",
        "description": "Returns the individual reactions LinkedIn holds for a post published through PostZen, plus a per-type count of the returned page. LinkedIn only, and organization (company page) posts only. The post's LinkedIn connection must hold `r_organization_social_feed`, which LinkedIn ships with its Community Management API product — reconnect the account if it was connected before company-page posting was enabled. A personal (member) post always returns `403 personalPostUnsupported`: reading a member's own comments and reactions needs `r_member_social_feed`, which LinkedIn grants to select developers only. Read-only and read-write API keys are accepted.",
        "parameters": [
          {
            "name": "accountId",
            "in": "query",
            "description": "Restrict the lookup to one LinkedIn target when the post was published to several LinkedIn accounts. PostZen account id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "Opaque pagination cursor. Pass the `nextCursor` from the previous response to fetch the next page.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Page size.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Reactions returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinkedInReactionsResponse"
                },
                "example": {
                  "reactions": [
                    {
                      "reactionType": "LIKE",
                      "actorUrn": "urn:li:person:AbC1dEfGhI",
                      "createdAt": "2026-07-29T16:02:47.000Z"
                    },
                    {
                      "reactionType": "PRAISE",
                      "actorUrn": "urn:li:person:JkL2mNoPqR",
                      "createdAt": "2026-07-29T16:03:02.000Z"
                    }
                  ],
                  "totalsByType": {
                    "LIKE": 1,
                    "PRAISE": 1
                  },
                  "nextCursor": "25"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The post was authored by a member rather than a company page (`personalPostUnsupported`), the API key cannot access the post's profile (`forbidden`), organization engagement reads are disabled on the deployment (`orgScopesDisabled`), or the connection is missing `r_organization_social_feed` (`platformCapabilityMissing`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinkedInSocialReadErrorResponse"
                },
                "example": {
                  "error": "LinkedIn only exposes comments and reactions for organization-authored posts. Publish with a LinkedIn company page to read engagement back.",
                  "code": "personalPostUnsupported",
                  "platform": "linkedin"
                }
              }
            }
          },
          "404": {
            "description": "No post exists with the given id, it is not visible to this API key, or it has no LinkedIn target.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinkedInSocialReadErrorResponse"
                },
                "example": {
                  "error": "Not found",
                  "code": "notFound",
                  "platform": "linkedin"
                }
              }
            }
          },
          "409": {
            "description": "The post's LinkedIn target has not published yet, so it has no URN to read engagement for.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinkedInSocialReadErrorResponse"
                },
                "example": {
                  "error": "This post has not been published to LinkedIn yet",
                  "code": "postNotPublished",
                  "platform": "linkedin"
                }
              }
            }
          },
          "424": {
            "description": "The LinkedIn account behind the post is no longer connected.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinkedInSocialReadErrorResponse"
                },
                "example": {
                  "error": "The LinkedIn account for this post is no longer connected",
                  "code": "notConnected",
                  "platform": "linkedin"
                }
              }
            }
          },
          "429": {
            "description": "LinkedIn rate limited the upstream request. PostZen's own per-account rate limiter also returns `429`, with `{\"error\":\"rate_limited\"}` and a `Retry-After` header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinkedInSocialReadErrorResponse"
                },
                "example": {
                  "error": "LinkedIn rate limited this request. Retry later.",
                  "code": "rateLimited",
                  "platform": "linkedin"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "LinkedIn could not answer the request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinkedInSocialReadErrorResponse"
                },
                "example": {
                  "error": "LinkedIn could not answer this request",
                  "code": "requestFailed",
                  "platform": "linkedin"
                }
              }
            }
          }
        }
      }
    },
    "/v1/inbox/comments/{postId}": {
      "parameters": [
        {
          "name": "postId",
          "in": "path",
          "required": true,
          "description": "Post the comments belong to. Accepts a PostZen post id — resolved to the id the post was published under for `accountId` — or the platform's own post/media id, which is the common case because the inbox covers every post the account has, not only the ones PostZen published. Instagram and Facebook also accept a comment id here.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Inbox"
        ],
        "operationId": "listInboxPostComments",
        "summary": "List comments on a post",
        "description": "Returns the comments the platform holds for one Instagram, Facebook, or Threads post, and refreshes PostZen's stored copy of the thread as a side effect. `replies` carries the first page of nested replies when the platform returns them cheaply — `repliesHasMore` says when it did not, and passing that comment's id as `commentId` pages through the rest. Instagram threads are only two levels deep, so a reply never carries replies of its own. Fields a platform does not report are absent rather than zero: Threads, for example, never reports per-reply like counts. Read-only and read-write API keys are accepted.",
        "parameters": [
          {
            "name": "accountId",
            "in": "query",
            "required": true,
            "description": "PostZen account id of the connected Instagram, Facebook, or Threads account that owns the post.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Page size. Platform page ceilings may return fewer.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "Opaque pagination cursor. Pass `pagination.cursor` from the previous response to fetch the next page.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "commentId",
            "in": "query",
            "description": "Return the replies to this comment instead of the post's own comments.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Comments returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxCommentsResponse"
                },
                "example": {
                  "status": "success",
                  "comments": [
                    {
                      "id": "17912345678901234",
                      "message": "Love this one!",
                      "createdTime": "2026-08-17T14:22:08.000Z",
                      "from": {
                        "id": "17841400000000000",
                        "name": "Jane Doe",
                        "username": "janedoe",
                        "isOwner": false
                      },
                      "likeCount": 3,
                      "replyCount": 1,
                      "platform": "instagram",
                      "url": "https://www.instagram.com/p/CxYzAbCdEfG/c/17912345678901234/",
                      "replies": [
                        {
                          "id": "17912345678901299",
                          "message": "Thank you!",
                          "createdTime": "2026-08-17T14:41:52.000Z",
                          "from": {
                            "id": "17841411111111111",
                            "username": "postzenhq",
                            "isOwner": true
                          },
                          "platform": "instagram",
                          "replies": [],
                          "repliesHasMore": false,
                          "canReply": true,
                          "canDelete": true,
                          "canHide": true,
                          "isHidden": false,
                          "parentId": "17912345678901234"
                        }
                      ],
                      "repliesHasMore": false,
                      "canReply": true,
                      "canDelete": true,
                      "canHide": true,
                      "isHidden": false,
                      "parentId": null
                    }
                  ],
                  "pagination": {
                    "hasMore": true,
                    "cursor": "QVFIUkxsdGx..."
                  },
                  "meta": {
                    "platform": "instagram",
                    "postId": "17895695668004550",
                    "accountId": "k17d8s9f0a1b2c3d4e5f6g7h8i",
                    "lastUpdated": "2026-08-18T09:15:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "A parameter is missing or malformed — a plain `{ error }` body — or the account is not on a platform the inbox supports (`platformUnsupported`). A platform-rejected identifier or parameter also lands here, with `platformError` carrying the platform's wording.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxErrorResponse"
                },
                "example": {
                  "error": "The inbox supports Instagram, Facebook and Threads accounts only.",
                  "code": "platformUnsupported",
                  "platform": "tiktok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The account is outside the API key's profile access (`forbidden`), or the platform refused the token because the connection lacks the scope this read needs (`platformCapabilityMissing`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxErrorResponse"
                },
                "example": {
                  "error": "Reconnect the account to grant PostZen access to its comments.",
                  "code": "platformCapabilityMissing",
                  "platform": "facebook"
                }
              }
            }
          },
          "404": {
            "description": "No such account (`accountNotFound`), no such post for that account (`postNotFound`), the PostZen post has not published to this account yet (`postNotPublished`), or the platform reported the comment as missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxErrorResponse"
                },
                "example": {
                  "error": "That post could not be found.",
                  "code": "postNotFound"
                }
              }
            }
          },
          "424": {
            "description": "The account's connection is dead and must be reauthorized before its comments can be read or written.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxErrorResponse"
                },
                "example": {
                  "error": "This connection needs to be reauthorized before its comments can be read.",
                  "code": "connectionDead",
                  "platform": "instagram"
                }
              }
            }
          },
          "429": {
            "description": "The platform rate limited the upstream request; `platformError` carries its wording and `Retry-After` gives the wait in seconds. PostZen's own per-key and per-operation limiters also return `429`, with the plain body `{\"error\":\"rate_limited\",\"retryAfter\":<seconds>}`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxErrorResponse"
                },
                "example": {
                  "error": "(#4) Application request limit reached",
                  "code": "4",
                  "platform": "instagram",
                  "platformError": "(#4) Application request limit reached"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "The platform could not answer the request. `platformError` carries its wording.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxErrorResponse"
                },
                "example": {
                  "error": "Unsupported get request.",
                  "code": "100",
                  "platform": "facebook",
                  "platformError": "Unsupported get request."
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Inbox"
        ],
        "operationId": "replyToInboxPost",
        "summary": "Comment on a post or reply to a comment",
        "description": "Publishes a comment on the post, or a reply to `commentId` when one is given. Requires a read-write API key and the account's comment-management scope; without the scope the call answers `403 platformCapabilityMissing` and nothing is sent to the platform. `attachmentUrl` is Facebook only — every other platform rejects it with `400 attachmentUnsupported`. Instagram is two levels deep, so replying to a reply is redirected to that reply's top-level parent. A Threads reply is created as a container and then published; when the publish is still pending after PostZen's internal retries the call returns `502 publishPending` and the reply may still appear on its own, so check the thread before retrying rather than posting twice.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InboxReplyRequest"
              },
              "example": {
                "accountId": "k17d8s9f0a1b2c3d4e5f6g7h8i",
                "message": "Thank you!",
                "commentId": "17912345678901234"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Comment published.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxReplyResponse"
                },
                "example": {
                  "success": true,
                  "data": {
                    "commentId": "17912345678901299",
                    "isReply": true
                  }
                }
              }
            }
          },
          "400": {
            "description": "A parameter is missing or malformed — a plain `{ error }` body — the account is not on a platform the inbox supports (`platformUnsupported`), or `attachmentUrl` was sent for a platform other than Facebook (`attachmentUnsupported`). A platform-rejected identifier or parameter also lands here, with `platformError` carrying the platform's wording.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxErrorResponse"
                },
                "example": {
                  "error": "attachmentUrl is supported for Facebook comments only.",
                  "code": "attachmentUnsupported",
                  "platform": "instagram"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The API key is read-only, the account is outside its profile access (`forbidden`), or the connection is missing the account's comment-management scope (`platformCapabilityMissing`) — nothing is sent to the platform in that case.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxErrorResponse"
                },
                "example": {
                  "error": "Reconnect the account to grant PostZen access to its comments.",
                  "code": "platformCapabilityMissing",
                  "platform": "facebook"
                }
              }
            }
          },
          "404": {
            "description": "No such account (`accountNotFound`), no such post for that account (`postNotFound`), the PostZen post has not published to this account yet (`postNotPublished`), or the platform reported the comment as missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxErrorResponse"
                },
                "example": {
                  "error": "That post could not be found.",
                  "code": "postNotFound"
                }
              }
            }
          },
          "424": {
            "description": "The account's connection is dead and must be reauthorized before its comments can be read or written.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxErrorResponse"
                },
                "example": {
                  "error": "This connection needs to be reauthorized before its comments can be read.",
                  "code": "connectionDead",
                  "platform": "instagram"
                }
              }
            }
          },
          "429": {
            "description": "The platform rate limited the upstream request; `platformError` carries its wording and `Retry-After` gives the wait in seconds. PostZen's own per-key and per-operation limiters also return `429`, with the plain body `{\"error\":\"rate_limited\",\"retryAfter\":<seconds>}`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxErrorResponse"
                },
                "example": {
                  "error": "(#4) Application request limit reached",
                  "code": "4",
                  "platform": "instagram",
                  "platformError": "(#4) Application request limit reached"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "The platform could not answer the request. `platformError` carries its wording.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxErrorResponse"
                },
                "example": {
                  "error": "Unsupported get request.",
                  "code": "100",
                  "platform": "facebook",
                  "platformError": "Unsupported get request."
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Inbox"
        ],
        "operationId": "deleteInboxComment",
        "summary": "Delete a comment",
        "description": "Deletes a comment. Requires a read-write API key and the account's comment-management scope. `canDelete` on the comment says whether the platform allows it: Facebook reports it per comment, and Threads only allows deleting replies the connected account itself wrote.",
        "parameters": [
          {
            "name": "accountId",
            "in": "query",
            "required": true,
            "description": "PostZen account id of the connected Instagram, Facebook, or Threads account that owns the post.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "commentId",
            "in": "query",
            "required": true,
            "description": "Platform id of the comment to delete.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Comment deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxActionResponse"
                },
                "example": {
                  "success": true,
                  "data": {
                    "message": "Comment deleted"
                  }
                }
              }
            }
          },
          "400": {
            "description": "A parameter is missing or malformed — a plain `{ error }` body — or the account is not on a platform the inbox supports (`platformUnsupported`). A platform-rejected identifier or parameter also lands here, with `platformError` carrying the platform's wording.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxErrorResponse"
                },
                "example": {
                  "error": "The inbox supports Instagram, Facebook and Threads accounts only.",
                  "code": "platformUnsupported",
                  "platform": "tiktok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The API key is read-only, the account is outside its profile access (`forbidden`), or the connection is missing the account's comment-management scope (`platformCapabilityMissing`) — nothing is sent to the platform in that case.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxErrorResponse"
                },
                "example": {
                  "error": "Reconnect the account to grant PostZen access to its comments.",
                  "code": "platformCapabilityMissing",
                  "platform": "facebook"
                }
              }
            }
          },
          "404": {
            "description": "No such account (`accountNotFound`), no such post for that account (`postNotFound`), the PostZen post has not published to this account yet (`postNotPublished`), or the platform reported the comment as missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxErrorResponse"
                },
                "example": {
                  "error": "That post could not be found.",
                  "code": "postNotFound"
                }
              }
            }
          },
          "424": {
            "description": "The account's connection is dead and must be reauthorized before its comments can be read or written.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxErrorResponse"
                },
                "example": {
                  "error": "This connection needs to be reauthorized before its comments can be read.",
                  "code": "connectionDead",
                  "platform": "instagram"
                }
              }
            }
          },
          "429": {
            "description": "The platform rate limited the upstream request; `platformError` carries its wording and `Retry-After` gives the wait in seconds. PostZen's own per-key and per-operation limiters also return `429`, with the plain body `{\"error\":\"rate_limited\",\"retryAfter\":<seconds>}`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxErrorResponse"
                },
                "example": {
                  "error": "(#4) Application request limit reached",
                  "code": "4",
                  "platform": "instagram",
                  "platformError": "(#4) Application request limit reached"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "The platform could not answer the request. `platformError` carries its wording.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxErrorResponse"
                },
                "example": {
                  "error": "Unsupported get request.",
                  "code": "100",
                  "platform": "facebook",
                  "platformError": "Unsupported get request."
                }
              }
            }
          }
        }
      }
    },
    "/v1/inbox/comments/{postId}/{commentId}/hide": {
      "parameters": [
        {
          "name": "postId",
          "in": "path",
          "required": true,
          "description": "Post the comments belong to. Accepts a PostZen post id — resolved to the id the post was published under for `accountId` — or the platform's own post/media id, which is the common case because the inbox covers every post the account has, not only the ones PostZen published. Instagram and Facebook also accept a comment id here.",
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "commentId",
          "in": "path",
          "required": true,
          "description": "Platform id of the comment to hide or unhide.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "tags": [
          "Inbox"
        ],
        "operationId": "hideInboxComment",
        "summary": "Hide a comment",
        "description": "Hides a comment so only its author still sees it. Requires a read-write API key and the account's comment-management scope. `canHide` on the comment says whether the platform allows it: Facebook reports it per comment, and Threads only supports hiding top-level replies on the connected account's own posts — hiding one there also hides the replies beneath it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InboxHideRequest"
              },
              "example": {
                "accountId": "k17d8s9f0a1b2c3d4e5f6g7h8i"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Comment hidden.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxHideResponse"
                },
                "example": {
                  "status": "success",
                  "commentId": "17912345678901234",
                  "hidden": true,
                  "platform": "instagram"
                }
              }
            }
          },
          "400": {
            "description": "A parameter is missing or malformed — a plain `{ error }` body — or the account is not on a platform the inbox supports (`platformUnsupported`). A platform-rejected identifier or parameter also lands here, with `platformError` carrying the platform's wording.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxErrorResponse"
                },
                "example": {
                  "error": "The inbox supports Instagram, Facebook and Threads accounts only.",
                  "code": "platformUnsupported",
                  "platform": "tiktok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The API key is read-only, the account is outside its profile access (`forbidden`), or the connection is missing the account's comment-management scope (`platformCapabilityMissing`) — nothing is sent to the platform in that case.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxErrorResponse"
                },
                "example": {
                  "error": "Reconnect the account to grant PostZen access to its comments.",
                  "code": "platformCapabilityMissing",
                  "platform": "facebook"
                }
              }
            }
          },
          "404": {
            "description": "No such account (`accountNotFound`), no such post for that account (`postNotFound`), the PostZen post has not published to this account yet (`postNotPublished`), or the platform reported the comment as missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxErrorResponse"
                },
                "example": {
                  "error": "That post could not be found.",
                  "code": "postNotFound"
                }
              }
            }
          },
          "424": {
            "description": "The account's connection is dead and must be reauthorized before its comments can be read or written.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxErrorResponse"
                },
                "example": {
                  "error": "This connection needs to be reauthorized before its comments can be read.",
                  "code": "connectionDead",
                  "platform": "instagram"
                }
              }
            }
          },
          "429": {
            "description": "The platform rate limited the upstream request; `platformError` carries its wording and `Retry-After` gives the wait in seconds. PostZen's own per-key and per-operation limiters also return `429`, with the plain body `{\"error\":\"rate_limited\",\"retryAfter\":<seconds>}`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxErrorResponse"
                },
                "example": {
                  "error": "(#4) Application request limit reached",
                  "code": "4",
                  "platform": "instagram",
                  "platformError": "(#4) Application request limit reached"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "The platform could not answer the request. `platformError` carries its wording.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxErrorResponse"
                },
                "example": {
                  "error": "Unsupported get request.",
                  "code": "100",
                  "platform": "facebook",
                  "platformError": "Unsupported get request."
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Inbox"
        ],
        "operationId": "unhideInboxComment",
        "summary": "Unhide a comment",
        "description": "Restores a hidden comment. Requires a read-write API key and the account's comment-management scope. On Threads, unhiding a top-level reply also restores the replies beneath it.",
        "parameters": [
          {
            "name": "accountId",
            "in": "query",
            "required": true,
            "description": "PostZen account id of the connected Instagram, Facebook, or Threads account that owns the post.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Comment unhidden.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxHideResponse"
                },
                "example": {
                  "status": "success",
                  "commentId": "17912345678901234",
                  "hidden": false,
                  "platform": "instagram"
                }
              }
            }
          },
          "400": {
            "description": "A parameter is missing or malformed — a plain `{ error }` body — or the account is not on a platform the inbox supports (`platformUnsupported`). A platform-rejected identifier or parameter also lands here, with `platformError` carrying the platform's wording.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxErrorResponse"
                },
                "example": {
                  "error": "The inbox supports Instagram, Facebook and Threads accounts only.",
                  "code": "platformUnsupported",
                  "platform": "tiktok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The API key is read-only, the account is outside its profile access (`forbidden`), or the connection is missing the account's comment-management scope (`platformCapabilityMissing`) — nothing is sent to the platform in that case.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxErrorResponse"
                },
                "example": {
                  "error": "Reconnect the account to grant PostZen access to its comments.",
                  "code": "platformCapabilityMissing",
                  "platform": "facebook"
                }
              }
            }
          },
          "404": {
            "description": "No such account (`accountNotFound`), no such post for that account (`postNotFound`), the PostZen post has not published to this account yet (`postNotPublished`), or the platform reported the comment as missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxErrorResponse"
                },
                "example": {
                  "error": "That post could not be found.",
                  "code": "postNotFound"
                }
              }
            }
          },
          "424": {
            "description": "The account's connection is dead and must be reauthorized before its comments can be read or written.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxErrorResponse"
                },
                "example": {
                  "error": "This connection needs to be reauthorized before its comments can be read.",
                  "code": "connectionDead",
                  "platform": "instagram"
                }
              }
            }
          },
          "429": {
            "description": "The platform rate limited the upstream request; `platformError` carries its wording and `Retry-After` gives the wait in seconds. PostZen's own per-key and per-operation limiters also return `429`, with the plain body `{\"error\":\"rate_limited\",\"retryAfter\":<seconds>}`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxErrorResponse"
                },
                "example": {
                  "error": "(#4) Application request limit reached",
                  "code": "4",
                  "platform": "instagram",
                  "platformError": "(#4) Application request limit reached"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "The platform could not answer the request. `platformError` carries its wording.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxErrorResponse"
                },
                "example": {
                  "error": "Unsupported get request.",
                  "code": "100",
                  "platform": "facebook",
                  "platformError": "Unsupported get request."
                }
              }
            }
          }
        }
      }
    },
    "/v1/inbox/conversations": {
      "get": {
        "tags": [
          "Inbox"
        ],
        "operationId": "listInboxConversations",
        "summary": "List direct message conversations",
        "description": "Returns the Instagram direct message threads PostZen holds for the accounts this key can reach, newest activity first, and refreshes that stored copy from the platform as a side effect (throttled per account, so a tight polling loop costs nothing extra). `meta` reports which accounts could not be refreshed; their stored threads are still returned. Read-only and read-write API keys are accepted.",
        "parameters": [
          {
            "name": "accountId",
            "in": "query",
            "description": "Restrict the read to one connected account. Omitted, every Instagram account the key can reach is queried.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "profileId",
            "in": "query",
            "description": "Restrict the read to accounts on one profile.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "platform",
            "in": "query",
            "description": "Restrict the read to one platform. Direct messages cover Instagram only; any other value is a validation error.",
            "schema": {
              "type": "string",
              "enum": [
                "instagram"
              ]
            }
          },
          {
            "name": "status",
            "in": "query",
            "description": "Return only active or only archived threads. Archive state is PostZen-local.",
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "archived"
              ]
            }
          },
          {
            "name": "sortOrder",
            "in": "query",
            "description": "Order by `updatedTime`.",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "desc"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Page size.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "Opaque pagination cursor. Pass `pagination.nextCursor` from the previous response.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Conversations returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxConversationsResponse"
                },
                "example": {
                  "data": [
                    {
                      "id": "t_17845791234567890",
                      "platform": "instagram",
                      "accountId": "k17d8s9f0a1b2c3d4e5f6g7h8i",
                      "accountUsername": "postzenhq",
                      "participantId": "17841400000000000",
                      "participantName": "Jane Doe",
                      "participantUsername": "janedoe",
                      "participantPicture": "https://scontent.cdninstagram.com/v/t51.2885-19/jane.jpg",
                      "lastMessage": "Is the sale still on?",
                      "lastMessageAt": "2026-08-18T16:04:11.000Z",
                      "updatedTime": "2026-08-18T16:04:11.000Z",
                      "status": "active",
                      "unreadCount": 2
                    }
                  ],
                  "pagination": {
                    "hasMore": true,
                    "nextCursor": "eyJpZCI6ImsxN2Q4czlmMGExYjJjM2Q0ZTVmNmc3aDhpIn0"
                  },
                  "meta": {
                    "accountsQueried": 2,
                    "accountsFailed": 0,
                    "failedAccounts": [],
                    "lastUpdated": "2026-08-18T16:10:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "A parameter is missing or malformed — a plain `{ error }` body — or the account is not on a platform direct messages support (`platformUnsupported`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxDmErrorResponse"
                },
                "example": {
                  "error": "Direct messages support Instagram accounts only.",
                  "code": "platformUnsupported",
                  "platform": "tiktok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The account is outside the API key's profile access (`forbidden`), or the connection is missing the messaging scope this call needs (`platformCapabilityMissing`). Reconnect the account to grant it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxDmErrorResponse"
                },
                "example": {
                  "error": "Reconnect the account to grant PostZen access to its messages.",
                  "code": "platformCapabilityMissing",
                  "platform": "instagram"
                }
              }
            }
          },
          "404": {
            "description": "No such account (`accountNotFound`) or no such conversation for that account (`conversationNotFound`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxDmErrorResponse"
                },
                "example": {
                  "error": "That conversation could not be found.",
                  "code": "conversationNotFound"
                }
              }
            }
          },
          "424": {
            "description": "The connection needs to be reauthorized before its messages can be read (`connectionDead`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxDmErrorResponse"
                },
                "example": {
                  "error": "This connection needs to be reauthorized before its messages can be read.",
                  "code": "connectionDead"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/inbox/conversations/search": {
      "get": {
        "tags": [
          "Inbox"
        ],
        "operationId": "searchInboxConversations",
        "summary": "Search direct message conversations",
        "description": "Searches the conversations PostZen has already synced — Meta exposes no message search API, so this reads PostZen's own copy and refreshes it first. Message text matches whole tokens, case- and accent-insensitively (`cafe` finds `café`, `art` does not match `start`); participant names and usernames match on substring. Setting `direction` narrows the search to message text only, so a thread that matched only on the participant's name is excluded. Read-only and read-write API keys are accepted.",
        "parameters": [
          {
            "name": "query",
            "in": "query",
            "required": true,
            "description": "Search term.",
            "schema": {
              "type": "string",
              "minLength": 2,
              "maxLength": 200
            }
          },
          {
            "name": "direction",
            "in": "query",
            "description": "Match only messages the account received (`incoming`) or sent (`outgoing`). Restricts matching to message text.",
            "schema": {
              "type": "string",
              "enum": [
                "incoming",
                "outgoing"
              ]
            }
          },
          {
            "name": "accountId",
            "in": "query",
            "description": "Restrict the read to one connected account. Omitted, every Instagram account the key can reach is queried.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "profileId",
            "in": "query",
            "description": "Restrict the read to accounts on one profile.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "platform",
            "in": "query",
            "description": "Restrict the read to one platform. Direct messages cover Instagram only; any other value is a validation error.",
            "schema": {
              "type": "string",
              "enum": [
                "instagram"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Page size.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "Pagination cursor from `pagination.nextCursor`. Unlike the list cursor this one is an offset into the result set, so it is only valid for the same query.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Matching conversations returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxConversationSearchResponse"
                },
                "example": {
                  "data": [
                    {
                      "conversation": {
                        "id": "t_17845791234567890",
                        "platform": "instagram",
                        "accountId": "k17d8s9f0a1b2c3d4e5f6g7h8i",
                        "participantName": "Jane Doe",
                        "participantUsername": "janedoe",
                        "status": "active",
                        "lastMessage": "Is the sale still on?",
                        "lastMessageAt": "2026-08-18T16:04:11.000Z"
                      },
                      "matchCount": 2,
                      "matches": [
                        {
                          "id": "aWdfZGFtOm1zZ18x",
                          "text": "Is the sale still on?",
                          "direction": "incoming",
                          "timestamp": "2026-08-18T16:04:11.000Z"
                        },
                        {
                          "id": "aWdfZGFtOm1zZ18w",
                          "text": "The sale runs through Friday.",
                          "direction": "outgoing",
                          "timestamp": "2026-08-17T09:12:44.000Z"
                        }
                      ]
                    }
                  ],
                  "pagination": {
                    "hasMore": false,
                    "nextCursor": null
                  },
                  "meta": {
                    "accountsQueried": 2,
                    "accountsFailed": 0,
                    "failedAccounts": [],
                    "lastUpdated": "2026-08-18T16:10:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "A parameter is missing or malformed — a plain `{ error }` body — or the account is not on a platform direct messages support (`platformUnsupported`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxDmErrorResponse"
                },
                "example": {
                  "error": "Direct messages support Instagram accounts only.",
                  "code": "platformUnsupported",
                  "platform": "tiktok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The account is outside the API key's profile access (`forbidden`), or the connection is missing the messaging scope this call needs (`platformCapabilityMissing`). Reconnect the account to grant it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxDmErrorResponse"
                },
                "example": {
                  "error": "Reconnect the account to grant PostZen access to its messages.",
                  "code": "platformCapabilityMissing",
                  "platform": "instagram"
                }
              }
            }
          },
          "404": {
            "description": "No such account (`accountNotFound`) or no such conversation for that account (`conversationNotFound`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxDmErrorResponse"
                },
                "example": {
                  "error": "That conversation could not be found.",
                  "code": "conversationNotFound"
                }
              }
            }
          },
          "424": {
            "description": "The connection needs to be reauthorized before its messages can be read (`connectionDead`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxDmErrorResponse"
                },
                "example": {
                  "error": "This connection needs to be reauthorized before its messages can be read.",
                  "code": "connectionDead"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/inbox/conversations/{conversationId}": {
      "parameters": [
        {
          "name": "conversationId",
          "in": "path",
          "required": true,
          "description": "The platform's own conversation (thread) id, as returned in `id` by the list and search endpoints — not a PostZen document id.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Inbox"
        ],
        "operationId": "getInboxConversation",
        "summary": "Get a direct message conversation",
        "description": "Returns one thread, refreshing PostZen’s stored copy from the platform first. Read-only and read-write API keys are accepted.",
        "parameters": [
          {
            "name": "accountId",
            "in": "query",
            "required": true,
            "description": "PostZen account id that owns the conversation.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Conversation returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxConversationResponse"
                },
                "example": {
                  "data": {
                    "id": "t_17845791234567890",
                    "platform": "instagram",
                    "accountId": "k17d8s9f0a1b2c3d4e5f6g7h8i",
                    "accountUsername": "postzenhq",
                    "participantId": "17841400000000000",
                    "participantName": "Jane Doe",
                    "participantUsername": "janedoe",
                    "participantPicture": "https://scontent.cdninstagram.com/v/t51.2885-19/jane.jpg",
                    "lastMessage": "Is the sale still on?",
                    "lastMessageAt": "2026-08-18T16:04:11.000Z",
                    "updatedTime": "2026-08-18T16:04:11.000Z",
                    "status": "active",
                    "unreadCount": 2
                  }
                }
              }
            }
          },
          "400": {
            "description": "A parameter is missing or malformed — a plain `{ error }` body — or the account is not on a platform direct messages support (`platformUnsupported`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxDmErrorResponse"
                },
                "example": {
                  "error": "Direct messages support Instagram accounts only.",
                  "code": "platformUnsupported",
                  "platform": "tiktok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The account is outside the API key's profile access (`forbidden`), or the connection is missing the messaging scope this call needs (`platformCapabilityMissing`). Reconnect the account to grant it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxDmErrorResponse"
                },
                "example": {
                  "error": "Reconnect the account to grant PostZen access to its messages.",
                  "code": "platformCapabilityMissing",
                  "platform": "instagram"
                }
              }
            }
          },
          "404": {
            "description": "No such account (`accountNotFound`) or no such conversation for that account (`conversationNotFound`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxDmErrorResponse"
                },
                "example": {
                  "error": "That conversation could not be found.",
                  "code": "conversationNotFound"
                }
              }
            }
          },
          "424": {
            "description": "The connection needs to be reauthorized before its messages can be read (`connectionDead`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxDmErrorResponse"
                },
                "example": {
                  "error": "This connection needs to be reauthorized before its messages can be read.",
                  "code": "connectionDead"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "put": {
        "tags": [
          "Inbox"
        ],
        "operationId": "updateInboxConversation",
        "summary": "Archive or unarchive a conversation",
        "description": "Sets the thread’s status. This is PostZen-local state — Meta has no archive API, so nothing is sent to the platform and a later sync never overwrites it. Requires a read-write API key.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InboxConversationUpdateRequest"
              },
              "example": {
                "accountId": "k17d8s9f0a1b2c3d4e5f6g7h8i",
                "status": "archived"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Conversation updated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxConversationUpdateResponse"
                },
                "example": {
                  "success": true,
                  "data": {
                    "id": "t_17845791234567890",
                    "accountId": "k17d8s9f0a1b2c3d4e5f6g7h8i",
                    "status": "archived",
                    "platform": "instagram",
                    "updatedAt": "2026-08-18T16:12:03.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "A parameter is missing or malformed — a plain `{ error }` body — or the account is not on a platform direct messages support (`platformUnsupported`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxDmErrorResponse"
                },
                "example": {
                  "error": "Direct messages support Instagram accounts only.",
                  "code": "platformUnsupported",
                  "platform": "tiktok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The account is outside the API key's profile access (`forbidden`), or the connection is missing the messaging scope this call needs (`platformCapabilityMissing`). Reconnect the account to grant it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxDmErrorResponse"
                },
                "example": {
                  "error": "Reconnect the account to grant PostZen access to its messages.",
                  "code": "platformCapabilityMissing",
                  "platform": "instagram"
                }
              }
            }
          },
          "404": {
            "description": "No such account (`accountNotFound`) or no such conversation for that account (`conversationNotFound`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxDmErrorResponse"
                },
                "example": {
                  "error": "That conversation could not be found.",
                  "code": "conversationNotFound"
                }
              }
            }
          },
          "424": {
            "description": "The connection needs to be reauthorized before its messages can be read (`connectionDead`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxDmErrorResponse"
                },
                "example": {
                  "error": "This connection needs to be reauthorized before its messages can be read.",
                  "code": "connectionDead"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/inbox/conversations/{conversationId}/read": {
      "parameters": [
        {
          "name": "conversationId",
          "in": "path",
          "required": true,
          "description": "The platform's own conversation (thread) id, as returned in `id` by the list and search endpoints — not a PostZen document id.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "tags": [
          "Inbox"
        ],
        "operationId": "markInboxConversationRead",
        "summary": "Mark a conversation read",
        "description": "Moves the thread’s local read watermark to its newest synced message and returns how many incoming messages crossed from unread to read. Nothing is sent to the platform, and reading messages never does this implicitly. Requires a read-write API key.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InboxConversationReadRequest"
              },
              "example": {
                "accountId": "k17d8s9f0a1b2c3d4e5f6g7h8i"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Conversation marked read.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxConversationReadResponse"
                },
                "example": {
                  "success": true,
                  "markedCount": 2
                }
              }
            }
          },
          "400": {
            "description": "A parameter is missing or malformed — a plain `{ error }` body — or the account is not on a platform direct messages support (`platformUnsupported`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxDmErrorResponse"
                },
                "example": {
                  "error": "Direct messages support Instagram accounts only.",
                  "code": "platformUnsupported",
                  "platform": "tiktok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The account is outside the API key's profile access (`forbidden`), or the connection is missing the messaging scope this call needs (`platformCapabilityMissing`). Reconnect the account to grant it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxDmErrorResponse"
                },
                "example": {
                  "error": "Reconnect the account to grant PostZen access to its messages.",
                  "code": "platformCapabilityMissing",
                  "platform": "instagram"
                }
              }
            }
          },
          "404": {
            "description": "No such account (`accountNotFound`) or no such conversation for that account (`conversationNotFound`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxDmErrorResponse"
                },
                "example": {
                  "error": "That conversation could not be found.",
                  "code": "conversationNotFound"
                }
              }
            }
          },
          "424": {
            "description": "The connection needs to be reauthorized before its messages can be read (`connectionDead`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxDmErrorResponse"
                },
                "example": {
                  "error": "This connection needs to be reauthorized before its messages can be read.",
                  "code": "connectionDead"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/inbox/conversations/{conversationId}/messages": {
      "parameters": [
        {
          "name": "conversationId",
          "in": "path",
          "required": true,
          "description": "The platform's own conversation (thread) id, as returned in `id` by the list and search endpoints — not a PostZen document id.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Inbox"
        ],
        "operationId": "listInboxConversationMessages",
        "summary": "List messages in a conversation",
        "description": "Returns the messages PostZen holds for one thread, oldest first by default, refreshing them from the platform when the stored copy is stale. Strictly read-only: it never marks the thread read — use `POST /v1/inbox/conversations/{conversationId}/read` for that. Meta returns full content only for roughly the twenty newest Instagram messages, so older ones may carry an id and timestamp without text or attachments. Read-only and read-write API keys are accepted.",
        "parameters": [
          {
            "name": "accountId",
            "in": "query",
            "required": true,
            "description": "PostZen account id that owns the conversation.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Page size.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "Opaque pagination cursor from `pagination.nextCursor`. A malformed cursor is a 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sortOrder",
            "in": "query",
            "description": "Order by send time.",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "asc"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Messages returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxMessagesResponse"
                },
                "example": {
                  "status": "ok",
                  "pagination": {
                    "hasMore": false,
                    "nextCursor": null
                  },
                  "sortOrderApplied": "asc",
                  "messages": [
                    {
                      "id": "aWdfZGFtOm1zZ18w",
                      "conversationId": "t_17845791234567890",
                      "accountId": "k17d8s9f0a1b2c3d4e5f6g7h8i",
                      "platform": "instagram",
                      "message": "The sale runs through Friday.",
                      "senderId": "17841411111111111",
                      "senderName": "postzenhq",
                      "direction": "outgoing",
                      "createdAt": "2026-08-17T09:12:44.000Z"
                    },
                    {
                      "id": "aWdfZGFtOm1zZ18x",
                      "conversationId": "t_17845791234567890",
                      "accountId": "k17d8s9f0a1b2c3d4e5f6g7h8i",
                      "platform": "instagram",
                      "message": "Is the sale still on?",
                      "senderId": "17841400000000000",
                      "senderName": "Jane Doe",
                      "direction": "incoming",
                      "createdAt": "2026-08-18T16:04:11.000Z"
                    }
                  ],
                  "lastUpdated": "2026-08-18T16:10:00.000Z"
                }
              }
            }
          },
          "400": {
            "description": "A parameter is missing or malformed — a plain `{ error }` body — or the account is not on a platform direct messages support (`platformUnsupported`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxDmErrorResponse"
                },
                "example": {
                  "error": "Direct messages support Instagram accounts only.",
                  "code": "platformUnsupported",
                  "platform": "tiktok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The account is outside the API key's profile access (`forbidden`), or the connection is missing the messaging scope this call needs (`platformCapabilityMissing`). Reconnect the account to grant it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxDmErrorResponse"
                },
                "example": {
                  "error": "Reconnect the account to grant PostZen access to its messages.",
                  "code": "platformCapabilityMissing",
                  "platform": "instagram"
                }
              }
            }
          },
          "404": {
            "description": "No such account (`accountNotFound`) or no such conversation for that account (`conversationNotFound`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxDmErrorResponse"
                },
                "example": {
                  "error": "That conversation could not be found.",
                  "code": "conversationNotFound"
                }
              }
            }
          },
          "424": {
            "description": "The connection needs to be reauthorized before its messages can be read (`connectionDead`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxDmErrorResponse"
                },
                "example": {
                  "error": "This connection needs to be reauthorized before its messages can be read.",
                  "code": "connectionDead"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "Inbox"
        ],
        "operationId": "sendInboxMessage",
        "summary": "Send a direct message",
        "description": "Sends a message into an existing thread. The recipient is the other participant on that thread, so no recipient id is needed. Exactly one of `message` or `attachmentUrl` may be sent per call — Meta's Send API carries one payload at a time, so combining them is a 400. Meta only accepts replies within 24 hours of the person's last message; outside that window the platform refuses the send and PostZen reports `400 PLATFORM_LIMITATION` with Meta's own envelope attached. Sends are hard-gated on the account's messaging scope. Requires a read-write API key.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "Replay protection. Retrying with the same key and the same body replays the stored outcome and sets `Idempotent-Replayed: true`; the same key with a different body is a 422, and a key whose first request is still in flight is a 409. A send whose outcome could not be confirmed (`502 providerOutcomeUnknown`) is stored as a terminal outcome, so retrying that key returns the same 502 rather than risking a duplicate message. Keys are retained for 24 hours.",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InboxMessageSendRequest"
              },
              "example": {
                "accountId": "k17d8s9f0a1b2c3d4e5f6g7h8i",
                "message": "Yes — the sale runs through Friday!"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Message sent. `Idempotent-Replayed: true` on the response means this was a replay of an earlier identical request rather than a new send.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxMessageSendResponse"
                },
                "example": {
                  "success": true,
                  "data": {
                    "messageId": "aWdfZGFtOm1zZ18y",
                    "conversationId": "t_17845791234567890",
                    "sentAt": "2026-08-18T16:14:52.000Z",
                    "message": "Yes — the sale runs through Friday!"
                  }
                }
              }
            }
          },
          "400": {
            "description": "A parameter is missing or malformed, `message` and `attachmentUrl` were combined, the account is not on a supported platform (`platformUnsupported`), or the platform refused a send it could accept at another time (`PLATFORM_LIMITATION`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxDmErrorResponse"
                },
                "example": {
                  "error": "This message is sent outside of allowed window.",
                  "code": "PLATFORM_LIMITATION",
                  "platform": "instagram",
                  "platformError": {
                    "code": 10,
                    "subcode": 2534022,
                    "type": "OAuthException"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The account is outside the API key's profile access (`forbidden`), or the connection is missing the messaging scope this call needs (`platformCapabilityMissing`). Reconnect the account to grant it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxDmErrorResponse"
                },
                "example": {
                  "error": "Reconnect the account to grant PostZen access to its messages.",
                  "code": "platformCapabilityMissing",
                  "platform": "instagram"
                }
              }
            }
          },
          "404": {
            "description": "No such account (`accountNotFound`) or no such conversation for that account (`conversationNotFound`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxDmErrorResponse"
                },
                "example": {
                  "error": "That conversation could not be found.",
                  "code": "conversationNotFound"
                }
              }
            }
          },
          "409": {
            "description": "A request with the same `Idempotency-Key` is still in flight (`idempotencyInFlight`). Retry once it settles.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxDmErrorResponse"
                },
                "example": {
                  "error": "A request with that Idempotency-Key is still in flight.",
                  "code": "idempotencyInFlight"
                }
              }
            }
          },
          "422": {
            "description": "That `Idempotency-Key` was already used with a different request body (`idempotencyConflict`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxDmErrorResponse"
                },
                "example": {
                  "error": "That Idempotency-Key was already used with a different request body.",
                  "code": "idempotencyConflict"
                }
              }
            }
          },
          "424": {
            "description": "The connection needs to be reauthorized before its messages can be read (`connectionDead`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxDmErrorResponse"
                },
                "example": {
                  "error": "This connection needs to be reauthorized before its messages can be read.",
                  "code": "connectionDead"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "The send request to Meta did not come back (timeout or transport fault), so PostZen cannot confirm whether the message was delivered (`providerOutcomeUnknown`). Check the conversation before sending again — retrying the same `Idempotency-Key` returns this same response rather than sending a second copy.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxDmErrorResponse"
                },
                "example": {
                  "error": "Message delivery outcome unknown — do not retry with the same idempotency key. Check the conversation before sending again.",
                  "code": "providerOutcomeUnknown",
                  "platform": "instagram"
                }
              }
            }
          }
        }
      }
    },
    "/v1/queue/slots": {
      "get": {
        "tags": [
          "Queues"
        ],
        "operationId": "listQueueSlots",
        "summary": "Get a queue schedule",
        "description": "Returns the queue selected by `queueId`, or the profile's default queue when `queueId` is omitted. With `all=true` every queue on the profile is returned instead. `nextSlots` holds the next five instants the queue would hand out, already skipping occupied slots; a paused or slotless queue returns an empty array.",
        "parameters": [
          {
            "name": "profileId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Profile that owns the queue."
          },
          {
            "name": "queueId",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Specific queue. Defaults to the profile's default queue. Cannot be combined with `all=true`."
          },
          {
            "name": "all",
            "in": "query",
            "schema": {
              "type": "boolean"
            },
            "description": "Return every queue on the profile instead of a single schedule."
          }
        ],
        "responses": {
          "200": {
            "description": "Queue schedule returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QueueSlotsResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "Queues"
        ],
        "operationId": "createQueueSlot",
        "summary": "Create a queue",
        "description": "Creates a posting queue on a profile. A profile may hold up to 10 queues, each with up to 56 slots. The first queue on a profile automatically becomes its default. This endpoint requires a read-write API key.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QueueCreateRequest"
              },
              "example": {
                "profileId": "k57a1p3q9w2y8b6d4f0g",
                "name": "Morning Posts",
                "timezone": "America/Edmonton",
                "slots": [
                  {
                    "dayOfWeek": 1,
                    "time": "09:00"
                  },
                  {
                    "dayOfWeek": 3,
                    "time": "09:00"
                  }
                ],
                "active": true
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Queue created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QueueWriteResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The profile has reached its queue limit.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "put": {
        "tags": [
          "Queues"
        ],
        "operationId": "updateQueueSlot",
        "summary": "Update a queue",
        "description": "Replaces a queue's timezone and slots, and optionally renames, pauses, or promotes it to default. With `reshuffleExisting: true` the queue's future scheduled posts are re-placed onto the new schedule in their existing order; posts that are already publishing, published, or failed are never moved, and at most 200 posts may move in one call. Validation and slot assignment both run before the first write, so a rejected update leaves the queue untouched. This endpoint requires a read-write API key.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QueueUpdateRequest"
              },
              "example": {
                "profileId": "k57a1p3q9w2y8b6d4f0g",
                "timezone": "America/Edmonton",
                "slots": [
                  {
                    "dayOfWeek": 1,
                    "time": "08:30"
                  }
                ],
                "reshuffleExisting": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Queue updated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QueueUpdateResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "delete": {
        "tags": [
          "Queues"
        ],
        "operationId": "deleteQueueSlot",
        "summary": "Delete a queue",
        "description": "Deletes the queue named by `queueId`, or every queue on the profile when `queueId` is omitted. Posts already placed by the queue keep their scheduled times; only the queue link is removed. Deleting the default queue promotes the profile's oldest remaining queue. This endpoint requires a read-write API key.",
        "parameters": [
          {
            "name": "profileId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Profile that owns the queue."
          },
          {
            "name": "queueId",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Queue to delete. Omit to delete every queue on the profile."
          }
        ],
        "responses": {
          "200": {
            "description": "Queue deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QueueDeleteResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/queue/next-slot": {
      "get": {
        "tags": [
          "Queues"
        ],
        "operationId": "getNextQueueSlot",
        "summary": "Get the next queue slot",
        "description": "Returns the next free instant the queue would hand out. This is a preview only — the slot is not reserved. Create queued posts with `queuedFromProfile` rather than passing this value back as `scheduledFor`, or a concurrent create can take the slot first.",
        "parameters": [
          {
            "name": "profileId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Profile that owns the queue."
          },
          {
            "name": "queueId",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Specific queue. Defaults to the profile's default queue."
          }
        ],
        "responses": {
          "200": {
            "description": "Next slot returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QueueNextSlotResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The queue is paused or has no slots.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/queue/preview": {
      "get": {
        "tags": [
          "Queues"
        ],
        "operationId": "previewQueue",
        "summary": "Preview upcoming queue slots",
        "description": "Returns the next `count` instants the queue would hand out, in ascending order. Occupied slots are skipped, so the preview matches what the next posts would actually receive. Previewing reserves nothing.",
        "parameters": [
          {
            "name": "profileId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Profile that owns the queue."
          },
          {
            "name": "queueId",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Specific queue. Defaults to the profile's default queue."
          },
          {
            "name": "count",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            },
            "description": "How many upcoming slots to return."
          }
        ],
        "responses": {
          "200": {
            "description": "Upcoming slots returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QueuePreviewResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The queue is paused or has no slots.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/analytics": {
      "get": {
        "tags": [
          "Analytics"
        ],
        "operationId": "getAnalytics",
        "summary": "Get post analytics",
        "description": "> **Coming soon** — Analytics endpoints are being rolled out and may return empty data until the rollout completes.\n\nReturns one post when `postId` is supplied. Otherwise returns a paginated analytics list with aggregate overview metrics.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "postId",
            "in": "query",
            "description": "PostZen post id or platform post id. When supplied, the response is a single analytics object.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "platform",
            "in": "query",
            "description": "Platform slug, or `all` for every platform.",
            "schema": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/AnalyticsPlatform"
                },
                {
                  "type": "string",
                  "const": "all"
                }
              ],
              "default": "all"
            }
          },
          {
            "name": "profileId",
            "in": "query",
            "description": "Filter by PostZen profile id.",
            "schema": {
              "type": "string",
              "default": "all"
            }
          },
          {
            "name": "accountId",
            "in": "query",
            "description": "Filter by connected social account id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "source",
            "in": "query",
            "description": "Filter by posts published through PostZen or imported from a platform.",
            "schema": {
              "$ref": "#/components/schemas/AnalyticsSource"
            }
          },
          {
            "name": "fromDate",
            "in": "query",
            "description": "Inclusive range start. Accepts `YYYY-MM-DD` or an ISO 8601 datetime. Defaults to 90 days before `toDate`.",
            "schema": {
              "$ref": "#/components/schemas/AnalyticsDate"
            }
          },
          {
            "name": "toDate",
            "in": "query",
            "description": "Inclusive range end. Accepts `YYYY-MM-DD` or an ISO 8601 datetime. Defaults to the current time.",
            "schema": {
              "$ref": "#/components/schemas/AnalyticsDate"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Page size.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "1-based page number.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "sortBy",
            "in": "query",
            "description": "Field used to order list results.",
            "schema": {
              "type": "string",
              "enum": [
                "date",
                "engagement",
                "impressions",
                "reach",
                "likes",
                "comments",
                "shares",
                "saves",
                "clicks",
                "views"
              ],
              "default": "date"
            }
          },
          {
            "name": "order",
            "in": "query",
            "description": "Sort direction.",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "desc"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Single-post analytics or a paginated analytics list.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/AnalyticsPost"
                    },
                    {
                      "$ref": "#/components/schemas/AnalyticsListResponse"
                    }
                  ]
                }
              }
            }
          },
          "202": {
            "description": "The first analytics synchronization is pending; stored post data is returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalyticsPost"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "Analytics entitlement required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "424": {
            "description": "All platform analytics fetches failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/analytics/post-timeline": {
      "get": {
        "tags": [
          "Analytics"
        ],
        "operationId": "getPostTimeline",
        "summary": "Get a post analytics timeline",
        "description": "> **Coming soon** — Analytics endpoints are being rolled out and may return empty data until the rollout completes.\n\nReturns one daily row per platform for a PostZen post, imported external post, or platform post id.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "postId",
            "in": "query",
            "required": true,
            "description": "PostZen post id, external post id, analytics row id, or platform post id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "fromDate",
            "in": "query",
            "description": "Inclusive range start. Defaults to 90 days before `toDate`.",
            "schema": {
              "$ref": "#/components/schemas/AnalyticsDate"
            }
          },
          {
            "name": "toDate",
            "in": "query",
            "description": "Inclusive range end. Defaults to the current time.",
            "schema": {
              "$ref": "#/components/schemas/AnalyticsDate"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Post timeline returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PostTimelineResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "Analytics entitlement required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/analytics/daily-metrics": {
      "get": {
        "tags": [
          "Analytics"
        ],
        "operationId": "getDailyMetrics",
        "summary": "Get daily analytics metrics",
        "description": "> **Coming soon** — Analytics endpoints are being rolled out and may return empty data until the rollout completes.\n\nReturns daily aggregate metrics and a per-platform breakdown. Publish attribution assigns lifetime metrics to the post's publish date; received attribution assigns metric deltas to the day they were observed.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "platform",
            "in": "query",
            "description": "Filter by platform.",
            "schema": {
              "$ref": "#/components/schemas/AnalyticsPlatform"
            }
          },
          {
            "name": "profileId",
            "in": "query",
            "description": "Filter by PostZen profile id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "accountId",
            "in": "query",
            "description": "Filter by connected social account id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "fromDate",
            "in": "query",
            "description": "Inclusive range start. Defaults to 180 days before `toDate`.",
            "schema": {
              "$ref": "#/components/schemas/AnalyticsDate"
            }
          },
          {
            "name": "toDate",
            "in": "query",
            "description": "Inclusive range end. Defaults to the current time.",
            "schema": {
              "$ref": "#/components/schemas/AnalyticsDate"
            }
          },
          {
            "name": "source",
            "in": "query",
            "description": "Filter by PostZen-published or externally imported posts.",
            "schema": {
              "$ref": "#/components/schemas/AnalyticsSource"
            }
          },
          {
            "name": "attribution",
            "in": "query",
            "description": "Controls whether lifetime metrics are assigned to publish dates or daily deltas to receipt dates.",
            "schema": {
              "type": "string",
              "enum": [
                "publish",
                "received"
              ],
              "default": "publish"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Daily metrics returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DailyMetricsResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "Analytics entitlement required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/analytics/best-time": {
      "get": {
        "tags": [
          "Analytics"
        ],
        "operationId": "getBestTimeToPost",
        "summary": "Get the best times to post",
        "description": "> **Coming soon** — Analytics endpoints are being rolled out and may return empty data until the rollout completes.\n\nReturns historical engagement slots grouped by UTC day of week and hour, ordered by average engagement descending.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "platform",
            "in": "query",
            "description": "Filter by platform.",
            "schema": {
              "$ref": "#/components/schemas/AnalyticsPlatform"
            }
          },
          {
            "name": "profileId",
            "in": "query",
            "description": "Filter by PostZen profile id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "accountId",
            "in": "query",
            "description": "Filter by connected social account id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "source",
            "in": "query",
            "description": "Filter by PostZen-published or externally imported posts.",
            "schema": {
              "$ref": "#/components/schemas/AnalyticsSource"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Best posting slots returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BestTimeResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "Analytics entitlement required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/accounts/follower-stats": {
      "get": {
        "tags": [
          "Analytics"
        ],
        "operationId": "getFollowerStats",
        "summary": "Get follower statistics",
        "description": "> **Coming soon** — Analytics endpoints are being rolled out and may return empty data until the rollout completes.\n\nReturns follower history and growth for connected accounts at daily, weekly, or monthly granularity.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "accountIds",
            "in": "query",
            "description": "Comma-separated connected account ids. Omit to include every accessible account.",
            "schema": {
              "type": "string"
            },
            "example": "j97kp4mw2xnv6r1t8c3zq5bs9fh0yg4d,j86km3nv1xpw5r2t9c4zq7bs0fh6yg8e"
          },
          {
            "name": "profileId",
            "in": "query",
            "description": "Filter by PostZen profile id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "fromDate",
            "in": "query",
            "description": "Inclusive range start. Defaults to 30 days before `toDate`.",
            "schema": {
              "$ref": "#/components/schemas/AnalyticsDate"
            }
          },
          {
            "name": "toDate",
            "in": "query",
            "description": "Inclusive range end. Defaults to the current time.",
            "schema": {
              "$ref": "#/components/schemas/AnalyticsDate"
            }
          },
          {
            "name": "granularity",
            "in": "query",
            "description": "Follower history bucket size.",
            "schema": {
              "type": "string",
              "enum": [
                "daily",
                "weekly",
                "monthly"
              ],
              "default": "daily"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Follower statistics returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FollowerStatsResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "Analytics entitlement required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/posts/sync-external": {
      "post": {
        "tags": [
          "Analytics"
        ],
        "operationId": "syncExternalPosts",
        "summary": "Synchronize external posts",
        "description": "> **Coming soon** — Analytics endpoints are being rolled out and may return empty data until the rollout completes.\n\nFetches an account's latest posts published directly on its platform. Supplying `url` or `postId` searches for a specific post. Requests made within the per-account debounce window return cached results with `synced.skipped` set to true.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SyncExternalPostsRequest"
              },
              "example": {
                "accountId": "j97kp4mw2xnv6r1t8c3zq5bs9fh0yg4d",
                "url": "https://www.pinterest.com/pin/1234567890/"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "External posts synchronized or served from the debounce cache.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SyncExternalPostsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request, unsupported platform, or platform synchronization failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalyticsErrorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "Analytics entitlement required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/api-keys": {
      "get": {
        "tags": [
          "API Keys"
        ],
        "operationId": "listApiKeys",
        "summary": "List API keys",
        "description": "Returns the API keys for the authenticated account. The full key value is never returned; only a masked `keyPreview` is shown. Read-only and read-write API keys are accepted.",
        "responses": {
          "200": {
            "description": "API keys returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeysListResponse"
                },
                "example": {
                  "apiKeys": [
                    {
                      "id": "jk91m3xw7ktr5v2n8q4zs0b6hf1yg3pd",
                      "name": "Production key",
                      "keyPreview": "pzn_live_a1b2c3d4e...",
                      "createdAt": "2026-06-13T16:00:00.000Z",
                      "lastUsedAt": "2026-07-22T09:14:00.000Z",
                      "scope": "full",
                      "profileIds": [],
                      "permission": "read-write"
                    },
                    {
                      "id": "jk52t8nqm4wr9v3c7z1p6bs0dh5fg2yx",
                      "name": "Marketing read-only",
                      "keyPreview": "pzn_live_9f8e7d6c5...",
                      "createdAt": "2026-06-19T16:00:00.000Z",
                      "lastUsedAt": null,
                      "scope": "profiles",
                      "profileIds": [
                        {
                          "_id": "jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e",
                          "name": "Marketing Team",
                          "color": "#4caf50"
                        }
                      ],
                      "permission": "read"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": "Failed to list API keys"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "API Keys"
        ],
        "operationId": "createApiKey",
        "summary": "Create an API key",
        "description": "Creates an API key and returns the full key value exactly once. This endpoint requires a read-write API key with access to all profiles; profile-scoped keys receive a `403`. Store the returned `key` securely — it cannot be retrieved again after this response.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ApiKeyCreateRequest"
              },
              "example": {
                "name": "Production key",
                "scope": "full",
                "permission": "read-write"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "API key created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyCreateResponse"
                },
                "example": {
                  "message": "API key created successfully",
                  "apiKey": {
                    "id": "jk91m3xw7ktr5v2n8q4zs0b6hf1yg3pd",
                    "name": "Production key",
                    "keyPreview": "pzn_live_3f8c1b7d4...",
                    "createdAt": "2026-07-23T16:00:00.000Z",
                    "lastUsedAt": null,
                    "scope": "full",
                    "profileIds": [],
                    "permission": "read-write",
                    "key": "pzn_live_3f8c1b7d4e90a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8091a2b3c4d5e6f770"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/api-keys/{keyId}": {
      "parameters": [
        {
          "name": "keyId",
          "in": "path",
          "required": true,
          "description": "PostZen API key id.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "delete": {
        "tags": [
          "API Keys"
        ],
        "operationId": "deleteApiKey",
        "summary": "Delete an API key",
        "description": "Permanently revokes and deletes an API key. Requires a read-write key with full profile access. A key may delete itself.",
        "responses": {
          "200": {
            "description": "API key deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                },
                "example": {
                  "message": "API key deleted successfully"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "No API key exists with the given id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": "API key not found"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/webhooks": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "listWebhooks",
        "summary": "List webhooks",
        "description": "Returns webhook endpoints visible to the API key, newest first. Secret and custom header values are never returned.",
        "responses": {
          "200": {
            "description": "Webhooks returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhooksListResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "createWebhook",
        "summary": "Create a webhook",
        "description": "Creates an outbound webhook endpoint. A user may have up to 25 endpoints. This endpoint requires a read-write API key.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookCreateRequest"
              },
              "example": {
                "name": "Production events",
                "url": "https://example.com/webhooks/postzen",
                "events": [
                  "post.published",
                  "post.failed"
                ],
                "profileAccess": "all_profiles",
                "secret": "customer-provided-secret"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Webhook created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookWriteResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "description": "The endpoint limit has been reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/webhooks/{webhookId}": {
      "parameters": [
        {
          "name": "webhookId",
          "in": "path",
          "required": true,
          "description": "PostZen webhook endpoint id.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "getWebhook",
        "summary": "Get a webhook",
        "description": "Returns one webhook endpoint visible to the API key without secret or custom header values.",
        "responses": {
          "200": {
            "description": "Webhook returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "put": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "updateWebhook",
        "summary": "Update a webhook",
        "description": "Updates one or more endpoint settings. Omitted fields are unchanged. This endpoint requires a read-write API key.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookUpdateRequest"
              },
              "example": {
                "events": [
                  "post.published",
                  "post.partially_failed",
                  "post.failed"
                ],
                "customHeaders": []
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook updated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookWriteResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "delete": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "deleteWebhook",
        "summary": "Delete a webhook",
        "description": "Deletes an endpoint and stops new event fan-out. Retained delivery logs remain available until their retention period ends.",
        "responses": {
          "200": {
            "description": "Webhook deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/webhooks/{webhookId}/test": {
      "parameters": [
        {
          "name": "webhookId",
          "in": "path",
          "required": true,
          "description": "PostZen webhook endpoint id.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "testWebhook",
        "summary": "Test a webhook",
        "description": "Queues a `webhook.test` event through the normal delivery pipeline using the endpoint's current URL, secret, and custom headers. The endpoint must be active.",
        "responses": {
          "202": {
            "description": "Test delivery queued.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookTestResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The webhook endpoint is inactive.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/webhook-deliveries": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "listWebhookDeliveries",
        "summary": "List webhook deliveries",
        "description": "Returns retained delivery logs visible to the API key, newest first. Results are capped to the 1000 most recent matching deliveries.",
        "parameters": [
          {
            "name": "webhookId",
            "in": "query",
            "description": "Filter by webhook endpoint id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "event",
            "in": "query",
            "description": "Filter by webhook event type.",
            "schema": {
              "$ref": "#/components/schemas/WebhookEvent"
            }
          },
          {
            "name": "status",
            "in": "query",
            "description": "Filter by delivery status.",
            "schema": {
              "$ref": "#/components/schemas/WebhookDeliveryStatus"
            }
          },
          {
            "name": "dateFrom",
            "in": "query",
            "description": "Only include deliveries created at or after this ISO 8601 timestamp.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "dateTo",
            "in": "query",
            "description": "Only include deliveries created at or before this ISO 8601 timestamp. Must be later than `dateFrom`.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "1-based page number.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Page size.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Webhook deliveries returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookDeliveriesListResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/webhook-deliveries/{deliveryId}/redeliver": {
      "parameters": [
        {
          "name": "deliveryId",
          "in": "path",
          "required": true,
          "description": "PostZen webhook delivery id.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "redeliverWebhookDelivery",
        "summary": "Redeliver a webhook event",
        "description": "Queues a new six-attempt delivery run on the retained delivery record while preserving its event id and attempt history. The endpoint must still exist and be active, and the delivery must not already be in progress.",
        "responses": {
          "202": {
            "description": "Webhook redelivery queued.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookRedeliveryResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The endpoint is unavailable, the delivery is expired, or a delivery is already in progress.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "PostZen API key."
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Invalid request.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "error": "JSON body is required"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing or invalid API key.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "error": "Unauthorized"
            }
          }
        }
      },
      "PaymentRequired": {
        "description": "A plan limit, missing payment method, or paused account prevents the requested operation.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/PaymentRequiredError"
            },
            "example": {
              "error": "The Free plan includes 2 connected accounts. Upgrade your plan to connect more.",
              "code": "paymentRequired",
              "reason": "freeTierExceeded",
              "documentationUrl": "https://docs.postzen.dev/api-reference/connect/create-connect-url",
              "dashboardUrl": "https://app.postzen.dev/settings?tab=billing",
              "details": {
                "planId": "free",
                "includedAccounts": 2,
                "freeTierAccountLimit": 2,
                "currentAccountCount": 2,
                "hasPaymentMethod": false
              }
            }
          }
        }
      },
      "Forbidden": {
        "description": "The API key does not have sufficient permission or profile access.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "error": "API key requires read_write permission"
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "error": "Not found"
            }
          }
        }
      },
      "Conflict": {
        "description": "The resource is busy or conflicts with an in-progress operation.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "error": "Post publishing is in progress. Retry after it completes.",
              "type": "invalid_request_error",
              "code": "post_publish_in_progress",
              "param": null
            }
          }
        }
      },
      "InternalError": {
        "description": "Unexpected server error.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "error": "Failed to create post"
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Rate limit exceeded. Retry after the interval indicated by the `Retry-After` header.",
        "headers": {
          "Retry-After": {
            "description": "Number of seconds to wait before retrying.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/RateLimitError"
            },
            "example": {
              "error": "rate_limited",
              "retryAfter": 30
            }
          }
        }
      }
    },
    "schemas": {
      "BulkUploadResult": {
        "type": "object",
        "required": [
          "total",
          "valid",
          "invalid",
          "results",
          "warnings"
        ],
        "properties": {
          "total": {
            "type": "integer",
            "minimum": 0,
            "maximum": 500
          },
          "valid": {
            "type": "integer",
            "minimum": 0
          },
          "invalid": {
            "type": "integer",
            "minimum": 0
          },
          "results": {
            "type": "array",
            "items": {
              "oneOf": [
                {
                  "type": "object",
                  "required": [
                    "rowIndex",
                    "ok"
                  ],
                  "properties": {
                    "rowIndex": {
                      "type": "integer",
                      "minimum": 1
                    },
                    "ok": {
                      "type": "boolean",
                      "const": true
                    },
                    "createdPostId": {
                      "type": "string",
                      "description": "Present when the row was created by this request or replayed from a previous request with the same idempotency inputs. On dry-run, only replayed rows include this field."
                    }
                  }
                },
                {
                  "type": "object",
                  "required": [
                    "rowIndex",
                    "ok",
                    "errors"
                  ],
                  "properties": {
                    "rowIndex": {
                      "type": "integer",
                      "minimum": 1
                    },
                    "ok": {
                      "type": "boolean",
                      "const": false
                    },
                    "errors": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              ]
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "param": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "PinterestBoard": {
        "type": "object",
        "required": [
          "id",
          "name"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "privacy": {
            "type": "string",
            "enum": [
              "PUBLIC",
              "PROTECTED",
              "SECRET"
            ]
          },
          "pinCount": {
            "type": "integer",
            "minimum": 0
          }
        }
      },
      "PinterestBoardsResponse": {
        "type": "object",
        "required": [
          "boards"
        ],
        "properties": {
          "boards": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PinterestBoard"
            }
          }
        }
      },
      "PinterestBoardResponse": {
        "type": "object",
        "required": [
          "board"
        ],
        "properties": {
          "board": {
            "type": "object",
            "required": [
              "id",
              "name"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "description": {
                "type": "string"
              },
              "privacy": {
                "type": "string",
                "enum": [
                  "PUBLIC",
                  "PROTECTED",
                  "SECRET"
                ]
              }
            }
          }
        }
      },
      "PinterestCreateBoardRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1
          },
          "description": {
            "type": "string"
          },
          "privacy": {
            "type": "string",
            "enum": [
              "PUBLIC",
              "PROTECTED",
              "SECRET"
            ],
            "default": "PUBLIC"
          }
        }
      },
      "PinterestDefaultBoardRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "defaultBoardId"
        ],
        "properties": {
          "defaultBoardId": {
            "type": "string",
            "minLength": 1
          },
          "defaultBoardName": {
            "type": "string"
          }
        }
      },
      "PinterestDefaultBoardResponse": {
        "type": "object",
        "required": [
          "message",
          "account"
        ],
        "properties": {
          "message": {
            "type": "string",
            "const": "Default Pinterest board updated successfully"
          },
          "account": {
            "$ref": "#/components/schemas/Account"
          }
        }
      },
      "PinterestSelectBoardRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "state",
          "boardId"
        ],
        "properties": {
          "state": {
            "type": "string",
            "minLength": 1
          },
          "boardId": {
            "type": "string",
            "minLength": 1
          },
          "boardName": {
            "type": "string"
          },
          "redirectUrl": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "PinterestSelectBoardResponse": {
        "type": "object",
        "required": [
          "message",
          "account"
        ],
        "properties": {
          "message": {
            "type": "string",
            "const": "Pinterest connected successfully with default board"
          },
          "account": {
            "$ref": "#/components/schemas/Account"
          },
          "redirectUrl": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "AnalyticsErrorResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ErrorResponse"
          },
          {
            "type": "object",
            "properties": {
              "platform": {
                "$ref": "#/components/schemas/AnalyticsPlatform"
              },
              "platformError": {
                "type": "string",
                "description": "Original error detail returned by the social platform."
              }
            }
          }
        ]
      },
      "AnalyticsDate": {
        "oneOf": [
          {
            "type": "string",
            "format": "date"
          },
          {
            "type": "string",
            "format": "date-time"
          }
        ],
        "description": "A `YYYY-MM-DD` calendar date or ISO 8601 datetime."
      },
      "AnalyticsPlatform": {
        "type": "string",
        "enum": [
          "x",
          "instagram",
          "tiktok",
          "linkedin",
          "facebook",
          "youtube",
          "threads",
          "pinterest",
          "bluesky",
          "telegram"
        ]
      },
      "AnalyticsSource": {
        "type": "string",
        "enum": [
          "all",
          "postzen",
          "external"
        ],
        "default": "all"
      },
      "AnalyticsMetricTotals": {
        "type": "object",
        "required": [
          "impressions",
          "reach",
          "likes",
          "comments",
          "shares",
          "saves",
          "clicks",
          "views"
        ],
        "properties": {
          "impressions": {
            "type": "number"
          },
          "reach": {
            "type": "number"
          },
          "likes": {
            "type": "number"
          },
          "comments": {
            "type": "number"
          },
          "shares": {
            "type": "number"
          },
          "saves": {
            "type": "number"
          },
          "clicks": {
            "type": "number"
          },
          "views": {
            "type": "number"
          }
        }
      },
      "AnalyticsMetrics": {
        "allOf": [
          {
            "$ref": "#/components/schemas/AnalyticsMetricTotals"
          },
          {
            "type": "object",
            "required": [
              "engagementRate",
              "lastUpdated"
            ],
            "properties": {
              "engagementRate": {
                "type": "number",
                "description": "Interaction count divided by impressions, multiplied by 100 and rounded to two decimals."
              },
              "lastUpdated": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              }
            }
          }
        ]
      },
      "AnalyticsMediaItem": {
        "type": "object",
        "required": [
          "type",
          "url",
          "thumbnail"
        ],
        "properties": {
          "type": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "thumbnail": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "PlatformAnalytics": {
        "type": "object",
        "required": [
          "platform",
          "status",
          "platformPostId",
          "accountId",
          "accountUsername",
          "analytics",
          "syncStatus",
          "platformPostUrl",
          "errorMessage"
        ],
        "properties": {
          "platform": {
            "$ref": "#/components/schemas/AnalyticsPlatform"
          },
          "status": {
            "type": "string"
          },
          "platformPostId": {
            "type": "string"
          },
          "accountId": {
            "type": "string"
          },
          "accountUsername": {
            "type": "string"
          },
          "analytics": {
            "$ref": "#/components/schemas/AnalyticsMetrics"
          },
          "syncStatus": {
            "type": "string",
            "enum": [
              "synced",
              "pending",
              "failed"
            ]
          },
          "platformPostUrl": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "errorMessage": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "AnalyticsPost": {
        "type": "object",
        "required": [
          "postId",
          "postzenPostId",
          "status",
          "content",
          "scheduledFor",
          "publishedAt",
          "analytics",
          "platformAnalytics",
          "platform",
          "platformPostUrl",
          "isExternal",
          "syncStatus",
          "message",
          "thumbnailUrl",
          "mediaType",
          "mediaItems"
        ],
        "properties": {
          "postId": {
            "type": "string"
          },
          "postzenPostId": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string"
          },
          "content": {
            "type": "string"
          },
          "scheduledFor": {
            "type": "string",
            "format": "date-time"
          },
          "publishedAt": {
            "type": "string",
            "format": "date-time"
          },
          "analytics": {
            "$ref": "#/components/schemas/AnalyticsMetrics"
          },
          "platformAnalytics": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PlatformAnalytics"
            }
          },
          "platform": {
            "$ref": "#/components/schemas/AnalyticsPlatform"
          },
          "platformPostUrl": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "isExternal": {
            "type": "boolean"
          },
          "syncStatus": {
            "type": "string",
            "enum": [
              "synced",
              "pending",
              "failed"
            ]
          },
          "message": {
            "type": [
              "string",
              "null"
            ]
          },
          "thumbnailUrl": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "mediaType": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "image",
              "video",
              "text",
              "carousel",
              null
            ]
          },
          "mediaItems": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AnalyticsMediaItem"
            }
          }
        }
      },
      "AnalyticsOverview": {
        "type": "object",
        "required": [
          "totalPosts",
          "totals",
          "avgEngagementRate"
        ],
        "properties": {
          "totalPosts": {
            "type": "number"
          },
          "totals": {
            "$ref": "#/components/schemas/AnalyticsMetricTotals"
          },
          "avgEngagementRate": {
            "type": "number"
          }
        }
      },
      "AnalyticsListResponse": {
        "type": "object",
        "required": [
          "posts",
          "pagination",
          "overview",
          "truncated"
        ],
        "properties": {
          "posts": {
            "type": "array",
            "description": "One entry per PostZen post, not per platform target. A post published to several platforms appears once, with one `platformAnalytics` entry per platform and `analytics` summed across them; `platform` is that post's only platform when it has one. Posts imported from a platform always appear on their own. `pagination.total` and `overview.totalPosts` count these grouped entries.",
            "items": {
              "$ref": "#/components/schemas/AnalyticsPost"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/Pagination"
          },
          "overview": {
            "$ref": "#/components/schemas/AnalyticsOverview"
          },
          "truncated": {
            "type": "boolean",
            "description": "True when the requested window contained more posts than a single response can scan. `pagination.total` and `overview` then describe the most recent slice of the window rather than all of it; narrow `dateFrom`/`dateTo`, `accountId`, or `platform` to get exact totals."
          }
        }
      },
      "PostTimelineResponse": {
        "type": "object",
        "required": [
          "postId",
          "timeline"
        ],
        "properties": {
          "postId": {
            "type": "string"
          },
          "timeline": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "date",
                "platform",
                "platformPostId",
                "impressions",
                "reach",
                "likes",
                "comments",
                "shares",
                "saves",
                "clicks",
                "views"
              ],
              "properties": {
                "date": {
                  "type": "string",
                  "format": "date"
                },
                "platform": {
                  "$ref": "#/components/schemas/AnalyticsPlatform"
                },
                "platformPostId": {
                  "type": "string"
                },
                "impressions": {
                  "type": "number"
                },
                "reach": {
                  "type": "number"
                },
                "likes": {
                  "type": "number"
                },
                "comments": {
                  "type": "number"
                },
                "shares": {
                  "type": "number"
                },
                "saves": {
                  "type": "number"
                },
                "clicks": {
                  "type": "number"
                },
                "views": {
                  "type": "number"
                }
              }
            }
          }
        }
      },
      "DailyMetricsResponse": {
        "type": "object",
        "required": [
          "dailyData",
          "platformBreakdown"
        ],
        "properties": {
          "dailyData": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "date",
                "postCount",
                "platforms",
                "metrics"
              ],
              "properties": {
                "date": {
                  "type": "string",
                  "format": "date"
                },
                "postCount": {
                  "type": "number"
                },
                "platforms": {
                  "type": "object",
                  "additionalProperties": {
                    "type": "number"
                  }
                },
                "metrics": {
                  "$ref": "#/components/schemas/AnalyticsMetricTotals"
                }
              }
            }
          },
          "platformBreakdown": {
            "type": "array",
            "items": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/AnalyticsMetricTotals"
                },
                {
                  "type": "object",
                  "required": [
                    "platform",
                    "postCount"
                  ],
                  "properties": {
                    "platform": {
                      "$ref": "#/components/schemas/AnalyticsPlatform"
                    },
                    "postCount": {
                      "type": "number"
                    }
                  }
                }
              ]
            }
          }
        }
      },
      "BestTimeResponse": {
        "type": "object",
        "required": [
          "slots"
        ],
        "properties": {
          "slots": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "day_of_week",
                "hour",
                "avg_engagement",
                "post_count"
              ],
              "properties": {
                "day_of_week": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 6,
                  "description": "UTC day of week, where 0 is Sunday."
                },
                "hour": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 23
                },
                "avg_engagement": {
                  "type": "number"
                },
                "post_count": {
                  "type": "number"
                }
              }
            }
          }
        }
      },
      "FollowerStatsResponse": {
        "type": "object",
        "required": [
          "accounts",
          "stats",
          "dateRange",
          "granularity"
        ],
        "properties": {
          "accounts": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "_id",
                "platform",
                "username",
                "currentFollowers",
                "growth",
                "growthPercentage",
                "dataPoints"
              ],
              "properties": {
                "_id": {
                  "type": "string"
                },
                "platform": {
                  "$ref": "#/components/schemas/AnalyticsPlatform"
                },
                "username": {
                  "type": "string"
                },
                "currentFollowers": {
                  "type": "number"
                },
                "growth": {
                  "type": "number"
                },
                "growthPercentage": {
                  "type": "number"
                },
                "dataPoints": {
                  "type": "number"
                }
              }
            }
          },
          "stats": {
            "type": "object",
            "additionalProperties": {
              "type": "array",
              "items": {
                "type": "object",
                "required": [
                  "date",
                  "followers"
                ],
                "properties": {
                  "date": {
                    "type": "string",
                    "format": "date"
                  },
                  "followers": {
                    "type": "number"
                  }
                }
              }
            }
          },
          "dateRange": {
            "type": "object",
            "required": [
              "from",
              "to"
            ],
            "properties": {
              "from": {
                "type": "string",
                "format": "date-time"
              },
              "to": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "granularity": {
            "type": "string",
            "enum": [
              "daily",
              "weekly",
              "monthly"
            ]
          }
        }
      },
      "SyncExternalPostsRequest": {
        "type": "object",
        "required": [
          "accountId"
        ],
        "properties": {
          "accountId": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Optional platform post URL to locate."
          },
          "postId": {
            "type": "string",
            "description": "Optional platform post id to locate."
          }
        }
      },
      "ExternalSyncedPost": {
        "type": "object",
        "required": [
          "platform",
          "platformPostId",
          "platformPostUrl",
          "content",
          "publishedAt",
          "mediaType",
          "mediaUrl",
          "thumbnailUrl",
          "mediaItems",
          "analytics"
        ],
        "properties": {
          "platform": {
            "$ref": "#/components/schemas/AnalyticsPlatform"
          },
          "platformPostId": {
            "type": "string"
          },
          "platformPostUrl": {
            "type": "string",
            "format": "uri"
          },
          "content": {
            "type": "string"
          },
          "publishedAt": {
            "type": "string",
            "format": "date-time"
          },
          "mediaType": {
            "type": "string"
          },
          "mediaUrl": {
            "type": "string",
            "format": "uri"
          },
          "thumbnailUrl": {
            "type": "string",
            "format": "uri"
          },
          "mediaItems": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AnalyticsMediaItem"
            }
          },
          "analytics": {
            "$ref": "#/components/schemas/AnalyticsMetrics"
          }
        }
      },
      "SyncExternalPostsResponse": {
        "type": "object",
        "required": [
          "synced",
          "found",
          "post",
          "posts"
        ],
        "properties": {
          "synced": {
            "type": "object",
            "required": [
              "postsFound",
              "postsSynced",
              "skipped"
            ],
            "properties": {
              "postsFound": {
                "type": "number"
              },
              "postsSynced": {
                "type": "number"
              },
              "skipped": {
                "type": "boolean"
              }
            }
          },
          "found": {
            "type": "boolean"
          },
          "post": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ExternalSyncedPost"
              },
              {
                "type": "null"
              }
            ]
          },
          "posts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ExternalSyncedPost"
            }
          }
        }
      },
      "ConnectCompleteErrorResponse": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Human-readable message, or a camelCase error code (for example `oauthCallbackFailed`) when the platform callback fails."
          },
          "errorDescription": {
            "type": "string",
            "description": "Additional detail reported by the platform, when available."
          }
        }
      },
      "PaymentRequiredError": {
        "type": "object",
        "required": [
          "error",
          "code",
          "reason",
          "documentationUrl",
          "dashboardUrl"
        ],
        "properties": {
          "error": {
            "type": "string"
          },
          "code": {
            "type": "string",
            "const": "paymentRequired"
          },
          "reason": {
            "type": "string",
            "enum": [
              "freeTierExceeded",
              "xRequiresPaymentMethod",
              "accountPaused",
              "freePostLimitExceeded"
            ]
          },
          "documentationUrl": {
            "type": "string",
            "format": "uri"
          },
          "dashboardUrl": {
            "type": "string",
            "format": "uri"
          },
          "details": {
            "type": "object",
            "description": "Reason-specific context. Account-connection limits use the plan and account fields; `freePostLimitExceeded` uses `postsUsed` and `postLimit`.",
            "properties": {
              "planId": {
                "type": "string",
                "enum": [
                  "free",
                  "starter",
                  "pro",
                  "scale"
                ]
              },
              "includedAccounts": {
                "type": "number"
              },
              "freeTierAccountLimit": {
                "type": "number",
                "description": "Same value as `includedAccounts`; retained for compatibility."
              },
              "currentAccountCount": {
                "type": "number"
              },
              "hasPaymentMethod": {
                "type": "boolean"
              },
              "postsUsed": {
                "type": "number",
                "description": "Platform-posts already counted in the current UTC month."
              },
              "postLimit": {
                "type": "number",
                "description": "The plan's monthly platform-post allowance."
              }
            }
          }
        }
      },
      "MessageResponse": {
        "type": "object",
        "required": [
          "message"
        ],
        "properties": {
          "message": {
            "type": "string"
          }
        }
      },
      "PublicPlatformInput": {
        "type": "string",
        "enum": [
          "twitter",
          "x",
          "instagram",
          "tiktok",
          "linkedin",
          "facebook",
          "youtube",
          "threads",
          "pinterest",
          "bluesky",
          "telegram"
        ]
      },
      "PublicPlatformOutput": {
        "type": "string",
        "enum": [
          "twitter",
          "instagram",
          "tiktok",
          "linkedin",
          "facebook",
          "youtube",
          "threads",
          "pinterest",
          "bluesky",
          "telegram"
        ]
      },
      "Profile": {
        "type": "object",
        "required": [
          "_id",
          "userId",
          "name",
          "color",
          "isDefault",
          "createdAt"
        ],
        "properties": {
          "_id": {
            "type": "string",
            "description": "PostZen profile id."
          },
          "userId": {
            "type": "string",
            "description": "Owner user id."
          },
          "name": {
            "type": "string",
            "maxLength": 80
          },
          "description": {
            "type": "string",
            "maxLength": 240
          },
          "color": {
            "type": "string",
            "pattern": "^#[0-9a-fA-F]{6}$",
            "description": "Hex color in `#rrggbb` format."
          },
          "isDefault": {
            "type": "boolean"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ProfilesListResponse": {
        "type": "object",
        "required": [
          "profiles"
        ],
        "properties": {
          "profiles": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Profile"
            }
          }
        }
      },
      "ProfileResponse": {
        "type": "object",
        "required": [
          "profile"
        ],
        "properties": {
          "profile": {
            "$ref": "#/components/schemas/Profile"
          }
        }
      },
      "ProfileWriteResponse": {
        "type": "object",
        "required": [
          "message",
          "profile"
        ],
        "properties": {
          "message": {
            "type": "string"
          },
          "profile": {
            "$ref": "#/components/schemas/Profile"
          }
        }
      },
      "ProfileCreateRequest": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80
          },
          "description": {
            "type": "string",
            "maxLength": 240
          },
          "color": {
            "type": "string",
            "pattern": "^#[0-9a-fA-F]{6}$",
            "default": "#ffeda0"
          }
        }
      },
      "ProfileUpdateRequest": {
        "type": "object",
        "description": "Include at least one field.",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 240,
            "description": "Pass an empty string or `null` to clear the description."
          },
          "color": {
            "type": "string",
            "pattern": "^#[0-9a-fA-F]{6}$"
          },
          "isDefault": {
            "type": "boolean",
            "description": "Set to `true` to make this the default profile."
          }
        }
      },
      "AccountProfileSummary": {
        "type": "object",
        "required": [
          "_id",
          "name",
          "slug",
          "color"
        ],
        "properties": {
          "_id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "color": {
            "type": "string"
          }
        }
      },
      "Account": {
        "type": "object",
        "required": [
          "_id",
          "platform",
          "providerAccountId",
          "profileId",
          "username",
          "displayName",
          "status",
          "isActive",
          "connectedAt"
        ],
        "properties": {
          "_id": {
            "type": "string"
          },
          "platform": {
            "$ref": "#/components/schemas/PublicPlatformOutput"
          },
          "providerAccountId": {
            "type": "string"
          },
          "profileId": {
            "$ref": "#/components/schemas/AccountProfileSummary"
          },
          "username": {
            "type": "string"
          },
          "displayName": {
            "type": "string"
          },
          "profileUrl": {
            "type": "string",
            "format": "uri"
          },
          "avatarUrl": {
            "type": "string",
            "format": "uri"
          },
          "status": {
            "type": "string",
            "enum": [
              "connected",
              "needsReauth",
              "disconnected",
              "disabled"
            ]
          },
          "isActive": {
            "type": "boolean"
          },
          "connectedAt": {
            "type": "string",
            "format": "date-time"
          },
          "lastSyncedAt": {
            "type": "string",
            "format": "date-time"
          },
          "defaultBoardId": {
            "type": "string",
            "description": "Default Pinterest board id. Present only when one has been selected for a Pinterest account."
          },
          "defaultBoardName": {
            "type": "string",
            "description": "Default Pinterest board name. Present when Pinterest returned a name for the selected default board."
          }
        }
      },
      "Pagination": {
        "type": "object",
        "required": [
          "page",
          "limit",
          "total",
          "totalPages"
        ],
        "properties": {
          "page": {
            "type": "integer",
            "minimum": 1
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100
          },
          "total": {
            "type": "integer",
            "minimum": 0
          },
          "totalPages": {
            "type": "integer",
            "minimum": 0
          }
        }
      },
      "AccountsListResponse": {
        "type": "object",
        "required": [
          "accounts"
        ],
        "properties": {
          "accounts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Account"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/Pagination"
          }
        }
      },
      "PostsListResponse": {
        "type": "object",
        "required": [
          "posts",
          "pagination"
        ],
        "properties": {
          "posts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiPost"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/Pagination"
          }
        }
      },
      "LinkedInComment": {
        "type": "object",
        "description": "A comment LinkedIn holds on an organization post published through PostZen.",
        "required": [
          "id",
          "text",
          "authorUrn",
          "createdAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "LinkedIn's numeric comment id, unique within its thread."
          },
          "commentUrn": {
            "type": "string",
            "description": "Composite comment URN — `urn:li:comment:(<threadUrn>,<id>)` — which every comment-scoped LinkedIn endpoint takes as its path parameter. Absent when LinkedIn omits it."
          },
          "text": {
            "type": "string",
            "description": "Comment body as plain text."
          },
          "authorUrn": {
            "type": "string",
            "description": "URN of the member or organization that wrote the comment."
          },
          "authorName": {
            "type": "string",
            "description": "Display name of the author, when LinkedIn returns one."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the comment was created."
          },
          "likeCount": {
            "type": "integer",
            "description": "Likes on the comment, when LinkedIn returns a count."
          },
          "replyCount": {
            "type": "integer",
            "description": "Replies to the comment, when LinkedIn returns a count."
          }
        }
      },
      "LinkedInCommentsResponse": {
        "type": "object",
        "required": [
          "comments"
        ],
        "properties": {
          "comments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LinkedInComment"
            }
          },
          "nextCursor": {
            "type": "string",
            "description": "Present when more comments are available. Pass it back as `cursor` to fetch the next page."
          }
        }
      },
      "LinkedInReaction": {
        "type": "object",
        "description": "A single reaction LinkedIn holds on an organization post published through PostZen.",
        "required": [
          "reactionType",
          "actorUrn"
        ],
        "properties": {
          "reactionType": {
            "type": "string",
            "description": "LinkedIn reaction type, such as `LIKE`, `PRAISE`, `EMPATHY`, `INTEREST`, `APPRECIATION`, or `ENTERTAINMENT`. Treat this as an open set — LinkedIn adds reaction types over time."
          },
          "actorUrn": {
            "type": "string",
            "description": "URN of the member or organization that reacted."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the reaction was recorded, when LinkedIn returns a timestamp."
          }
        }
      },
      "LinkedInReactionsResponse": {
        "type": "object",
        "required": [
          "reactions",
          "totalsByType"
        ],
        "properties": {
          "reactions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LinkedInReaction"
            }
          },
          "totalsByType": {
            "type": "object",
            "description": "Reaction count keyed by reaction type, tallied from the reactions in this response. Sum the pages yourself for a whole-post total.",
            "additionalProperties": {
              "type": "integer"
            }
          },
          "nextCursor": {
            "type": "string",
            "description": "Present when more reactions are available. Pass it back as `cursor` to fetch the next page."
          }
        }
      },
      "LinkedInSocialReadErrorResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ErrorResponse"
          },
          {
            "type": "object",
            "required": [
              "code",
              "platform"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "notFound",
                  "forbidden",
                  "postNotPublished",
                  "personalPostUnsupported",
                  "orgScopesDisabled",
                  "platformCapabilityMissing",
                  "notConnected",
                  "rateLimited",
                  "requestFailed"
                ],
                "description": "Machine-readable failure reason."
              },
              "platform": {
                "type": "string",
                "const": "linkedin"
              }
            }
          }
        ]
      },
      "RateLimitError": {
        "type": "object",
        "required": [
          "error",
          "retryAfter"
        ],
        "properties": {
          "error": {
            "type": "string",
            "const": "rate_limited"
          },
          "retryAfter": {
            "type": "integer",
            "minimum": 1,
            "description": "Number of seconds to wait before retrying."
          }
        }
      },
      "InboxComment": {
        "type": "object",
        "description": "A comment or reply normalized to one shape across Instagram, Facebook, and Threads. A field the platform does not report is absent rather than zero.",
        "required": [
          "id",
          "message",
          "createdTime",
          "from",
          "platform",
          "replies",
          "repliesHasMore",
          "canReply",
          "canDelete",
          "canHide",
          "isHidden",
          "parentId"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Platform comment id. Pass it as `commentId` to the reply, delete, and hide endpoints."
          },
          "message": {
            "type": "string",
            "description": "Comment body as plain text."
          },
          "createdTime": {
            "type": "string",
            "format": "date-time",
            "description": "When the platform recorded the comment."
          },
          "from": {
            "type": "object",
            "description": "Author of the comment.",
            "required": [
              "isOwner"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "username": {
                "type": "string"
              },
              "picture": {
                "type": "string",
                "format": "uri"
              },
              "isOwner": {
                "type": "boolean",
                "description": "True when the connected account wrote the comment."
              }
            }
          },
          "likeCount": {
            "type": "integer",
            "description": "Likes on the comment, when the platform reports a count. Threads never does."
          },
          "replyCount": {
            "type": "integer",
            "description": "Replies to the comment, when the platform reports a count."
          },
          "platform": {
            "type": "string",
            "enum": [
              "instagram",
              "facebook",
              "threads"
            ],
            "description": "Platform the comment lives on."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Permalink to the comment, when the platform returns one."
          },
          "replies": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InboxComment"
            },
            "description": "First page of nested replies, when the listing returned them cheaply. Instagram threads are two levels deep, so a reply's own `replies` is always empty."
          },
          "repliesHasMore": {
            "type": "boolean",
            "description": "True when the comment has replies beyond the ones in `replies`. Fetch them by passing this comment's id as `commentId`."
          },
          "canReply": {
            "type": "boolean",
            "description": "True when the connected account's granted scopes and the platform's own rules allow replying to this comment."
          },
          "canDelete": {
            "type": "boolean",
            "description": "True when this comment can be deleted. Facebook reports it per comment; Threads reports it only for replies the connected account wrote."
          },
          "canHide": {
            "type": "boolean",
            "description": "True when this comment can be hidden. Facebook reports it per comment; on Threads only top-level replies on the account's own posts qualify."
          },
          "isHidden": {
            "type": "boolean",
            "description": "True when the comment is currently hidden. Threads' `HIDDEN` and `COVERED` states both map to true."
          },
          "parentId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Comment this one replies to, or `null` when it is a top-level comment on the post."
          }
        }
      },
      "InboxCommentsResponse": {
        "type": "object",
        "required": [
          "status",
          "comments",
          "pagination",
          "meta"
        ],
        "properties": {
          "status": {
            "type": "string",
            "const": "success"
          },
          "comments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InboxComment"
            }
          },
          "pagination": {
            "type": "object",
            "required": [
              "hasMore"
            ],
            "properties": {
              "hasMore": {
                "type": "boolean",
                "description": "True when the platform has another page of comments."
              },
              "cursor": {
                "type": "string",
                "description": "Opaque cursor for the next page. Present only when `hasMore` is true."
              }
            }
          },
          "meta": {
            "type": "object",
            "required": [
              "platform",
              "postId",
              "accountId",
              "lastUpdated"
            ],
            "properties": {
              "platform": {
                "type": "string",
                "enum": [
                  "instagram",
                  "facebook",
                  "threads"
                ],
                "description": "Platform the comment lives on."
              },
              "postId": {
                "type": "string",
                "description": "Platform post id the comments were read from. When a PostZen post id was supplied it is the resolved platform id, not the id you sent."
              },
              "accountId": {
                "type": "string",
                "description": "PostZen account id used for the read."
              },
              "lastUpdated": {
                "type": "string",
                "format": "date-time",
                "description": "When PostZen fetched this page from the platform."
              }
            }
          }
        }
      },
      "InboxReplyRequest": {
        "type": "object",
        "required": [
          "accountId",
          "message"
        ],
        "properties": {
          "accountId": {
            "type": "string",
            "description": "PostZen account id of the connected Instagram, Facebook, or Threads account that owns the post."
          },
          "message": {
            "type": "string",
            "description": "Comment text to publish."
          },
          "commentId": {
            "type": "string",
            "description": "Reply to this comment instead of commenting on the post. On Instagram a reply to a reply is redirected to its top-level parent."
          },
          "attachmentUrl": {
            "type": "string",
            "format": "uri",
            "description": "Facebook only. Publicly reachable image URL to attach to the comment. Sending it for any other platform returns `400 attachmentUnsupported`."
          }
        }
      },
      "InboxReplyResponse": {
        "type": "object",
        "required": [
          "success",
          "data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "data": {
            "type": "object",
            "required": [
              "commentId",
              "isReply"
            ],
            "properties": {
              "commentId": {
                "type": "string",
                "description": "Platform id of the comment that was created."
              },
              "isReply": {
                "type": "boolean",
                "description": "True when the comment was published as a reply to another comment rather than directly on the post."
              }
            }
          }
        }
      },
      "InboxHideRequest": {
        "type": "object",
        "required": [
          "accountId"
        ],
        "properties": {
          "accountId": {
            "type": "string",
            "description": "PostZen account id of the connected Instagram, Facebook, or Threads account that owns the post."
          }
        }
      },
      "InboxHideResponse": {
        "type": "object",
        "required": [
          "status",
          "commentId",
          "hidden",
          "platform"
        ],
        "properties": {
          "status": {
            "type": "string",
            "const": "success"
          },
          "commentId": {
            "type": "string",
            "description": "Platform id of the comment."
          },
          "hidden": {
            "type": "boolean",
            "description": "The comment's hidden state after the call."
          },
          "platform": {
            "type": "string",
            "enum": [
              "instagram",
              "facebook",
              "threads"
            ],
            "description": "Platform the comment lives on."
          }
        }
      },
      "InboxActionResponse": {
        "type": "object",
        "description": "Acknowledgement returned by an inbox write that has nothing to return.",
        "required": [
          "success",
          "data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "data": {
            "$ref": "#/components/schemas/MessageResponse"
          }
        }
      },
      "InboxErrorResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ErrorResponse"
          },
          {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "description": "Machine-readable failure reason. PostZen's own reasons are `accountNotFound`, `forbidden`, `platformUnsupported`, `platformCapabilityMissing`, `connectionDead`, `postNotFound`, `postNotPublished`, `attachmentUnsupported`, and `publishPending` (Threads accepted the reply container but had not published it yet — the reply may still appear, so check the thread before retrying). Failures raised by the platform carry the platform's own error code instead, which for Meta is a numeric string such as `100` or `190`. Absent on plain parameter-validation errors and on PostZen's own rate-limit response."
              },
              "platform": {
                "type": "string",
                "description": "Platform the failure relates to, when PostZen could determine one. `platformUnsupported` reports the account's actual platform, which is why this is not limited to the three inbox platforms."
              },
              "platformError": {
                "type": "string",
                "description": "The platform's own error message, present when the failure came from the platform rather than from PostZen."
              }
            }
          }
        ]
      },
      "InboxConversation": {
        "type": "object",
        "required": [
          "id",
          "platform",
          "accountId",
          "accountUsername",
          "updatedTime",
          "status",
          "unreadCount"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "The platform's own conversation (thread) id. This is the `conversationId` every other conversation endpoint takes, not a PostZen document id."
          },
          "platform": {
            "type": "string",
            "enum": [
              "instagram"
            ],
            "description": "Platform the thread lives on."
          },
          "accountId": {
            "type": "string",
            "description": "PostZen account id the thread belongs to."
          },
          "accountUsername": {
            "type": "string",
            "description": "Username of that connected account, so a multi-account list needs no second lookup."
          },
          "participantId": {
            "type": "string",
            "description": "The other party's platform id (the IGSID on Instagram). Absent when the platform did not name the participant."
          },
          "participantName": {
            "type": "string"
          },
          "participantUsername": {
            "type": "string"
          },
          "participantPicture": {
            "type": "string",
            "format": "uri"
          },
          "participants": {
            "type": "array",
            "description": "Every party on the thread, including the connected account itself. Present when the platform returned the participant edge.",
            "items": {
              "type": "object",
              "required": [
                "id"
              ],
              "properties": {
                "id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "username": {
                  "type": "string"
                }
              }
            }
          },
          "lastMessage": {
            "type": "string",
            "description": "Text of the most recent message PostZen has synced, when the platform reported one cheaply."
          },
          "lastMessageAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedTime": {
            "type": "string",
            "format": "date-time",
            "description": "The platform's own last-activity time for the thread. This is the list sort key."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "archived"
            ],
            "description": "PostZen-local state. Meta has no archive API, so archiving affects PostZen only and is never overwritten by a sync."
          },
          "unreadCount": {
            "type": "integer",
            "description": "Incoming messages newer than the local read watermark. Marking the thread read sets it to 0 and a later sync cannot resurrect a stale count."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Deep link into the platform's own inbox, when the platform reports one. Instagram has no equivalent, so it is currently always absent."
          }
        }
      },
      "InboxDmPagination": {
        "type": "object",
        "required": [
          "hasMore",
          "nextCursor"
        ],
        "properties": {
          "hasMore": {
            "type": "boolean"
          },
          "nextCursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass back as `cursor` for the next page. `null` when the last page has been returned."
          }
        }
      },
      "InboxDmFailedAccount": {
        "type": "object",
        "required": [
          "accountId",
          "accountUsername",
          "platform",
          "error",
          "code"
        ],
        "properties": {
          "accountId": {
            "type": "string"
          },
          "accountUsername": {
            "type": "string"
          },
          "platform": {
            "type": "string",
            "enum": [
              "instagram"
            ]
          },
          "error": {
            "type": "string",
            "description": "Why the refresh failed, in PostZen or the platform’s own words."
          },
          "code": {
            "type": "string"
          },
          "retryAfter": {
            "type": "integer",
            "description": "Seconds the platform asked PostZen to wait, when it said."
          }
        }
      },
      "InboxDmMeta": {
        "type": "object",
        "required": [
          "accountsQueried",
          "accountsFailed",
          "failedAccounts",
          "lastUpdated"
        ],
        "description": "How the read-through refresh went. A failure here is a failure to REFRESH, not to answer: the stored threads are still returned alongside it.",
        "properties": {
          "accountsQueried": {
            "type": "integer"
          },
          "accountsFailed": {
            "type": "integer"
          },
          "failedAccounts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InboxDmFailedAccount"
            }
          },
          "lastUpdated": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "InboxConversationsResponse": {
        "type": "object",
        "required": [
          "data",
          "pagination",
          "meta"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InboxConversation"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/InboxDmPagination"
          },
          "meta": {
            "$ref": "#/components/schemas/InboxDmMeta"
          }
        }
      },
      "InboxConversationResponse": {
        "type": "object",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "$ref": "#/components/schemas/InboxConversation"
          }
        }
      },
      "InboxConversationSearchMatch": {
        "type": "object",
        "required": [
          "id",
          "direction",
          "timestamp"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Platform message id of the matching message."
          },
          "text": {
            "type": "string"
          },
          "direction": {
            "type": "string",
            "enum": [
              "incoming",
              "outgoing"
            ]
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "InboxConversationSearchHit": {
        "type": "object",
        "required": [
          "conversation",
          "matchCount",
          "matches"
        ],
        "properties": {
          "conversation": {
            "type": "object",
            "required": [
              "id",
              "platform",
              "accountId",
              "status"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "platform": {
                "type": "string",
                "enum": [
                  "instagram"
                ]
              },
              "accountId": {
                "type": "string"
              },
              "participantName": {
                "type": "string"
              },
              "participantUsername": {
                "type": "string"
              },
              "participantPicture": {
                "type": "string",
                "format": "uri"
              },
              "status": {
                "type": "string",
                "enum": [
                  "active",
                  "archived"
                ]
              },
              "lastMessage": {
                "type": "string"
              },
              "lastMessageAt": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "matchCount": {
            "type": "integer",
            "description": "Matching messages in the thread. `matches` carries at most the first five of them; a thread matched only on the participant’s name reports 0."
          },
          "matches": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InboxConversationSearchMatch"
            }
          }
        }
      },
      "InboxConversationSearchResponse": {
        "type": "object",
        "required": [
          "data",
          "pagination",
          "meta"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InboxConversationSearchHit"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/InboxDmPagination"
          },
          "meta": {
            "$ref": "#/components/schemas/InboxDmMeta"
          }
        }
      },
      "InboxConversationUpdateRequest": {
        "type": "object",
        "required": [
          "accountId",
          "status"
        ],
        "properties": {
          "accountId": {
            "type": "string",
            "description": "PostZen account id that owns the conversation."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "archived"
            ]
          }
        }
      },
      "InboxConversationUpdateResponse": {
        "type": "object",
        "required": [
          "success",
          "data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "data": {
            "type": "object",
            "required": [
              "id",
              "accountId",
              "status",
              "platform",
              "updatedAt"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "accountId": {
                "type": "string"
              },
              "status": {
                "type": "string",
                "enum": [
                  "active",
                  "archived"
                ]
              },
              "platform": {
                "type": "string",
                "enum": [
                  "instagram"
                ]
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        }
      },
      "InboxConversationReadRequest": {
        "type": "object",
        "required": [
          "accountId"
        ],
        "properties": {
          "accountId": {
            "type": "string",
            "description": "PostZen account id that owns the conversation."
          }
        }
      },
      "InboxConversationReadResponse": {
        "type": "object",
        "required": [
          "success",
          "markedCount"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "markedCount": {
            "type": "integer",
            "description": "Incoming messages that crossed from unread to read on this call. Marking an already-read thread returns 0 rather than failing."
          }
        }
      },
      "InboxMessageAttachment": {
        "type": "object",
        "required": [
          "type",
          "url"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "image",
              "video",
              "audio",
              "file"
            ]
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Platform-hosted URL. Meta expires these, so treat them as short-lived."
          },
          "previewUrl": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "InboxMessage": {
        "type": "object",
        "required": [
          "id",
          "conversationId",
          "accountId",
          "platform",
          "direction",
          "createdAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Platform message id."
          },
          "conversationId": {
            "type": "string"
          },
          "accountId": {
            "type": "string"
          },
          "platform": {
            "type": "string",
            "enum": [
              "instagram"
            ]
          },
          "message": {
            "type": "string",
            "description": "Message text. Absent on an attachment-only message, and on older Instagram messages the platform no longer returns text for."
          },
          "senderId": {
            "type": "string"
          },
          "senderName": {
            "type": "string"
          },
          "direction": {
            "type": "string",
            "enum": [
              "incoming",
              "outgoing"
            ],
            "description": "Resolved against the connected account’s own identity: `outgoing` is a message the account sent."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "attachments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InboxMessageAttachment"
            }
          },
          "storyReply": {
            "type": "boolean",
            "description": "Instagram only, and present only when the platform said so."
          },
          "isStoryMention": {
            "type": "boolean",
            "description": "Instagram only, and present only when the platform said so."
          }
        }
      },
      "InboxMessagesResponse": {
        "type": "object",
        "required": [
          "status",
          "pagination",
          "sortOrderApplied",
          "messages",
          "lastUpdated"
        ],
        "properties": {
          "status": {
            "type": "string",
            "const": "ok"
          },
          "pagination": {
            "$ref": "#/components/schemas/InboxDmPagination"
          },
          "sortOrderApplied": {
            "type": "string",
            "enum": [
              "asc",
              "desc"
            ]
          },
          "messages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InboxMessage"
            }
          },
          "lastUpdated": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "InboxMessageSendRequest": {
        "type": "object",
        "required": [
          "accountId"
        ],
        "description": "Exactly one of `message` or `attachmentUrl` is required. Meta’s Send API carries one payload per call, so the two cannot be combined — send two messages instead.",
        "properties": {
          "accountId": {
            "type": "string",
            "description": "PostZen account id that owns the conversation."
          },
          "message": {
            "type": "string",
            "description": "Message text. Cannot be combined with `attachmentUrl`: Meta accepts one payload per call, so a request carrying both is rejected with a 400 rather than silently sending half of it."
          },
          "attachmentUrl": {
            "type": "string",
            "format": "uri",
            "description": "Publicly fetchable http(s) URL. Meta downloads it itself, so it must be reachable without credentials — `POST /v1/media/upload-direct` returns a URL that qualifies."
          },
          "attachmentType": {
            "type": "string",
            "enum": [
              "image",
              "video",
              "audio",
              "file"
            ],
            "description": "Required whenever `attachmentUrl` is set."
          }
        }
      },
      "InboxMessageSendResponse": {
        "type": "object",
        "required": [
          "success",
          "data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "data": {
            "type": "object",
            "required": [
              "messageId",
              "conversationId",
              "sentAt"
            ],
            "properties": {
              "messageId": {
                "type": "string"
              },
              "conversationId": {
                "type": "string"
              },
              "sentAt": {
                "type": "string",
                "format": "date-time"
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      },
      "InboxDmErrorResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ErrorResponse"
          },
          {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "description": "Machine-readable failure reason. PostZen's own reasons are `accountNotFound`, `conversationNotFound`, `forbidden`, `platformUnsupported`, `platformCapabilityMissing`, `connectionDead`, `idempotencyConflict`, and `idempotencyInFlight`. `PLATFORM_LIMITATION` means the platform refused a send it could accept at another time — the 24-hour window has closed, or the recipient is unavailable — rather than that the request was wrong. Other platform failures carry the platform's own error code, which for Meta is a numeric string such as `100` or `190`. Absent on plain parameter-validation errors and on PostZen's own rate-limit response."
              },
              "platform": {
                "type": "string",
                "description": "Platform the failure relates to, when PostZen could determine one."
              },
              "platformError": {
                "type": "object",
                "description": "Meta's own error envelope, passed through untouched. Fields Meta did not report are absent rather than null.",
                "properties": {
                  "code": {
                    "type": "integer"
                  },
                  "subcode": {
                    "type": "integer"
                  },
                  "fbtraceId": {
                    "type": "string"
                  },
                  "type": {
                    "type": "string"
                  }
                }
              }
            }
          }
        ]
      },
      "ConnectStartResponse": {
        "type": "object",
        "required": [
          "authUrl",
          "state"
        ],
        "properties": {
          "authUrl": {
            "type": "string",
            "format": "uri",
            "description": "URL to redirect the user to for platform authorization."
          },
          "state": {
            "type": "string",
            "description": "OAuth state parameter generated by PostZen."
          }
        }
      },
      "ConnectCompleteRequest": {
        "type": "object",
        "required": [
          "code",
          "state",
          "profileId"
        ],
        "properties": {
          "code": {
            "type": "string",
            "description": "OAuth authorization code returned by the platform."
          },
          "state": {
            "type": "string",
            "description": "State returned by `GET /v1/connect/{platform}`."
          },
          "profileId": {
            "type": "string",
            "description": "PostZen profile id used when the connection was initiated."
          }
        }
      },
      "ConnectCompleteResponse": {
        "type": "object",
        "required": [
          "message",
          "platform",
          "profileId",
          "status"
        ],
        "properties": {
          "message": {
            "type": "string"
          },
          "platform": {
            "$ref": "#/components/schemas/PublicPlatformOutput"
          },
          "profileId": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "connected",
              "needsReauth"
            ]
          },
          "missingScopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "connectedAccountCount": {
            "type": "integer",
            "minimum": 0
          },
          "accounts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Account"
            }
          }
        }
      },
      "MediaPresignRequest": {
        "type": "object",
        "required": [
          "filename",
          "contentType"
        ],
        "properties": {
          "filename": {
            "type": "string",
            "description": "Original file name. PostZen sanitizes it for storage."
          },
          "contentType": {
            "type": "string",
            "enum": [
              "image/jpeg",
              "image/jpg",
              "image/png",
              "image/webp",
              "image/gif",
              "video/mp4",
              "video/mpeg",
              "video/quicktime",
              "video/avi",
              "video/x-msvideo",
              "video/webm",
              "video/x-m4v",
              "application/pdf"
            ]
          },
          "size": {
            "type": "integer",
            "minimum": 0,
            "maximum": 5368709120,
            "description": "File size in bytes."
          },
          "profileId": {
            "type": "string",
            "description": "Optional profile scope. If provided, the API key must have access to the profile."
          }
        }
      },
      "MediaPresignResponse": {
        "type": "object",
        "required": [
          "uploadUrl",
          "publicUrl",
          "key",
          "type"
        ],
        "properties": {
          "uploadUrl": {
            "type": "string",
            "format": "uri",
            "description": "Use this URL for the direct file upload."
          },
          "publicUrl": {
            "type": "string",
            "format": "uri",
            "description": "Use this URL in post `mediaItems`."
          },
          "key": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "image",
              "video",
              "gif",
              "document"
            ]
          }
        }
      },
      "MediaDirectUploadResponse": {
        "type": "object",
        "required": [
          "url",
          "filename",
          "contentType",
          "size"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Publicly fetchable URL for the stored file. Pass it as `attachmentUrl` when sending a direct message."
          },
          "filename": {
            "type": "string",
            "description": "Sanitized file name the object was stored under."
          },
          "contentType": {
            "type": "string"
          },
          "size": {
            "type": "integer",
            "description": "Stored size in bytes."
          }
        }
      },
      "CreatePostRequest": {
        "type": "object",
        "description": "Provide exactly one creation mode: `publishNow`, `scheduledFor`, `isDraft`, or `queuedFromProfile`. `platforms` is required unless `isDraft` is true.",
        "properties": {
          "title": {
            "type": "string",
            "description": "Internal post title. YouTube uses this as the fallback video title when `settings.title` is omitted."
          },
          "content": {
            "type": "string",
            "default": "",
            "description": "Shared post text. Platform-specific validation still applies."
          },
          "mediaItems": {
            "type": "array",
            "maxItems": 10,
            "description": "Duplicate URLs are removed before the post is created.",
            "items": {
              "$ref": "#/components/schemas/PostMediaItem"
            }
          },
          "platforms": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CreatePostTarget"
            }
          },
          "scheduledFor": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 date for scheduled posts. Must be at least 60 seconds in the future."
          },
          "publishNow": {
            "type": "boolean",
            "description": "Publish synchronously where possible."
          },
          "isDraft": {
            "type": "boolean",
            "description": "Create a draft. `platforms` is optional for drafts."
          },
          "queuedFromProfile": {
            "type": "string",
            "description": "Profile id whose queue places the post. PostZen assigns the next free slot and returns it as `scheduledFor`. Do not call `GET /v1/queue/next-slot` and pass the result as `scheduledFor`: the slot is only claimed by the create call itself, so a fetched slot can be taken by another request before yours arrives, and the post would be scheduled outside the queue."
          },
          "queueId": {
            "type": "string",
            "description": "Specific queue on `queuedFromProfile`. Defaults to that profile's default queue. Requires `queuedFromProfile`."
          },
          "timezone": {
            "type": "string",
            "default": "UTC",
            "description": "Ignored in queue mode; queued posts take the queue's timezone."
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "PostMediaItem": {
        "type": "object",
        "required": [
          "url"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "PostZen-hosted `publicUrl` from `POST /v1/media/presign`, or an external image/video URL. External URLs are downloaded and re-hosted by PostZen; they must resolve to an image or video (PDF is not supported) of at most 100 MB."
          },
          "title": {
            "type": "string",
            "description": "Optional alt text/title for the media item."
          }
        }
      },
      "CreatePostTarget": {
        "type": "object",
        "required": [
          "platform",
          "accountId"
        ],
        "properties": {
          "platform": {
            "$ref": "#/components/schemas/PublicPlatformInput"
          },
          "accountId": {
            "type": "string",
            "description": "PostZen account id or provider account id."
          },
          "customContent": {
            "type": "string",
            "description": "Overrides shared `content` for this platform."
          },
          "settings": {
            "$ref": "#/components/schemas/PostPlatformSettings"
          }
        }
      },
      "PostPlatformSettings": {
        "description": "Platform-specific publishing options. Unknown keys are ignored.",
        "oneOf": [
          {
            "$ref": "#/components/schemas/InstagramSettings"
          },
          {
            "$ref": "#/components/schemas/FacebookSettings"
          },
          {
            "$ref": "#/components/schemas/ThreadsSettings"
          },
          {
            "$ref": "#/components/schemas/TikTokSettings"
          },
          {
            "$ref": "#/components/schemas/LinkedInSettings"
          },
          {
            "$ref": "#/components/schemas/XSettings"
          },
          {
            "$ref": "#/components/schemas/YouTubeSettings"
          },
          {
            "$ref": "#/components/schemas/PinterestSettings"
          },
          {
            "$ref": "#/components/schemas/BlueskySettings"
          },
          {
            "$ref": "#/components/schemas/TelegramSettings"
          }
        ]
      },
      "InstagramSettings": {
        "type": "object",
        "description": "Instagram target settings. `postType` selects the format: `feed` and `story` take exactly one media item, `reel` takes exactly one video, and `carousel` takes 2–10 media items (images and videos may be mixed). Captions are limited to 2,200 characters.",
        "properties": {
          "postType": {
            "type": "string",
            "enum": [
              "feed",
              "story",
              "reel",
              "carousel"
            ],
            "default": "feed",
            "description": "Instagram post format. `feed` (default) and `story` publish a single media item; `reel` publishes a single video; `carousel` publishes 2–10 ordered media items and may mix images and videos. Sending multiple media items with `feed` returns a validation error directing you to `carousel`."
          },
          "collaborators": {
            "type": "array",
            "description": "Up to three Instagram usernames to invite as collaborators. Supported on feed, reel, and carousel posts.",
            "items": {
              "type": "string"
            }
          },
          "userTags": {
            "type": "array",
            "description": "Photo tags with relative `x`/`y` coordinates from 0 to 1. Feed posts only — user tags are not supported on carousels, reels, or stories.",
            "items": {
              "type": "object",
              "required": [
                "username",
                "x",
                "y"
              ],
              "properties": {
                "username": {
                  "type": "string"
                },
                "x": {
                  "type": "number"
                },
                "y": {
                  "type": "number"
                }
              }
            }
          },
          "firstComment": {
            "type": "string",
            "description": "Posts a first comment after publishing. Supported on feed, reel, and carousel posts. Maximum 2,200 characters."
          },
          "shareToFeed": {
            "type": "boolean",
            "description": "Reels only. Defaults to `true`. Set to `false` to keep the reel off the profile feed."
          }
        }
      },
      "FacebookSettings": {
        "type": "object",
        "properties": {
          "link": {
            "type": "string",
            "format": "uri"
          },
          "firstComment": {
            "type": "string"
          }
        }
      },
      "ThreadsSettings": {
        "type": "object",
        "properties": {
          "replyControl": {
            "type": "string",
            "enum": [
              "everyone",
              "accountsYouFollow",
              "mentionedOnly"
            ]
          }
        }
      },
      "TikTokSettings": {
        "type": "object",
        "properties": {
          "privacyLevel": {
            "type": "string",
            "enum": [
              "publicToEveryone",
              "mutualFollowFriends",
              "followerOfCreator",
              "selfOnly"
            ]
          },
          "allowComments": {
            "type": "boolean"
          },
          "allowDuet": {
            "type": "boolean"
          },
          "allowStitch": {
            "type": "boolean"
          },
          "disableComment": {
            "type": "boolean"
          },
          "disableDuet": {
            "type": "boolean"
          },
          "disableStitch": {
            "type": "boolean"
          },
          "videoCoverTimestampMs": {
            "type": "number"
          },
          "uploadAsDraft": {
            "type": "boolean"
          },
          "brandContentToggle": {
            "type": "boolean"
          },
          "brandOrganicToggle": {
            "type": "boolean"
          }
        }
      },
      "LinkedInSettings": {
        "type": "object",
        "description": "LinkedIn target settings. A LinkedIn post carries exactly one media kind: no media (text only), 1–20 images, one MP4 video, or one document (PDF/DOC/DOCX/PPT/PPTX). Commentary is limited to 3,000 characters. Every key below is also accepted in `snake_case` (for example `first_comment` and `organization_urn`).",
        "properties": {
          "visibility": {
            "type": "string",
            "enum": [
              "PUBLIC",
              "CONNECTIONS"
            ],
            "default": "PUBLIC",
            "description": "Who can see the post. `CONNECTIONS` is only valid for a member (personal profile) post — combining it with `organizationUrn` is a validation error, because a company page has no connections."
          },
          "videoTitle": {
            "type": "string",
            "maxLength": 200,
            "description": "Optional title for video posts, shown on the LinkedIn video player. Ignored for non-video posts."
          },
          "documentTitle": {
            "type": "string",
            "maxLength": 200,
            "description": "Title for a document (PDF carousel) post. LinkedIn requires a title on document posts; when this is omitted PostZen falls back to the uploaded file's name. Ignored for non-document posts."
          },
          "organizationUrn": {
            "type": "string",
            "pattern": "^(urn:li:organization:[0-9]+|[0-9]+)$",
            "description": "Publish as a LinkedIn company page instead of the connected member. Accepts either the full URN (`urn:li:organization:12345`) or the bare numeric page id (`12345`), which PostZen expands to the URN. The connection must have been authorized with the organization scopes — reconnect the account if it was connected before company-page posting was enabled. Also accepted as `organizationId` / `organization_id`."
          },
          "firstComment": {
            "type": "string",
            "maxLength": 1250,
            "description": "Comment posted by the same author immediately after the post goes live. LinkedIn's comment composer caps this at 1,250 characters, tighter than the 3,000-character post body. Best-effort: a failure here is logged and never fails the post, and the post is never retried because of it."
          },
          "disableLinkPreview": {
            "type": "boolean",
            "description": "LinkedIn's Posts API never scrapes URLs, so a bare link renders as plain text. When this is `false` or omitted and the text contains a URL, PostZen attaches a link card for the first URL; because no scraped metadata is available, the card is titled with the URL's hostname (for example `example.com`). Set to `true` to keep the post as plain text with no card. Also accepted as `disableLinkCard`."
          },
          "reshareUrl": {
            "type": "string",
            "description": "LinkedIn post to quote-reshare. Accepts a public post permalink or a `urn:li:activity:` / `urn:li:share:` / `urn:li:ugcPost:` URN. Mutually exclusive with uploaded media."
          },
          "geoRestrictionCountries": {
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "^[A-Z]{2}$"
            },
            "maxItems": 25,
            "description": "Restrict who sees the post to these countries, as uppercase ISO 3166-1 alpha-2 codes (for example `[\"US\", \"CA\"]`). Up to 25 countries, and organization posts only — supplying this without `organizationUrn` is a validation error."
          }
        }
      },
      "XSettings": {
        "type": "object",
        "properties": {
          "replySettings": {
            "type": "string",
            "enum": [
              "following",
              "mentionedUsers"
            ]
          }
        }
      },
      "YouTubeSettings": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string"
          },
          "privacyStatus": {
            "type": "string",
            "enum": [
              "public",
              "unlisted",
              "private"
            ]
          },
          "tags": {
            "oneOf": [
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              {
                "type": "string",
                "description": "Comma-separated tags."
              }
            ]
          },
          "categoryId": {
            "type": "string"
          },
          "madeForKids": {
            "type": "boolean"
          },
          "notifySubscribers": {
            "type": "boolean"
          }
        }
      },
      "PinterestSettings": {
        "type": "object",
        "required": [
          "boardId"
        ],
        "properties": {
          "boardId": {
            "type": "string",
            "description": "Pinterest board to publish the pin to."
          },
          "title": {
            "type": "string",
            "description": "Pin title."
          },
          "link": {
            "type": "string",
            "format": "uri",
            "description": "Destination link for the pin."
          },
          "altText": {
            "type": "string"
          },
          "coverImageUrl": {
            "type": "string",
            "format": "uri",
            "description": "Cover or thumbnail image URL for video pins."
          },
          "coverImageKeyFrameTime": {
            "type": "number",
            "minimum": 0,
            "description": "Cover keyframe time in seconds for video pins, as an alternative to coverImageUrl."
          }
        }
      },
      "BlueskySettings": {
        "type": "object",
        "properties": {
          "altTexts": {
            "type": "array",
            "items": {
              "type": "string",
              "maxLength": 2000
            },
            "description": "Alt text for each image, matched to the media by order. Each entry is limited to 2000 characters."
          },
          "languages": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "maxItems": 3,
            "description": "Up to 3 BCP-47 language codes (e.g. `en`, `pt-BR`) declaring the languages of the post text."
          },
          "disableLinkCard": {
            "type": "boolean",
            "description": "When true, PostZen skips generating an external link preview card for the first URL in the post."
          }
        }
      },
      "TelegramSettings": {
        "type": "object",
        "description": "Telegram target settings. Text-only posts allow 4,096 characters; attaching any media caps the text at 1,024 characters as a caption. A post carries 1-10 media items and may mix photos and videos in one album, but a GIF must be posted on its own. Telegram reports no post analytics.",
        "properties": {
          "parseMode": {
            "type": "string",
            "enum": [
              "html",
              "markdownv2"
            ],
            "description": "Formatting mode for the message or caption. Omit to send plain text, which is the default. `html` is recommended: it only requires escaping `<`, `>`, and `&`, whereas `markdownv2` requires escaping every one of `_ * [ ] ( ) ~ ` > # + - = | { } . !` and rejects the whole message otherwise."
          },
          "disableNotification": {
            "type": "boolean",
            "description": "When true, members receive the post silently, with no sound or vibration."
          },
          "disableLinkPreview": {
            "type": "boolean",
            "description": "When true, suppresses the link preview card for URLs in the text. Applies to text-only posts; a post with media has no link preview."
          },
          "protectContent": {
            "type": "boolean",
            "description": "When true, Telegram blocks forwarding and saving of the post."
          }
        }
      },
      "ApiPost": {
        "type": "object",
        "required": [
          "_id",
          "title",
          "content",
          "status",
          "scheduledFor",
          "timezone",
          "platforms"
        ],
        "properties": {
          "_id": {
            "type": "string"
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "content": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "scheduledFor": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "queueId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Queue that placed this post, or null when the post was not queued."
          },
          "timezone": {
            "type": "string"
          },
          "platforms": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiPostPlatformResult"
            }
          }
        }
      },
      "ApiPostPlatformResult": {
        "type": "object",
        "required": [
          "platform",
          "accountId",
          "status"
        ],
        "properties": {
          "platform": {
            "$ref": "#/components/schemas/PublicPlatformInput"
          },
          "accountId": {
            "$ref": "#/components/schemas/ApiPostAccount"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "scheduled",
              "pending",
              "publishing",
              "published",
              "failed",
              "canceled"
            ]
          },
          "platformPostUrl": {
            "type": "string",
            "format": "uri"
          },
          "error": {
            "type": "string"
          }
        }
      },
      "ApiPostAccount": {
        "type": "object",
        "required": [
          "_id",
          "platform",
          "username",
          "displayName",
          "isActive"
        ],
        "properties": {
          "_id": {
            "type": "string"
          },
          "platform": {
            "$ref": "#/components/schemas/PublicPlatformInput"
          },
          "username": {
            "type": "string"
          },
          "displayName": {
            "type": "string"
          },
          "isActive": {
            "type": "boolean"
          }
        }
      },
      "CreatePostResponse": {
        "type": "object",
        "required": [
          "post",
          "message"
        ],
        "properties": {
          "post": {
            "$ref": "#/components/schemas/ApiPost"
          },
          "message": {
            "type": "string",
            "enum": [
              "Post published successfully",
              "Post scheduled successfully",
              "Draft created successfully"
            ]
          }
        }
      },
      "UpdatePostResponse": {
        "type": "object",
        "required": [
          "post",
          "message"
        ],
        "properties": {
          "post": {
            "$ref": "#/components/schemas/ApiPost"
          },
          "message": {
            "type": "string"
          }
        }
      },
      "CreatePostReplayResponse": {
        "type": "object",
        "required": [
          "existingPost",
          "message"
        ],
        "properties": {
          "existingPost": {
            "$ref": "#/components/schemas/ApiPost"
          },
          "message": {
            "type": "string",
            "const": "Post already exists for this request id"
          }
        }
      },
      "ApiKeyProfileRef": {
        "type": "object",
        "required": [
          "_id",
          "name",
          "color"
        ],
        "properties": {
          "_id": {
            "type": "string",
            "description": "PostZen profile id."
          },
          "name": {
            "type": "string"
          },
          "color": {
            "type": "string",
            "description": "Hex color in `#rrggbb` format."
          }
        }
      },
      "ApiKey": {
        "type": "object",
        "required": [
          "id",
          "name",
          "keyPreview",
          "createdAt",
          "lastUsedAt",
          "scope",
          "profileIds",
          "permission"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "API key id."
          },
          "name": {
            "type": "string",
            "description": "Human-readable label."
          },
          "keyPreview": {
            "type": "string",
            "description": "Masked preview of the key, for example `pzn_live_a1b2c3d4e...`. The full key is never retrievable after creation."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "lastUsedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Last time the key authenticated a request, or `null` if it has never been used."
          },
          "scope": {
            "type": "string",
            "enum": [
              "full",
              "profiles"
            ],
            "description": "`full` grants access to all profiles; `profiles` restricts the key to `profileIds`."
          },
          "profileIds": {
            "type": "array",
            "description": "Profiles the key is restricted to. Empty for full-scope keys.",
            "items": {
              "$ref": "#/components/schemas/ApiKeyProfileRef"
            }
          },
          "permission": {
            "type": "string",
            "enum": [
              "read-write",
              "read"
            ],
            "description": "`read` keys may only call `GET` endpoints; `read-write` keys may call every endpoint."
          }
        }
      },
      "ApiKeyWithSecret": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ApiKey"
          },
          {
            "type": "object",
            "required": [
              "key"
            ],
            "properties": {
              "key": {
                "type": "string",
                "description": "The full API key. Shown only once in the create response — store it securely, as it cannot be retrieved again."
              }
            }
          }
        ]
      },
      "ApiKeysListResponse": {
        "type": "object",
        "required": [
          "apiKeys"
        ],
        "properties": {
          "apiKeys": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiKey"
            }
          }
        }
      },
      "ApiKeyCreateRequest": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "description": "Human-readable label for the key."
          },
          "scope": {
            "type": "string",
            "enum": [
              "full",
              "profiles"
            ],
            "default": "full",
            "description": "`full` grants access to all profiles; `profiles` restricts the key to `profileIds`."
          },
          "profileIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "PostZen profile ids. Required when `scope` is `profiles`, and forbidden when `scope` is `full`."
          },
          "permission": {
            "type": "string",
            "enum": [
              "read-write",
              "read"
            ],
            "default": "read-write",
            "description": "`read` keys may only call `GET` endpoints."
          }
        }
      },
      "ApiKeyCreateResponse": {
        "type": "object",
        "required": [
          "message",
          "apiKey"
        ],
        "properties": {
          "message": {
            "type": "string"
          },
          "apiKey": {
            "$ref": "#/components/schemas/ApiKeyWithSecret"
          }
        }
      },
      "WebhookEvent": {
        "type": "string",
        "enum": [
          "post.published",
          "post.partially_failed",
          "post.failed",
          "account.needs_reauth",
          "account.disconnected",
          "webhook.test"
        ],
        "description": "Webhook event type. Receivers should tolerate additional event types in future API versions."
      },
      "WebhookDeliveryStatus": {
        "type": "string",
        "enum": [
          "pending",
          "retrying",
          "delivered",
          "failed"
        ]
      },
      "WebhookCustomHeader": {
        "type": "object",
        "required": [
          "name",
          "value"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "HTTP header name. Content-Type, transport headers, and X-PostZen-* names are reserved."
          },
          "value": {
            "type": "string",
            "maxLength": 4096,
            "writeOnly": true,
            "description": "Plaintext header value. PostZen encrypts it at rest and never returns it."
          }
        }
      },
      "Webhook": {
        "type": "object",
        "required": [
          "id",
          "name",
          "url",
          "events",
          "profileAccess",
          "profileIds",
          "hasSigningSecret",
          "customHeaderNames",
          "isActive",
          "consecutiveFailures",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/WebhookEvent"
            }
          },
          "profileAccess": {
            "type": "string",
            "enum": [
              "all_profiles",
              "selected_profiles"
            ]
          },
          "profileIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Selected profile ids. Empty when profileAccess is all_profiles."
          },
          "hasSigningSecret": {
            "type": "boolean",
            "description": "Whether PostZen signs deliveries for this endpoint. The secret itself is never returned."
          },
          "customHeaderNames": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Configured custom header names. Header values are never returned."
          },
          "isActive": {
            "type": "boolean"
          },
          "disabledReason": {
            "type": "string",
            "enum": [
              "auto_consecutive_failures",
              "user_disabled"
            ]
          },
          "consecutiveFailures": {
            "type": "integer",
            "minimum": 0
          },
          "lastDeliveredAt": {
            "type": "string",
            "format": "date-time"
          },
          "lastFailedAt": {
            "type": "string",
            "format": "date-time"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WebhookCreateRequest": {
        "type": "object",
        "required": [
          "name",
          "url",
          "events",
          "profileAccess"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Public HTTPS endpoint URL."
          },
          "events": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/WebhookEvent"
            }
          },
          "profileAccess": {
            "type": "string",
            "enum": [
              "all_profiles",
              "selected_profiles"
            ],
            "description": "all_profiles receives events for every profile. selected_profiles requires profileIds."
          },
          "profileIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Required for selected_profiles and omitted or empty for all_profiles."
          },
          "secret": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 1,
            "maxLength": 256,
            "writeOnly": true,
            "description": "Optional HMAC signing secret. Omit or send null to create an unsigned endpoint."
          },
          "customHeaders": {
            "type": "array",
            "maxItems": 20,
            "items": {
              "$ref": "#/components/schemas/WebhookCustomHeader"
            }
          },
          "isActive": {
            "type": "boolean",
            "default": true
          }
        }
      },
      "WebhookUpdateRequest": {
        "type": "object",
        "description": "Include at least one field. Omitted fields keep their stored values.",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/WebhookEvent"
            }
          },
          "profileAccess": {
            "type": "string",
            "enum": [
              "all_profiles",
              "selected_profiles"
            ]
          },
          "profileIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Replacement profile selection. Must be non-empty for selected_profiles and empty for all_profiles."
          },
          "secret": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 1,
            "maxLength": 256,
            "writeOnly": true,
            "description": "Omit to keep the stored secret, send a non-empty string to replace it, or send null to remove it."
          },
          "customHeaders": {
            "type": "array",
            "maxItems": 20,
            "items": {
              "$ref": "#/components/schemas/WebhookCustomHeader"
            },
            "description": "Omit to keep stored headers. Send the complete collection to replace all headers, or [] to remove all headers. Every supplied value is plaintext and is encrypted at rest."
          },
          "isActive": {
            "type": "boolean",
            "description": "Enable or disable delivery. Re-enabling resets the consecutive exhausted-event count."
          }
        }
      },
      "WebhookDelivery": {
        "type": "object",
        "required": [
          "event",
          "eventId",
          "deliveryId",
          "resourceId",
          "status",
          "attemptCount",
          "createdAt"
        ],
        "properties": {
          "event": {
            "$ref": "#/components/schemas/WebhookEvent"
          },
          "eventId": {
            "type": "string",
            "description": "Stable across automatic retries and manual redeliveries."
          },
          "deliveryId": {
            "type": "string"
          },
          "resourceId": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/WebhookDeliveryStatus"
          },
          "attemptCount": {
            "type": "integer",
            "minimum": 0,
            "description": "Monotonic count across automatic retries and manual redeliveries."
          },
          "lastHttpStatus": {
            "type": "integer",
            "minimum": 100,
            "maximum": 599
          },
          "lastErrorCode": {
            "type": "string"
          },
          "lastDurationMs": {
            "type": "number",
            "minimum": 0
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "lastAttemptAt": {
            "type": "string",
            "format": "date-time"
          },
          "deliveredAt": {
            "type": "string",
            "format": "date-time"
          },
          "nextAttemptAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WebhooksListResponse": {
        "type": "object",
        "required": [
          "webhooks"
        ],
        "properties": {
          "webhooks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Webhook"
            }
          }
        }
      },
      "WebhookResponse": {
        "type": "object",
        "required": [
          "webhook"
        ],
        "properties": {
          "webhook": {
            "$ref": "#/components/schemas/Webhook"
          }
        }
      },
      "WebhookWriteResponse": {
        "type": "object",
        "required": [
          "message",
          "webhook"
        ],
        "properties": {
          "message": {
            "type": "string"
          },
          "webhook": {
            "$ref": "#/components/schemas/Webhook"
          }
        }
      },
      "WebhookTestResponse": {
        "type": "object",
        "required": [
          "message",
          "eventId",
          "deliveryId"
        ],
        "properties": {
          "message": {
            "type": "string"
          },
          "eventId": {
            "type": "string"
          },
          "deliveryId": {
            "type": "string"
          }
        }
      },
      "WebhookDeliveriesListResponse": {
        "type": "object",
        "required": [
          "deliveries",
          "pagination"
        ],
        "properties": {
          "deliveries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookDelivery"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/Pagination"
          }
        }
      },
      "WebhookRedeliveryResponse": {
        "type": "object",
        "required": [
          "message",
          "delivery"
        ],
        "properties": {
          "message": {
            "type": "string"
          },
          "delivery": {
            "$ref": "#/components/schemas/WebhookDelivery"
          }
        }
      },
      "QueueSlot": {
        "type": "object",
        "required": [
          "dayOfWeek",
          "time"
        ],
        "properties": {
          "dayOfWeek": {
            "type": "integer",
            "minimum": 0,
            "maximum": 6,
            "description": "Day of the week, 0 = Sunday through 6 = Saturday."
          },
          "time": {
            "type": "string",
            "pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$",
            "description": "24-hour wall-clock time (`HH:mm`) in the queue's timezone."
          }
        }
      },
      "QueueSchedule": {
        "type": "object",
        "required": [
          "_id",
          "profileId",
          "name",
          "timezone",
          "slots",
          "active",
          "isDefault",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "_id": {
            "type": "string"
          },
          "profileId": {
            "type": "string"
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "description": {
            "type": "string",
            "maxLength": 500
          },
          "timezone": {
            "type": "string",
            "description": "IANA timezone the slots are evaluated in, for example `America/Edmonton`."
          },
          "slots": {
            "type": "array",
            "maxItems": 56,
            "items": {
              "$ref": "#/components/schemas/QueueSlot"
            },
            "description": "Weekly slots, deduplicated and sorted by day then time."
          },
          "active": {
            "type": "boolean",
            "description": "Paused queues reject new queue-mode posts and report no upcoming slots."
          },
          "isDefault": {
            "type": "boolean",
            "description": "Whether this queue answers when `queueId` is omitted."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "QueueScheduleWithNextSlots": {
        "allOf": [
          {
            "$ref": "#/components/schemas/QueueSchedule"
          },
          {
            "type": "object",
            "required": [
              "nextSlots"
            ],
            "properties": {
              "nextSlots": {
                "type": "array",
                "items": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          }
        ]
      },
      "QueueScheduleResponse": {
        "type": "object",
        "required": [
          "exists",
          "schedule",
          "nextSlots"
        ],
        "properties": {
          "exists": {
            "type": "boolean",
            "description": "False when the profile has no matching queue; `schedule` is then null."
          },
          "schedule": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/QueueSchedule"
              },
              {
                "type": "null"
              }
            ]
          },
          "nextSlots": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Next five instants this queue would hand out, occupied slots already skipped."
          }
        }
      },
      "QueueSchedulesResponse": {
        "type": "object",
        "required": [
          "exists",
          "schedules"
        ],
        "properties": {
          "exists": {
            "type": "boolean"
          },
          "schedules": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/QueueScheduleWithNextSlots"
            }
          }
        }
      },
      "QueueSlotsResponse": {
        "description": "A single schedule, or every schedule on the profile when `all=true`.",
        "oneOf": [
          {
            "$ref": "#/components/schemas/QueueScheduleResponse"
          },
          {
            "$ref": "#/components/schemas/QueueSchedulesResponse"
          }
        ]
      },
      "QueueCreateRequest": {
        "type": "object",
        "required": [
          "profileId",
          "timezone",
          "slots"
        ],
        "properties": {
          "profileId": {
            "type": "string",
            "description": "Profile the queue belongs to."
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "default": "Default Queue"
          },
          "description": {
            "type": "string",
            "maxLength": 500
          },
          "timezone": {
            "type": "string",
            "description": "IANA timezone. Rejected if unknown — queues never silently fall back to UTC."
          },
          "slots": {
            "type": "array",
            "maxItems": 56,
            "items": {
              "$ref": "#/components/schemas/QueueSlot"
            },
            "description": "Weekly slots. May be empty; a queue with no slots accepts no queued posts until slots are added."
          },
          "active": {
            "type": "boolean",
            "default": true
          },
          "setAsDefault": {
            "type": "boolean",
            "description": "Make this the profile's default queue. The first queue on a profile becomes default regardless."
          }
        }
      },
      "QueueUpdateRequest": {
        "type": "object",
        "required": [
          "profileId",
          "timezone",
          "slots"
        ],
        "properties": {
          "profileId": {
            "type": "string",
            "description": "Profile that owns the queue."
          },
          "queueId": {
            "type": "string",
            "description": "Queue to update. Defaults to the profile's default queue."
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "description": {
            "type": "string",
            "maxLength": 500,
            "description": "Send an empty string to clear the description."
          },
          "timezone": {
            "type": "string"
          },
          "slots": {
            "type": "array",
            "maxItems": 56,
            "items": {
              "$ref": "#/components/schemas/QueueSlot"
            },
            "description": "Replaces the queue's slots entirely."
          },
          "active": {
            "type": "boolean"
          },
          "setAsDefault": {
            "type": "boolean"
          },
          "reshuffleExisting": {
            "type": "boolean",
            "description": "Re-place the queue's future scheduled posts onto the new schedule. Only applies when the timezone or slots actually changed."
          }
        }
      },
      "QueueWriteResponse": {
        "type": "object",
        "required": [
          "success",
          "schedule",
          "nextSlots"
        ],
        "properties": {
          "success": {
            "type": "boolean"
          },
          "schedule": {
            "$ref": "#/components/schemas/QueueSchedule"
          },
          "nextSlots": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "date-time"
            }
          }
        }
      },
      "QueueUpdateResponse": {
        "type": "object",
        "required": [
          "success",
          "schedule",
          "nextSlots",
          "reshuffledCount"
        ],
        "properties": {
          "success": {
            "type": "boolean"
          },
          "schedule": {
            "$ref": "#/components/schemas/QueueSchedule"
          },
          "nextSlots": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "date-time"
            }
          },
          "reshuffledCount": {
            "type": "integer",
            "minimum": 0,
            "description": "How many scheduled posts were moved onto the new schedule."
          }
        }
      },
      "QueueDeleteResponse": {
        "type": "object",
        "required": [
          "success",
          "deleted",
          "deletedCount"
        ],
        "properties": {
          "success": {
            "type": "boolean"
          },
          "deleted": {
            "type": "boolean"
          },
          "deletedCount": {
            "type": "integer",
            "minimum": 1,
            "description": "How many schedules were deleted. Always 1 when `queueId` is supplied; when it is omitted every schedule on the profile is deleted and this is that count."
          }
        }
      },
      "QueueNextSlotResponse": {
        "type": "object",
        "required": [
          "profileId",
          "nextSlot",
          "timezone",
          "queueId",
          "queueName"
        ],
        "properties": {
          "profileId": {
            "type": "string"
          },
          "nextSlot": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Null when every slot in the next year is already taken."
          },
          "timezone": {
            "type": "string"
          },
          "queueId": {
            "type": "string"
          },
          "queueName": {
            "type": "string"
          }
        }
      },
      "QueuePreviewResponse": {
        "type": "object",
        "required": [
          "profileId",
          "count",
          "slots"
        ],
        "properties": {
          "profileId": {
            "type": "string"
          },
          "count": {
            "type": "integer",
            "minimum": 0,
            "description": "Number of slots returned, which can be fewer than requested."
          },
          "slots": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "date-time"
            }
          }
        }
      }
    }
  }
}