{
  "openapi": "3.1.0",
  "info": {
    "title": "Castrook API",
    "version": "1.3.0",
    "summary": "Customer onboarding, media, publishing, comments and performance in one API.",
    "description": "Developer beta. Create scoped test/live keys in the dashboard. Test delivery never posts externally. Live platform access requires configured OAuth applications and any required provider approval. All times are RFC 3339. API changes are versioned under /v1. Plans include monthly allowances and a per-minute API rate; read them with GET /usage. Work beyond an allowance is rejected with 429 usage_limit_exceeded and is never charged automatically; do not retry it before details.reset_at. Cached resource/status/usage reads, cancellation, disconnect, webhook disabling and exact post replays do not consume monthly API requests. Fresh publishing-options and comment reads do. Authentication, scope and per-minute limits apply to all requests. Server errors (5xx) do not count toward the API request allowance. OAuth app grants also use this REST contract; access tokens must be bound to the exact REST resource https://castrook.com/api/v1. MCP has its own resource https://castrook.com/mcp. The grants are workspace-, mode- and scope-bound; credentials cannot cross these resources."
  },
  "servers": [
    {
      "url": "/api/v1",
      "description": "Same-origin Castrook API"
    }
  ],
  "tags": [
    {
      "name": "Profiles"
    },
    {
      "name": "Onboarding"
    },
    {
      "name": "Accounts"
    },
    {
      "name": "Media"
    },
    {
      "name": "Posts"
    },
    {
      "name": "Analytics"
    },
    {
      "name": "Comments"
    },
    {
      "name": "Webhooks"
    },
    {
      "name": "Logs"
    },
    {
      "name": "Usage"
    }
  ],
  "paths": {
    "/accounts/{id}/analytics-consent": {
      "post": {
        "operationId": "acceptAnalyticsPolicy",
        "summary": "Record account-owner acceptance for YouTube performance",
        "description": "Requires accounts:write and a connected YouTube account. Submit only after the account owner expressly accepts the current Castrook privacy policy at /privacy. Never infer consent from an old OAuth grant or submit on their behalf without authorization. The server timestamps the receipt and binds it to the connection authorization revision; reconnection requires new acceptance. Legacy YouTube metrics are redacted and external refresh is blocked until acceptance. Hosted YouTube onboarding records acceptance during its explicit legal step. Other platforms return 409 analytics_consent_not_required. Recovery operation, exempt from monthly API usage.",
        "tags": [
          "Accounts"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "accounts:write"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Account"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AnalyticsConsent"
              }
            }
          }
        },
        "x-castrook-scopes": [
          "accounts:write"
        ]
      }
    },
    "/profiles": {
      "get": {
        "operationId": "listProfiles",
        "summary": "List customer profiles",
        "description": "Requires profiles:read. Names use case-insensitive substring matching; external_id is an exact match. Keep filters fixed while paging. Cached recovery read, exempt from monthly API usage.",
        "tags": [
          "Profiles"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "profiles:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Profile"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/PageMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "parameters": [
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 4096
            },
            "description": "Opaque cursor returned by the preceding response."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 200
            }
          },
          {
            "name": "external_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 200
            }
          }
        ],
        "x-castrook-scopes": [
          "profiles:read"
        ]
      },
      "post": {
        "operationId": "createProfile",
        "summary": "Create a customer profile",
        "description": "Requires profiles:write. A profile groups customer accounts, posts, analytics and media within this workspace and mode. external_id must be unique when supplied.",
        "tags": [
          "Profiles"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "profiles:write"
            ]
          }
        ],
        "responses": {
          "201": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Profile"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateProfile"
              }
            }
          }
        },
        "x-castrook-scopes": [
          "profiles:write"
        ]
      }
    },
    "/profiles/{id}": {
      "get": {
        "operationId": "getProfile",
        "summary": "Retrieve a customer profile",
        "description": "Requires profiles:read. Cached recovery read, exempt from monthly API usage.",
        "tags": [
          "Profiles"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "profiles:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Profile"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "x-castrook-scopes": [
          "profiles:read"
        ]
      },
      "patch": {
        "operationId": "updateProfile",
        "summary": "Update a customer profile",
        "description": "Requires profiles:write. Omitted fields are preserved; external_id:null removes the reference.",
        "tags": [
          "Profiles"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "profiles:write"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Profile"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateProfile"
              }
            }
          }
        },
        "x-castrook-scopes": [
          "profiles:write"
        ]
      },
      "delete": {
        "operationId": "deleteProfile",
        "summary": "Delete an unused customer profile",
        "description": "Requires profiles:write. Move/unassign its accounts, finish/cancel pending posts and delete its media first; otherwise returns 409. Media already deleting does not block. Terminal post and analytics history retains the original profile ID and does not block deletion. Revokes associated hosted connection sessions. Recovery operation, exempt from monthly API usage.",
        "tags": [
          "Profiles"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "profiles:write"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/DeletedResource"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "x-castrook-scopes": [
          "profiles:write"
        ]
      }
    },
    "/connect-sessions": {
      "post": {
        "operationId": "createConnectSession",
        "summary": "Create a hosted customer connection link",
        "description": "Requires accounts:write and a profile in the same mode. Returns one private connection URL. The customer connects eligible accounts without accessing your dashboard or API key. Live connections use OAuth; test connections simulate authorization. Embed only when embed_origin is configured. Sessions are browser-bound and checked again at callback; expiry or revocation prevents new authorization.",
        "tags": [
          "Onboarding"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "accounts:write"
            ]
          }
        ],
        "responses": {
          "201": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ConnectSessionCreated"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateConnectSession"
              }
            }
          }
        },
        "x-castrook-scopes": [
          "accounts:write"
        ]
      }
    },
    "/connect-sessions/{id}": {
      "get": {
        "operationId": "getConnectSession",
        "summary": "Retrieve connection progress",
        "description": "Requires accounts:read. Poll status and account_ids; the capability URL is never returned again. Cached recovery read, exempt from monthly API usage.",
        "tags": [
          "Onboarding"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "accounts:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ConnectSession"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "x-castrook-scopes": [
          "accounts:read"
        ]
      },
      "delete": {
        "operationId": "revokeConnectSession",
        "summary": "Revoke a customer connection link",
        "description": "Requires accounts:write. Prevents pending and future authorization through this link. Accounts already connected remain connected. Recovery operation, exempt from monthly API usage.",
        "tags": [
          "Onboarding"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "accounts:write"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ConnectSession"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "x-castrook-scopes": [
          "accounts:write"
        ]
      }
    },
    "/accounts": {
      "get": {
        "operationId": "listAccounts",
        "summary": "List connected accounts",
        "description": "Requires accounts:read. Isolated by workspace and key mode. Cached recovery read, exempt from monthly API usage.",
        "tags": [
          "Accounts"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "accounts:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Account"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/PageMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "parameters": [
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 4096
            },
            "description": "Opaque cursor returned by the preceding response."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "profile_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^prf_[a-f0-9]{24}$"
            }
          },
          {
            "name": "platform",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "tiktok",
                "instagram",
                "facebook",
                "youtube",
                "pinterest",
                "linkedin"
              ]
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 200
            }
          }
        ],
        "x-castrook-scopes": [
          "accounts:read"
        ]
      },
      "post": {
        "operationId": "createTestAccount",
        "summary": "Create a sandbox account",
        "description": "Requires accounts:write and a test key. Live accounts connect through OAuth in the dashboard or a hosted customer connection session. Name and username are trimmed. Test mode allows 10 connected simulated accounts; disconnect one before adding another (409 account_limit_reached).",
        "tags": [
          "Accounts"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "accounts:write"
            ]
          }
        ],
        "responses": {
          "201": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Account"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "platform": {
                    "type": "string",
                    "enum": [
                      "tiktok",
                      "instagram",
                      "facebook",
                      "youtube",
                      "pinterest",
                      "linkedin"
                    ]
                  },
                  "profile_id": {
                    "type": "string",
                    "pattern": "^prf_[a-f0-9]{24}$"
                  },
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 100
                  },
                  "username": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 100
                  }
                },
                "required": [
                  "platform"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "x-castrook-scopes": [
          "accounts:write"
        ]
      }
    },
    "/accounts/{id}": {
      "patch": {
        "operationId": "assignAccountProfile",
        "summary": "Move an account to a customer profile",
        "description": "Requires accounts:write. Assigns a profile in the same workspace and mode; null removes its assignment. Returns 409 while accepted posts are pending for this account. Existing historical posts retain their recorded customer association.",
        "tags": [
          "Accounts"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "accounts:write"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Account"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "profile_id": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "pattern": "^prf_[a-f0-9]{24}$"
                  }
                },
                "required": [
                  "profile_id"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "x-castrook-scopes": [
          "accounts:write"
        ]
      },
      "delete": {
        "operationId": "disconnectAccount",
        "summary": "Disconnect an account",
        "description": "Requires accounts:write. Disables the connection. For YouTube, requests Google authorization revocation and purges associated stored API data; Google revocation can affect other Castrook connections under the same authorization. Provider failures are retried with bounded encrypted credential retention. An already-accepted upload may still finish. Does not delete published videos or comments. Reconnection can be temporarily blocked while revocation and cleanup finish.",
        "tags": [
          "Accounts"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "accounts:write"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Account"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "x-castrook-scopes": [
          "accounts:write"
        ]
      }
    },
    "/accounts/{id}/publishing-options": {
      "get": {
        "operationId": "getPublishingOptions",
        "summary": "Get current creator publishing controls",
        "description": "Requires accounts:read. TikTok accounts only; other platforms return 422 publishing_options_unavailable. Fetch before rendering the creator consent flow. Display the creator nickname, require manual privacy selection without a default, and respect disabled interactions and maximum duration. Test accounts return deterministic settings; live accounts query TikTok using the connected account. These controls may change before delivery, when Castrook validates them again.",
        "tags": [
          "Accounts"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "accounts:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/TikTokPublishingOptions"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "x-castrook-scopes": [
          "accounts:read"
        ]
      }
    },
    "/accounts/{id}/boards": {
      "get": {
        "operationId": "listPinterestBoards",
        "summary": "List Pinterest boards",
        "description": "Requires accounts:read. Pinterest accounts only, isolated by workspace and mode. Live reads require granted boards:read permission. Trial reads use Pinterest Sandbox and never expose production boards. Follow opaque meta.next_cursor while has_more is true, even when a page data array is empty. Cursor reuse across accounts, resources or modes is rejected. Fresh provider reads count as API requests; Test results are simulated.",
        "tags": [
          "Accounts"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "accounts:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PinterestBoard"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/PageMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 8192
            },
            "description": "Opaque encrypted cursor returned by the preceding board/section response."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          }
        ],
        "x-castrook-scopes": [
          "accounts:read"
        ]
      }
    },
    "/accounts/{id}/boards/{board_id}/sections": {
      "get": {
        "operationId": "listPinterestBoardSections",
        "summary": "List Pinterest board sections",
        "description": "Requires accounts:read. Return sections belonging to the selected board on this Pinterest account; the board and account remain bound to workspace, mode and cursor. Live reads require boards:read permission. Test mode returns deterministic sections. Fresh provider reads count as API requests.",
        "tags": [
          "Accounts"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "accounts:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PinterestBoardSection"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/PageMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "board_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9]{1,40}$",
              "description": "Numeric Pinterest ID returned by board/section discovery; preserve it as a string."
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 8192
            },
            "description": "Opaque encrypted cursor returned by the preceding board/section response."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          }
        ],
        "x-castrook-scopes": [
          "accounts:read"
        ]
      }
    },
    "/posts": {
      "get": {
        "operationId": "listPosts",
        "summary": "List posts",
        "description": "Requires posts:read. Cursor-paginated results, scoped to key mode.",
        "tags": [
          "Posts"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "posts:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Post"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/PageMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "parameters": [
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 4096
            },
            "description": "Opaque cursor returned by the preceding response."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "scheduled",
                "queued",
                "publishing",
                "published",
                "partially_failed",
                "failed",
                "canceled"
              ]
            }
          },
          {
            "name": "platform",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "tiktok",
                "instagram",
                "facebook",
                "youtube",
                "pinterest",
                "linkedin"
              ]
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 200
            },
            "description": "Case-insensitive caption substring. Keep all filters unchanged while paging; cursors are bound to workspace, mode and filters."
          },
          {
            "name": "profile_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^prf_[a-f0-9]{24}$"
            }
          }
        ],
        "x-castrook-scopes": [
          "posts:read"
        ]
      },
      "post": {
        "operationId": "createPost",
        "summary": "Queue or schedule a post",
        "description": "Requires posts:write. Returns acceptance, not final publication. Poll GET /posts/{id} or use webhooks. Every destination has independent status. Test mode simulates delivery without publishing. Request JSON is limited to 64 KiB. Live TikTok supports one video or up to 35 images; Instagram supports one image/Reel, ordered carousels of up to 10 images/videos, and one-image/video Stories for Business accounts; Facebook supports text, up to 10 feed photos, one video/Reel or one-image/video Story; YouTube requires one video and made_for_kids. Pinterest requires one image (feed) or video (video) and an explicit board_id; optional section/title/link/alt text and video cover controls are supported. Trial Pinterest access uses Sandbox images only and rejects video before provider writes. LinkedIn supports text, article links with an optional single-image thumbnail, up to 20 ordered images, or one video. LinkedIn CONNECTIONS visibility is member-only, and article_url requires article_title. Mixed TikTok/Facebook/Pinterest/LinkedIn media is rejected. Stories require an empty caption because these APIs do not overlay text. Omitted format retains existing per-platform defaults. All chosen destination content is validated before queueing; each account receives only its own overrides. TikTok API clients must provide the required creator-facing preview, current settings, commercial disclosures and explicit approval before submitting each post. Do not use scheduled execution as permission for unreviewed content. Each destination reserves one publication from the allowance of the period it is scheduled for (429 usage_limit_exceeded when none remain); the first provider write atomically claims the current period and a published destination consumes that claim even if completion crosses a reset, a failed or canceled destination releases it, and an uncertain delivery stays reserved until reconciled. A new accepted post consumes one API request atomically; an exact idempotent replay consumes no additional API request or publication, including at the monthly cap. Live posts return 409 account_capacity_exceeded while the workspace has more connected live accounts than its plan allows. Destinations are rechecked before delivery starts and fail with account_capacity_exceeded or usage_limit_exceeded if the workspace became ineligible.",
        "tags": [
          "Posts"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "posts:write"
            ]
          }
        ],
        "responses": {
          "202": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Idempotency-Replayed": {
                "schema": {
                  "type": "boolean"
                },
                "description": "true when returning an earlier operation."
              },
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "Relative URL of the accepted or replayed post."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Post"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          },
          "200": {
            "description": "Idempotent replay of the original post.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Idempotency-Replayed": {
                "schema": {
                  "type": "boolean"
                },
                "description": "true when returning an earlier operation."
              },
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "Relative URL of the accepted or replayed post."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Post"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 8,
              "maxLength": 128,
              "pattern": "^[A-Za-z0-9_.:-]{8,128}$"
            },
            "description": "Stable key for this logical post. Replay with identical JSON returns the original post; differing input returns 409."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePost"
              }
            }
          }
        },
        "x-castrook-scopes": [
          "posts:write"
        ]
      }
    },
    "/posts/{id}": {
      "get": {
        "operationId": "getPost",
        "summary": "Retrieve post and destination results",
        "description": "Requires posts:read. Published records the confirmed delivery outcome; it does not guarantee current availability on the platform. Partial failures retain successful destinations. YouTube provider IDs and URLs are refreshed while connected and can be removed after disconnection, provider deletion, or expiry of data that cannot be refreshed.",
        "tags": [
          "Posts"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "posts:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Post"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "x-castrook-scopes": [
          "posts:read"
        ]
      },
      "delete": {
        "operationId": "cancelPost",
        "summary": "Cancel queued or scheduled delivery",
        "description": "Requires posts:write. Returns 409 after delivery starts. Does not delete content already published on a social platform.",
        "tags": [
          "Posts"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "posts:write"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Post"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "x-castrook-scopes": [
          "posts:write"
        ]
      }
    },
    "/media": {
      "get": {
        "operationId": "listMedia",
        "summary": "List uploaded media",
        "description": "Requires media:read. Private assets scoped to workspace and mode. Cached recovery read, exempt from monthly API usage.",
        "tags": [
          "Media"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "media:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MediaAsset"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/PageMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "parameters": [
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 4096
            },
            "description": "Opaque cursor returned by the preceding response."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "profile_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^prf_[a-f0-9]{24}$"
            }
          }
        ],
        "x-castrook-scopes": [
          "media:read"
        ]
      }
    },
    "/media/uploads": {
      "post": {
        "operationId": "createMediaUpload",
        "summary": "Create a direct upload",
        "description": "Requires media:write. Reserve capacity and receive a short-lived private PUT ticket, valid for up to 15 minutes. Upload the exact file bytes with returned headers directly to storage; never send your API key there. Then call completion. Videos are limited to 128 MiB; still images must also fit the completion bounds of 20 MiB and 24 megapixels. Live uploads count new bytes once; a managed asset reused across destinations is not charged again. Test-mode media is isolated from live assets.",
        "tags": [
          "Media"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "media:write"
            ]
          }
        ],
        "responses": {
          "201": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/MediaUploadTicket"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateMediaUpload"
              }
            }
          }
        },
        "x-castrook-scopes": [
          "media:write"
        ]
      }
    },
    "/media/{id}": {
      "get": {
        "operationId": "getMedia",
        "summary": "Retrieve uploaded media metadata",
        "description": "Requires media:read. A ready asset can be used through media_items.asset_id. Unreferenced ready assets expire after 30 days; accepted scheduled work pins assets through terminal delivery, followed by at least a seven-day recovery window. Cached recovery read, exempt from monthly API usage.",
        "tags": [
          "Media"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "media:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/MediaAsset"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "x-castrook-scopes": [
          "media:read"
        ]
      },
      "delete": {
        "operationId": "deleteMedia",
        "summary": "Delete unused uploaded media",
        "description": "Requires media:write. Returns 409 media_in_use while an accepted post references the asset. Deletion is asynchronous; a deleting asset is no longer usable. Recovery operation, exempt from monthly API usage.",
        "tags": [
          "Media"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "media:write"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/DeletedResource"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "x-castrook-scopes": [
          "media:write"
        ]
      }
    },
    "/media/{id}/complete": {
      "post": {
        "operationId": "completeMediaUpload",
        "summary": "Verify and freeze an uploaded file",
        "description": "Requires media:write. No request body is needed. Verifies uploaded size, media type, SHA-256 and decodable image/video metadata, then freezes verified bytes at a separate immutable key. Still images must fully decode within 20 MiB and 24 megapixels; animated images are unsupported. A pending ticket is not publishable. Completion is safe to repeat once ready, does not charge twice, and remains available after the monthly API allowance is used. Unsupported or malformed files fail with 422; checksum/type/size mismatches never become ready assets.",
        "tags": [
          "Media"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "media:write"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/MediaAsset"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "x-castrook-scopes": [
          "media:write"
        ]
      }
    },
    "/media/{id}/content": {
      "get": {
        "operationId": "downloadMedia",
        "summary": "Read private media bytes",
        "description": "Requires media:read. Authenticated streaming preview; never a public asset URL. Supports one byte range. Exempt from monthly API usage. Standard authentication and per-minute rate limits apply. Bearer keys determine mode; the mode query is for authenticated dashboard previews only.",
        "tags": [
          "Media"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "media:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "One byte range, for example bytes=0-1023."
          }
        ],
        "responses": {
          "200": {
            "description": "Complete file or headers",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Content-Type": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "image/jpeg",
                    "image/png",
                    "image/webp",
                    "video/mp4",
                    "video/quicktime",
                    "video/webm"
                  ]
                }
              },
              "Content-Length": {
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "Accept-Ranges": {
                "schema": {
                  "type": "string",
                  "const": "bytes"
                }
              },
              "Content-Range": {
                "schema": {
                  "type": "string"
                }
              },
              "ETag": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "206": {
            "description": "Requested byte range or headers",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Content-Type": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "image/jpeg",
                    "image/png",
                    "image/webp",
                    "video/mp4",
                    "video/quicktime",
                    "video/webm"
                  ]
                }
              },
              "Content-Length": {
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "Accept-Ranges": {
                "schema": {
                  "type": "string",
                  "const": "bytes"
                }
              },
              "Content-Range": {
                "schema": {
                  "type": "string"
                }
              },
              "ETag": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          },
          "416": {
            "description": "Invalid or unsatisfiable byte range. Empty body.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Content-Range": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "x-castrook-scopes": [
          "media:read"
        ]
      },
      "head": {
        "operationId": "headMedia",
        "summary": "Read private media headers",
        "description": "Requires media:read. Authenticated streaming preview; never a public asset URL. Supports one byte range. Exempt from monthly API usage. Standard authentication and per-minute rate limits apply. Bearer keys determine mode; the mode query is for authenticated dashboard previews only.",
        "tags": [
          "Media"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "media:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "One byte range, for example bytes=0-1023."
          }
        ],
        "responses": {
          "200": {
            "description": "Complete file or headers",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Content-Type": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "image/jpeg",
                    "image/png",
                    "image/webp",
                    "video/mp4",
                    "video/quicktime",
                    "video/webm"
                  ]
                }
              },
              "Content-Length": {
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "Accept-Ranges": {
                "schema": {
                  "type": "string",
                  "const": "bytes"
                }
              },
              "Content-Range": {
                "schema": {
                  "type": "string"
                }
              },
              "ETag": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "206": {
            "description": "Requested byte range or headers",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Content-Type": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "image/jpeg",
                    "image/png",
                    "image/webp",
                    "video/mp4",
                    "video/quicktime",
                    "video/webm"
                  ]
                }
              },
              "Content-Length": {
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "Accept-Ranges": {
                "schema": {
                  "type": "string",
                  "const": "bytes"
                }
              },
              "Content-Range": {
                "schema": {
                  "type": "string"
                }
              },
              "ETag": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          },
          "416": {
            "description": "Invalid or unsatisfiable byte range. Empty body.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Content-Range": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "x-castrook-scopes": [
          "media:read"
        ]
      }
    },
    "/analytics/posts": {
      "get": {
        "operationId": "listPostAnalytics",
        "summary": "List post performance snapshots",
        "description": "Requires analytics:read. Filters select the post creation range, not the measurement window. Inspect each row reporting_period: Pinterest snapshots use a server-selected UTC date range of at most 90 days clipped to post creation, with IMPRESSION mapped to views and SAVE mapped to saves; other supported providers use lifetime snapshots. Summary totals can combine reporting periods and must not be presented as lifetime or engagement earned in the filter range. Defaults to the last 30 UTC days, bounded to one year. Cached recovery read, exempt from monthly API usage. Missing metrics stay unavailable, not zero; test-mode counters are explicitly simulated. Permissions, format or visibility can limit availability. YouTube API data is redacted after disconnect/revocation or expiry. Live YouTube performance also requires explicit current account-owner privacy acceptance; otherwise rows report privacy_consent_required and no statistics are fetched. See POST /accounts/{id}/analytics-consent.",
        "tags": [
          "Analytics"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "analytics:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AnalyticsRow"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/PageMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "parameters": [
          {
            "name": "profile_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^prf_[a-f0-9]{24}$"
            }
          },
          {
            "name": "account_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 200
            }
          },
          {
            "name": "platform",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "tiktok",
                "instagram",
                "facebook",
                "youtube",
                "pinterest",
                "linkedin"
              ]
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 40,
              "description": "ISO timestamp or YYYY-MM-DD. A date-only to value includes that entire UTC day."
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 40,
              "description": "ISO timestamp or YYYY-MM-DD. A date-only to value includes that entire UTC day."
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 4096
            },
            "description": "Opaque cursor returned by the preceding response."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          }
        ],
        "x-castrook-scopes": [
          "analytics:read"
        ]
      }
    },
    "/analytics/summary": {
      "get": {
        "operationId": "getAnalyticsSummary",
        "summary": "Aggregate post performance snapshots",
        "description": "Requires analytics:read. Filters select the post creation range, not the measurement window. Inspect each row reporting_period: Pinterest snapshots use a server-selected UTC date range of at most 90 days clipped to post creation, with IMPRESSION mapped to views and SAVE mapped to saves; other supported providers use lifetime snapshots. Summary totals can combine reporting periods and must not be presented as lifetime or engagement earned in the filter range. Defaults to the last 30 UTC days, bounded to one year. Cached recovery read, exempt from monthly API usage. Missing metrics stay unavailable, not zero; test-mode counters are explicitly simulated. Permissions, format or visibility can limit availability. YouTube API data is redacted after disconnect/revocation or expiry. Live YouTube performance also requires explicit current account-owner privacy acceptance; otherwise rows report privacy_consent_required and no statistics are fetched. See POST /accounts/{id}/analytics-consent. Coverage shows how many destinations returned each metric; an entirely missing metric has a null total. A range with over 5,000 destinations returns 422 analytics_range_too_large; it never silently truncates totals.",
        "tags": [
          "Analytics"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "analytics:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AnalyticsSummary"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "parameters": [
          {
            "name": "profile_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^prf_[a-f0-9]{24}$"
            }
          },
          {
            "name": "account_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 200
            }
          },
          {
            "name": "platform",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "tiktok",
                "instagram",
                "facebook",
                "youtube",
                "pinterest",
                "linkedin"
              ]
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 40,
              "description": "ISO timestamp or YYYY-MM-DD. A date-only to value includes that entire UTC day."
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 40,
              "description": "ISO timestamp or YYYY-MM-DD. A date-only to value includes that entire UTC day."
            }
          }
        ],
        "x-castrook-scopes": [
          "analytics:read"
        ]
      }
    },
    "/posts/{id}/analytics/refresh": {
      "post": {
        "operationId": "refreshPostAnalytics",
        "summary": "Refresh post performance",
        "description": "Requires analytics:write. Refreshes available published destinations using current granted access. Within the five-minute cooldown it returns cached:true; an active refresh lease returns 409. Fresh refreshes are metered API operations. Provider failure preserves the last successful snapshot and marks refresh_error; it never fabricates counts. Missing scopes or unsupported metrics are represented as unavailable. Scheduled refreshes are bounded.",
        "tags": [
          "Analytics"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "analytics:write"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AnalyticsRefresh"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {},
                "additionalProperties": false
              }
            }
          }
        },
        "x-castrook-scopes": [
          "analytics:write"
        ]
      }
    },
    "/comments": {
      "get": {
        "operationId": "listComments",
        "summary": "List comments for a published destination",
        "description": "Requires comments:read. Live requests also count toward the monthly comment request allowance. post_id is a Castrook post ID; account_id identifies its published destination. Live results contain up to 50 top-level comments per page. List a comment before replying to it. Pinterest organic comment management is unsupported. LinkedIn comments require granted Community Management permissions; self-service member sharing does not grant comment read access. TikTok general comment management is not available via Content Posting API. YouTube requires active authorization and a current video record; revoked access, provider deletion, unrefreshable expired records, private videos, and disabled comments can prevent access.",
        "tags": [
          "Comments"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "comments:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Comment"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/PageMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "parameters": [
          {
            "name": "account_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 100
            }
          },
          {
            "name": "post_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 4096
            }
          }
        ],
        "x-castrook-scopes": [
          "comments:read"
        ]
      },
      "post": {
        "operationId": "replyComment",
        "summary": "Reply to a comment",
        "description": "Requires comments:write. Live requests also count toward the monthly comment request allowance. A real reply is sent to the connected platform. Reply text is trimmed. Do not blindly retry on ambiguous network failures.",
        "tags": [
          "Comments"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "comments:write"
            ]
          }
        ],
        "responses": {
          "201": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Comment"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "account_id": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 100
                  },
                  "post_id": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 100
                  },
                  "comment_id": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 256
                  },
                  "text": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 2000,
                    "pattern": "\\S"
                  }
                },
                "required": [
                  "account_id",
                  "post_id",
                  "comment_id",
                  "text"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "x-castrook-scopes": [
          "comments:write"
        ]
      }
    },
    "/webhooks": {
      "get": {
        "operationId": "listWebhooks",
        "summary": "List event endpoints",
        "description": "Requires webhooks:read. Secrets are never included.",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "webhooks:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Webhook"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/PageMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "parameters": [
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 4096
            },
            "description": "Opaque cursor returned by the preceding response."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          }
        ],
        "x-castrook-scopes": [
          "webhooks:read"
        ]
      },
      "post": {
        "operationId": "createWebhook",
        "summary": "Create an event endpoint",
        "description": "Requires webhooks:write. Store the returned signing secret now; it cannot be retrieved later. Deliveries are at least once; deduplicate webhook-id.",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "webhooks:write"
            ]
          }
        ],
        "responses": {
          "201": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WebhookCreated"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "maxLength": 2048,
                    "pattern": "^https://",
                    "description": "Public HTTPS endpoint; private networks and redirects are rejected."
                  },
                  "events": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "post.published",
                        "post.failed",
                        "post.partially_failed",
                        "post.canceled"
                      ]
                    },
                    "minItems": 1,
                    "maxItems": 6,
                    "uniqueItems": true
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 120
                  }
                },
                "required": [
                  "url",
                  "events"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "x-castrook-scopes": [
          "webhooks:write"
        ]
      }
    },
    "/webhooks/{id}": {
      "delete": {
        "operationId": "disableWebhook",
        "summary": "Disable an event endpoint",
        "description": "Requires webhooks:write. Stops new and pending deliveries.",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "webhooks:write"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Webhook"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "x-castrook-scopes": [
          "webhooks:write"
        ]
      }
    },
    "/deliveries": {
      "get": {
        "operationId": "listDeliveries",
        "summary": "Inspect webhook delivery attempts",
        "description": "Requires webhooks:read. 2xx succeeds; transient network/408/429/5xx failures retry with bounded backoff, other 4xx are terminal.",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "webhooks:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Delivery"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/PageMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "parameters": [
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 4096
            },
            "description": "Opaque cursor returned by the preceding response."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "webhook_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "x-castrook-scopes": [
          "webhooks:read"
        ]
      }
    },
    "/logs": {
      "get": {
        "operationId": "listRequestLogs",
        "summary": "List API request logs",
        "description": "Requires logs:read. Logs include request IDs, status and duration; they exclude keys and request bodies.",
        "tags": [
          "Logs"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "logs:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/RequestLog"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/PageMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "parameters": [
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 4096
            },
            "description": "Opaque cursor returned by the preceding response."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          }
        ],
        "x-castrook-scopes": [
          "logs:read"
        ]
      }
    },
    "/usage": {
      "get": {
        "operationId": "getUsage",
        "summary": "Get current usage and allowances",
        "description": "Any active key, without an additional scope, or a dashboard session. Reports the key mode only: live keys see live allowances; test keys see the separate test allowances. Does not consume the monthly API allowance; authentication, scope and per-minute limits still apply. used counts completed work; reserved holds one publication per accepted destination not yet published, in the period it is scheduled for; remaining is max(0, limit - used - reserved). Metrics that do not apply to test mode are null.",
        "tags": [
          "Usage"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "usage:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/UsageSummary"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "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"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableContent"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "x-castrook-scopes": [
          "usage:read"
        ]
      }
    }
  },
  "components": {
    "headers": {
      "RequestId": {
        "description": "Use this ID when investigating a request.",
        "schema": {
          "type": "string"
        }
      },
      "RateLimitLimit": {
        "description": "Requests allowed per minute: the plan rate for API keys (per workspace and mode), or the dashboard session rate.",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimitRemaining": {
        "description": "Requests left in the current one-minute window.",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      },
      "RateLimitReset": {
        "description": "Unix time, in seconds, when the current one-minute window resets.",
        "schema": {
          "type": "integer"
        }
      },
      "RetryAfter": {
        "description": "Seconds to wait before retrying. For usage_limit_exceeded, the time until the allowance resets.",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Invalid request or cursor",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimitLimit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimitRemaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimitReset"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing, invalid or revoked key",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimitLimit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimitRemaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimitReset"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Forbidden": {
        "description": "Insufficient scope or wrong environment",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimitLimit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimitRemaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimitReset"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource does not exist in this workspace",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimitLimit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimitRemaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimitReset"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Conflict": {
        "description": "Idempotency conflict, invalid state transition, or an account limit: account_limit_reached (connecting beyond the plan, or beyond 10 test accounts) or account_capacity_exceeded (the workspace has more connected live accounts than its plan allows; new live publications are blocked until accounts are disconnected or the plan is upgraded; nothing is deleted).",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimitLimit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimitRemaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimitReset"
          },
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/AccountLimitError"
                },
                {
                  "$ref": "#/components/schemas/Error"
                }
              ]
            }
          }
        }
      },
      "PayloadTooLarge": {
        "description": "JSON body exceeds 64 KiB",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimitLimit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimitRemaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimitReset"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "UnsupportedMediaType": {
        "description": "Content-Type must be application/json",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimitLimit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimitRemaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimitReset"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "UnprocessableContent": {
        "description": "Platform or content validation failed",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimitLimit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimitRemaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimitReset"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "rate_limit_exceeded: the per-minute rate was reached; retry after Retry-After. usage_limit_exceeded: a monthly allowance is used; this request accepted no new work; previously accepted work remains recoverable; do not retry a blocked new operation before details.reset_at. platform_rate_limited: the connected platform throttled the request.",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimitLimit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimitRemaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimitReset"
          },
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/UsageLimitError"
                },
                {
                  "$ref": "#/components/schemas/RateLimitError"
                },
                {
                  "$ref": "#/components/schemas/Error"
                }
              ]
            }
          }
        }
      },
      "BadGateway": {
        "description": "Platform request failed",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimitLimit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimitRemaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimitReset"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ServiceUnavailable": {
        "description": "Service or integration unavailable",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimitLimit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimitRemaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimitReset"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Gone": {
        "description": "An upload or hosted connection capability expired or was revoked. Request a new one.",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimitLimit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimitRemaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimitReset"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "cr_test_* or cr_live_*",
        "description": "Workspace- and mode-scoped API key. Required scopes are documented on every operation; usage is implicitly readable for raw API keys."
      },
      "oauth2": {
        "type": "oauth2",
        "description": "Public authorization code client with required S256 PKCE and resource=https://castrook.com/api/v1. Dynamic registration /oauth/register; no client secret. Explicit scoped owner consent; access token expires in 3600 seconds; refresh rotates with a fixed 30-day grant expiry.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://castrook.com/oauth/authorize",
            "tokenUrl": "https://castrook.com/oauth/token",
            "refreshUrl": "https://castrook.com/oauth/token",
            "scopes": {
              "accounts:read": "Accounts read",
              "accounts:write": "Accounts write",
              "posts:read": "Posts read",
              "posts:write": "Posts write",
              "profiles:read": "Profiles read",
              "profiles:write": "Profiles write",
              "media:read": "Media read",
              "media:write": "Media write",
              "analytics:read": "Analytics read",
              "analytics:write": "Analytics write",
              "comments:read": "Comments read",
              "comments:write": "Comments write",
              "webhooks:read": "Webhooks read",
              "webhooks:write": "Webhooks write",
              "logs:read": "Logs read",
              "usage:read": "Usage read"
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "details": {}
            },
            "required": [
              "code",
              "message"
            ]
          },
          "request_id": {
            "type": "string"
          }
        },
        "required": [
          "error",
          "request_id"
        ]
      },
      "PageMeta": {
        "type": "object",
        "properties": {
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          },
          "has_more": {
            "type": "boolean"
          }
        },
        "required": [
          "next_cursor",
          "has_more"
        ]
      },
      "Account": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "profile_id": {
            "type": "string",
            "pattern": "^prf_[a-f0-9]{24}$"
          },
          "platform": {
            "type": "string",
            "enum": [
              "tiktok",
              "instagram",
              "facebook",
              "youtube",
              "pinterest",
              "linkedin"
            ]
          },
          "name": {
            "type": "string"
          },
          "username": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "connected",
              "disconnected",
              "expired"
            ]
          },
          "capabilities": {
            "type": "object",
            "properties": {
              "publish": {
                "type": "boolean"
              },
              "comments": {
                "type": "boolean"
              },
              "analytics": {
                "type": "boolean",
                "description": "Derived from granted permissions, not a guarantee that every post exposes metrics."
              }
            },
            "required": [
              "publish",
              "comments"
            ]
          },
          "mode": {
            "type": "string",
            "enum": [
              "test",
              "live"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "account_type": {
            "type": "string",
            "enum": [
              "member",
              "organization",
              "business"
            ],
            "description": "LinkedIn personal profile or administered organization, or Pinterest Business account."
          },
          "access_tier": {
            "type": "string",
            "enum": [
              "trial",
              "standard"
            ],
            "description": "Pinterest access tier from trusted server configuration. Trial uses Pinterest Sandbox; this field never proves customer-wide provider approval."
          },
          "analytics_consent": {
            "type": "object",
            "properties": {
              "privacy_policy_version": {
                "type": "string"
              },
              "accepted_at": {
                "type": "string",
                "format": "date-time"
              }
            },
            "required": [
              "privacy_policy_version",
              "accepted_at"
            ],
            "description": "Explicit acceptance of the current privacy policy, bound to this authorization. Omitted when acceptance is missing or outdated."
          }
        },
        "required": [
          "id",
          "platform",
          "name",
          "username",
          "status",
          "capabilities",
          "mode",
          "created_at",
          "updated_at"
        ]
      },
      "Media": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "pattern": "^https://",
            "description": "Public HTTPS URL without credentials, fragments, custom ports or private-network addresses. Fetches never follow redirects. Keep externally hosted bytes unchanged and available through dispatch. Direct uploads with asset_id provide immutable scheduled media instead."
          },
          "type": {
            "type": "string",
            "enum": [
              "video",
              "image"
            ]
          }
        },
        "required": [
          "url",
          "type"
        ],
        "additionalProperties": false,
        "description": "Legacy single URL. Use media_items for uploaded assets and carousels."
      },
      "MediaItem": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "pattern": "^https://",
            "description": "Public HTTPS URL without credentials, fragments, custom ports or private-network addresses. Fetches never follow redirects. Keep externally hosted bytes unchanged and available through dispatch. Direct uploads with asset_id provide immutable scheduled media instead."
          },
          "asset_id": {
            "type": "string",
            "pattern": "^ast_[a-f0-9]{24}$"
          },
          "type": {
            "type": "string",
            "enum": [
              "video",
              "image"
            ]
          }
        },
        "required": [
          "type"
        ],
        "additionalProperties": false,
        "oneOf": [
          {
            "required": [
              "url"
            ],
            "not": {
              "required": [
                "asset_id"
              ]
            }
          },
          {
            "required": [
              "asset_id"
            ],
            "not": {
              "required": [
                "url"
              ]
            }
          }
        ]
      },
      "TikTokOptions": {
        "type": "object",
        "properties": {
          "privacy_level": {
            "type": "string",
            "enum": [
              "PUBLIC_TO_EVERYONE",
              "MUTUAL_FOLLOW_FRIENDS",
              "FOLLOWER_OF_CREATOR",
              "SELF_ONLY"
            ],
            "description": "Must be explicitly selected from current GET /accounts/{id}/publishing-options privacy_level_options; no default."
          },
          "consent": {
            "type": "boolean",
            "const": true,
            "description": "Caller attests the creator explicitly approved this exact media, caption, destination, privacy, disclosures and schedule after preview and required policy notices. OAuth or an API key alone is not per-post consent. Never hard-code approval for unattended content."
          },
          "video_duration_sec": {
            "type": "number",
            "exclusiveMinimum": 0,
            "maximum": 3600,
            "description": "Optional compatibility hint. Castrook measures the staged video bytes and enforces the current creator duration limit; this value cannot override that check."
          },
          "disable_comment": {
            "type": "boolean",
            "default": true
          },
          "disable_duet": {
            "type": "boolean",
            "default": true
          },
          "disable_stitch": {
            "type": "boolean",
            "default": true
          },
          "brand_content_toggle": {
            "type": "boolean",
            "default": false,
            "description": "Creator disclosed third-party paid partnership; incompatible with SELF_ONLY. Your UI must show the paid-partnership label notice and the Branded Content Policy with music-use confirmation."
          },
          "brand_organic_toggle": {
            "type": "boolean",
            "default": false,
            "description": "Creator disclosed promotion of their own business. Your UI must show the corresponding promotional-content label notice."
          },
          "is_aigc": {
            "type": "boolean",
            "default": false
          },
          "video_cover_timestamp_ms": {
            "type": "integer",
            "minimum": 0
          },
          "format": {
            "type": "string",
            "enum": [
              "feed",
              "video"
            ]
          },
          "title": {
            "type": "string",
            "maxLength": 90,
            "description": "Photo post title; caption becomes the photo description."
          },
          "photo_cover_index": {
            "type": "integer",
            "minimum": 0,
            "maximum": 34,
            "default": 0
          },
          "auto_add_music": {
            "type": "boolean",
            "default": false,
            "description": "Photo posts only. Creator must approve music use."
          }
        },
        "required": [
          "privacy_level",
          "consent"
        ],
        "additionalProperties": false,
        "not": {
          "required": [
            "brand_content_toggle",
            "privacy_level"
          ],
          "properties": {
            "brand_content_toggle": {
              "const": true
            },
            "privacy_level": {
              "const": "SELF_ONLY"
            }
          }
        }
      },
      "TikTokPublishingOptions": {
        "type": "object",
        "properties": {
          "platform": {
            "type": "string",
            "const": "tiktok"
          },
          "creator_username": {
            "type": "string"
          },
          "creator_nickname": {
            "type": "string"
          },
          "privacy_level_options": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "PUBLIC_TO_EVERYONE",
                "MUTUAL_FOLLOW_FRIENDS",
                "FOLLOWER_OF_CREATOR",
                "SELF_ONLY"
              ],
              "description": "Must be explicitly selected from current GET /accounts/{id}/publishing-options privacy_level_options; no default."
            }
          },
          "comment_disabled": {
            "type": "boolean"
          },
          "duet_disabled": {
            "type": "boolean"
          },
          "stitch_disabled": {
            "type": "boolean"
          },
          "max_video_post_duration_sec": {
            "type": "number",
            "exclusiveMinimum": 0
          }
        },
        "required": [
          "platform",
          "creator_username",
          "creator_nickname",
          "privacy_level_options",
          "comment_disabled",
          "duet_disabled",
          "stitch_disabled",
          "max_video_post_duration_sec"
        ]
      },
      "YouTubeOptions": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "pattern": "\\S",
            "description": "Defaults to the first 100 characters of the trimmed caption. A nonblank title is required."
          },
          "privacy_status": {
            "type": "string",
            "enum": [
              "private",
              "unlisted",
              "public"
            ],
            "default": "private"
          },
          "made_for_kids": {
            "type": "boolean",
            "description": "Explicit audience designation."
          },
          "contains_synthetic_media": {
            "type": "boolean",
            "default": false
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 100
            },
            "maxItems": 20
          },
          "category_id": {
            "type": "string",
            "pattern": "^[0-9]{1,3}$",
            "default": "22"
          },
          "format": {
            "type": "string",
            "const": "video"
          }
        },
        "required": [
          "made_for_kids"
        ],
        "additionalProperties": false
      },
      "InstagramOptions": {
        "type": "object",
        "properties": {
          "format": {
            "type": "string",
            "enum": [
              "feed",
              "reel",
              "story"
            ]
          },
          "share_to_feed": {
            "type": "boolean",
            "default": true
          },
          "alt_text": {
            "type": "string",
            "maxLength": 1000
          }
        },
        "additionalProperties": false
      },
      "FacebookOptions": {
        "type": "object",
        "properties": {
          "format": {
            "type": "string",
            "enum": [
              "feed",
              "reel",
              "story",
              "video"
            ],
            "description": "Format precedence: destination.format, destination.options.format, shared format, then the selected platform's platform_options.format. Every supplied option format must be valid for that platform, even when a higher-precedence format wins. Omitted formats preserve existing platform defaults."
          },
          "title": {
            "type": "string",
            "maxLength": 100
          }
        },
        "additionalProperties": false
      },
      "PinterestBoard": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[0-9]{1,40}$",
            "description": "Numeric Pinterest ID returned by board/section discovery; preserve it as a string."
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "privacy": {
            "type": "string"
          },
          "owner": {
            "type": "object",
            "properties": {
              "username": {
                "type": "string"
              }
            },
            "required": [
              "username"
            ]
          }
        },
        "required": [
          "id",
          "name"
        ]
      },
      "PinterestBoardSection": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[0-9]{1,40}$",
            "description": "Numeric Pinterest ID returned by board/section discovery; preserve it as a string."
          },
          "name": {
            "type": "string"
          },
          "board_id": {
            "type": "string",
            "pattern": "^[0-9]{1,40}$",
            "description": "Numeric Pinterest ID returned by board/section discovery; preserve it as a string."
          }
        },
        "required": [
          "id",
          "name",
          "board_id"
        ]
      },
      "PinterestOptions": {
        "type": "object",
        "properties": {
          "board_id": {
            "type": "string",
            "pattern": "^[0-9]{1,40}$",
            "description": "Numeric Pinterest ID returned by board/section discovery; preserve it as a string."
          },
          "board_section_id": {
            "type": "string",
            "pattern": "^[0-9]{1,40}$",
            "description": "Numeric Pinterest ID returned by board/section discovery; preserve it as a string."
          },
          "format": {
            "type": "string",
            "enum": [
              "feed",
              "video"
            ]
          },
          "title": {
            "type": "string",
            "maxLength": 100
          },
          "link": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "pattern": "^https://",
            "description": "Public HTTPS URL without credentials, fragments, custom ports or private-network addresses. Fetches never follow redirects. Keep externally hosted bytes unchanged and available through dispatch. Direct uploads with asset_id provide immutable scheduled media instead."
          },
          "alt_text": {
            "type": "string",
            "maxLength": 500
          },
          "cover_image_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "pattern": "^https://",
            "description": "Video Pins only. Public HTTPS still image used as the cover."
          },
          "cover_image_key_frame_time": {
            "type": "integer",
            "minimum": 0,
            "maximum": 3600,
            "description": "Video Pins only. Cover frame timestamp in seconds; mutually exclusive with cover_image_url."
          }
        },
        "required": [
          "board_id"
        ],
        "additionalProperties": false,
        "not": {
          "required": [
            "cover_image_url",
            "cover_image_key_frame_time"
          ]
        }
      },
      "LinkedInOptions": {
        "type": "object",
        "properties": {
          "format": {
            "type": "string",
            "enum": [
              "feed",
              "video"
            ]
          },
          "visibility": {
            "type": "string",
            "enum": [
              "PUBLIC",
              "CONNECTIONS"
            ],
            "default": "PUBLIC",
            "description": "CONNECTIONS is available for member connections only; organizations require PUBLIC."
          },
          "title": {
            "type": "string",
            "maxLength": 200,
            "description": "Optional media title for single-image/video posts only. Rejected for text-only, article and multi-image posts; articles use article_title."
          },
          "alt_text": {
            "type": "string",
            "maxLength": 4086,
            "description": "Single-image alternative text. For ordered images use alt_texts. Not accepted for article thumbnails."
          },
          "alt_texts": {
            "type": "array",
            "items": {
              "type": "string",
              "maxLength": 4086
            },
            "maxItems": 20,
            "description": "Ordered alternative text; length must equal the destination image count. Not accepted for article thumbnails."
          },
          "article_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "pattern": "^https://",
            "description": "Public HTTPS URL without credentials, fragments, custom ports or private-network addresses. Fetches never follow redirects. Keep externally hosted bytes unchanged and available through dispatch. Direct uploads with asset_id provide immutable scheduled media instead."
          },
          "article_title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "pattern": "\\S",
            "description": "Trimmed nonblank article title."
          },
          "article_description": {
            "type": "string",
            "maxLength": 4000
          }
        },
        "additionalProperties": false,
        "dependentRequired": {
          "article_url": [
            "article_title"
          ],
          "article_title": [
            "article_url"
          ],
          "article_description": [
            "article_url"
          ]
        },
        "not": {
          "required": [
            "alt_text",
            "alt_texts"
          ]
        }
      },
      "TikTokTestOptions": {
        "type": "object",
        "properties": {
          "privacy_level": {
            "type": "string",
            "enum": [
              "PUBLIC_TO_EVERYONE",
              "MUTUAL_FOLLOW_FRIENDS",
              "FOLLOWER_OF_CREATOR",
              "SELF_ONLY"
            ],
            "description": "Must be explicitly selected from current GET /accounts/{id}/publishing-options privacy_level_options; no default."
          },
          "consent": {
            "type": "boolean",
            "const": true,
            "description": "Caller attests the creator explicitly approved this exact media, caption, destination, privacy, disclosures and schedule after preview and required policy notices. OAuth or an API key alone is not per-post consent. Never hard-code approval for unattended content."
          },
          "video_duration_sec": {
            "type": "number",
            "exclusiveMinimum": 0,
            "maximum": 3600,
            "description": "Optional compatibility hint. Castrook measures the staged video bytes and enforces the current creator duration limit; this value cannot override that check."
          },
          "disable_comment": {
            "type": "boolean",
            "default": true
          },
          "disable_duet": {
            "type": "boolean",
            "default": true
          },
          "disable_stitch": {
            "type": "boolean",
            "default": true
          },
          "brand_content_toggle": {
            "type": "boolean",
            "default": false,
            "description": "Creator disclosed third-party paid partnership; incompatible with SELF_ONLY. Your UI must show the paid-partnership label notice and the Branded Content Policy with music-use confirmation."
          },
          "brand_organic_toggle": {
            "type": "boolean",
            "default": false,
            "description": "Creator disclosed promotion of their own business. Your UI must show the corresponding promotional-content label notice."
          },
          "is_aigc": {
            "type": "boolean",
            "default": false
          },
          "video_cover_timestamp_ms": {
            "type": "integer",
            "minimum": 0
          },
          "format": {
            "type": "string",
            "enum": [
              "feed",
              "video"
            ]
          },
          "title": {
            "type": "string",
            "maxLength": 90,
            "description": "Photo post title; caption becomes the photo description."
          },
          "photo_cover_index": {
            "type": "integer",
            "minimum": 0,
            "maximum": 34,
            "default": 0
          },
          "auto_add_music": {
            "type": "boolean",
            "default": false,
            "description": "Photo posts only. Creator must approve music use."
          },
          "sandbox_outcome": {
            "type": "string",
            "enum": [
              "published",
              "failed"
            ],
            "description": "Test keys only. Forces a deterministic destination result; no external publication."
          }
        },
        "required": [
          "sandbox_outcome"
        ],
        "additionalProperties": false
      },
      "YouTubeTestOptions": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "pattern": "\\S",
            "description": "Defaults to the first 100 characters of the trimmed caption. A nonblank title is required."
          },
          "privacy_status": {
            "type": "string",
            "enum": [
              "private",
              "unlisted",
              "public"
            ],
            "default": "private"
          },
          "made_for_kids": {
            "type": "boolean",
            "description": "Explicit audience designation."
          },
          "contains_synthetic_media": {
            "type": "boolean",
            "default": false
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 100
            },
            "maxItems": 20
          },
          "category_id": {
            "type": "string",
            "pattern": "^[0-9]{1,3}$",
            "default": "22"
          },
          "format": {
            "type": "string",
            "const": "video"
          },
          "sandbox_outcome": {
            "type": "string",
            "enum": [
              "published",
              "failed"
            ],
            "description": "Test keys only. Forces a deterministic destination result; no external publication."
          }
        },
        "required": [
          "sandbox_outcome"
        ],
        "additionalProperties": false
      },
      "InstagramTestOptions": {
        "type": "object",
        "properties": {
          "format": {
            "type": "string",
            "enum": [
              "feed",
              "reel",
              "story"
            ]
          },
          "share_to_feed": {
            "type": "boolean",
            "default": true
          },
          "alt_text": {
            "type": "string",
            "maxLength": 1000
          },
          "sandbox_outcome": {
            "type": "string",
            "enum": [
              "published",
              "failed"
            ],
            "description": "Test keys only. Forces a deterministic destination result; no external publication."
          }
        },
        "required": [
          "sandbox_outcome"
        ],
        "additionalProperties": false
      },
      "FacebookTestOptions": {
        "type": "object",
        "properties": {
          "format": {
            "type": "string",
            "enum": [
              "feed",
              "reel",
              "story",
              "video"
            ],
            "description": "Format precedence: destination.format, destination.options.format, shared format, then the selected platform's platform_options.format. Every supplied option format must be valid for that platform, even when a higher-precedence format wins. Omitted formats preserve existing platform defaults."
          },
          "title": {
            "type": "string",
            "maxLength": 100
          },
          "sandbox_outcome": {
            "type": "string",
            "enum": [
              "published",
              "failed"
            ],
            "description": "Test keys only. Forces a deterministic destination result; no external publication."
          }
        },
        "required": [
          "sandbox_outcome"
        ],
        "additionalProperties": false
      },
      "PinterestTestOptions": {
        "type": "object",
        "properties": {
          "board_id": {
            "type": "string",
            "pattern": "^[0-9]{1,40}$",
            "description": "Numeric Pinterest ID returned by board/section discovery; preserve it as a string."
          },
          "board_section_id": {
            "type": "string",
            "pattern": "^[0-9]{1,40}$",
            "description": "Numeric Pinterest ID returned by board/section discovery; preserve it as a string."
          },
          "format": {
            "type": "string",
            "enum": [
              "feed",
              "video"
            ]
          },
          "title": {
            "type": "string",
            "maxLength": 100
          },
          "link": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "pattern": "^https://",
            "description": "Public HTTPS URL without credentials, fragments, custom ports or private-network addresses. Fetches never follow redirects. Keep externally hosted bytes unchanged and available through dispatch. Direct uploads with asset_id provide immutable scheduled media instead."
          },
          "alt_text": {
            "type": "string",
            "maxLength": 500
          },
          "cover_image_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "pattern": "^https://",
            "description": "Video Pins only. Public HTTPS still image used as the cover."
          },
          "cover_image_key_frame_time": {
            "type": "integer",
            "minimum": 0,
            "maximum": 3600,
            "description": "Video Pins only. Cover frame timestamp in seconds; mutually exclusive with cover_image_url."
          },
          "sandbox_outcome": {
            "type": "string",
            "enum": [
              "published",
              "failed"
            ],
            "description": "Test keys only. Forces a deterministic destination result; no external publication."
          }
        },
        "required": [
          "sandbox_outcome"
        ],
        "additionalProperties": false
      },
      "LinkedInTestOptions": {
        "type": "object",
        "properties": {
          "format": {
            "type": "string",
            "enum": [
              "feed",
              "video"
            ]
          },
          "visibility": {
            "type": "string",
            "enum": [
              "PUBLIC",
              "CONNECTIONS"
            ],
            "default": "PUBLIC",
            "description": "CONNECTIONS is available for member connections only; organizations require PUBLIC."
          },
          "title": {
            "type": "string",
            "maxLength": 200,
            "description": "Optional media title for single-image/video posts only. Rejected for text-only, article and multi-image posts; articles use article_title."
          },
          "alt_text": {
            "type": "string",
            "maxLength": 4086,
            "description": "Single-image alternative text. For ordered images use alt_texts. Not accepted for article thumbnails."
          },
          "alt_texts": {
            "type": "array",
            "items": {
              "type": "string",
              "maxLength": 4086
            },
            "maxItems": 20,
            "description": "Ordered alternative text; length must equal the destination image count. Not accepted for article thumbnails."
          },
          "article_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "pattern": "^https://",
            "description": "Public HTTPS URL without credentials, fragments, custom ports or private-network addresses. Fetches never follow redirects. Keep externally hosted bytes unchanged and available through dispatch. Direct uploads with asset_id provide immutable scheduled media instead."
          },
          "article_title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "pattern": "\\S",
            "description": "Trimmed nonblank article title."
          },
          "article_description": {
            "type": "string",
            "maxLength": 4000
          },
          "sandbox_outcome": {
            "type": "string",
            "enum": [
              "published",
              "failed"
            ],
            "description": "Test keys only. Forces a deterministic destination result; no external publication."
          }
        },
        "required": [
          "sandbox_outcome"
        ],
        "additionalProperties": false
      },
      "PlatformOptions": {
        "type": "object",
        "properties": {
          "tiktok": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/TikTokOptions"
              },
              {
                "$ref": "#/components/schemas/TikTokTestOptions"
              }
            ],
            "description": "Live requests use TikTokOptions. Test requests may instead supply sandbox_outcome."
          },
          "youtube": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/YouTubeOptions"
              },
              {
                "$ref": "#/components/schemas/YouTubeTestOptions"
              }
            ],
            "description": "Live requests use YouTubeOptions. Test requests may instead supply sandbox_outcome."
          },
          "instagram": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/InstagramOptions"
              },
              {
                "$ref": "#/components/schemas/InstagramTestOptions"
              }
            ],
            "description": "Live requests use InstagramOptions. Test requests may instead supply sandbox_outcome."
          },
          "facebook": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/FacebookOptions"
              },
              {
                "$ref": "#/components/schemas/FacebookTestOptions"
              }
            ],
            "description": "Live requests use FacebookOptions. Test requests may instead supply sandbox_outcome."
          },
          "pinterest": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/PinterestOptions"
              },
              {
                "$ref": "#/components/schemas/PinterestTestOptions"
              }
            ],
            "description": "Live requests use PinterestOptions. Test requests may instead supply sandbox_outcome."
          },
          "linkedin": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/LinkedInOptions"
              },
              {
                "$ref": "#/components/schemas/LinkedInTestOptions"
              }
            ],
            "description": "Live requests use LinkedInOptions. Test requests may instead supply sandbox_outcome."
          }
        },
        "additionalProperties": false
      },
      "DestinationContent": {
        "type": "object",
        "properties": {
          "account_id": {
            "type": "string",
            "pattern": "^acc_[a-f0-9]{24}$"
          },
          "text": {
            "type": "string",
            "maxLength": 5000
          },
          "media": {
            "$ref": "#/components/schemas/Media"
          },
          "media_items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MediaItem"
            },
            "maxItems": 35,
            "description": "An empty override explicitly removes inherited media."
          },
          "format": {
            "type": "string",
            "enum": [
              "feed",
              "reel",
              "story",
              "video"
            ],
            "description": "Format precedence: destination.format, destination.options.format, shared format, then the selected platform's platform_options.format. Every supplied option format must be valid for that platform, even when a higher-precedence format wins. Omitted formats preserve existing platform defaults."
          },
          "options": {
            "type": "object",
            "description": "Options for this account's platform, merged over platform_options. See the platform option schema."
          }
        },
        "required": [
          "account_id"
        ],
        "additionalProperties": false,
        "not": {
          "required": [
            "media",
            "media_items"
          ]
        }
      },
      "CreatePost": {
        "type": "object",
        "properties": {
          "text": {
            "type": "string",
            "maxLength": 5000,
            "description": "May be empty when shared or destination content supplies media, nonblank text, or a LinkedIn article_url. Instagram and TikTok video captions: 2,200 UTF-16 code units; TikTok photo descriptions: 4,000; Pinterest descriptions: 800; LinkedIn commentary: 3,000. Stories require an empty caption."
          },
          "account_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "^acc_[a-f0-9]{24}$"
            },
            "minItems": 1,
            "maxItems": 20,
            "uniqueItems": true
          },
          "profile_id": {
            "type": "string",
            "pattern": "^prf_[a-f0-9]{24}$"
          },
          "media": {
            "$ref": "#/components/schemas/Media"
          },
          "media_items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MediaItem"
            },
            "minItems": 1,
            "maxItems": 35
          },
          "format": {
            "type": "string",
            "enum": [
              "feed",
              "reel",
              "story",
              "video"
            ],
            "description": "Format precedence: destination.format, destination.options.format, shared format, then the selected platform's platform_options.format. Every supplied option format must be valid for that platform, even when a higher-precedence format wins. Omitted formats preserve existing platform defaults."
          },
          "destinations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DestinationContent"
            },
            "maxItems": 20,
            "description": "Unique selected account IDs only. Explicit fields override shared content; omitted fields inherit it. Every selected account must belong to profile_id when provided."
          },
          "scheduled_at": {
            "type": "string",
            "format": "date-time",
            "description": "RFC 3339 time including timezone, between five seconds and 365 days in the future when accepted. Omit for immediate queueing. Offsets are accepted."
          },
          "platform_options": {
            "$ref": "#/components/schemas/PlatformOptions"
          }
        },
        "required": [
          "text",
          "account_ids"
        ],
        "additionalProperties": false,
        "not": {
          "required": [
            "media",
            "media_items"
          ]
        },
        "anyOf": [
          {
            "required": [
              "media"
            ]
          },
          {
            "properties": {
              "text": {
                "pattern": "\\S"
              }
            }
          },
          {
            "required": [
              "media_items"
            ]
          },
          {
            "required": [
              "destinations"
            ],
            "properties": {
              "destinations": {
                "contains": {
                  "anyOf": [
                    {
                      "required": [
                        "text"
                      ],
                      "properties": {
                        "text": {
                          "pattern": "\\S"
                        }
                      }
                    },
                    {
                      "required": [
                        "media"
                      ]
                    },
                    {
                      "required": [
                        "media_items"
                      ],
                      "properties": {
                        "media_items": {
                          "minItems": 1
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          {
            "required": [
              "platform_options"
            ],
            "properties": {
              "platform_options": {
                "required": [
                  "linkedin"
                ],
                "properties": {
                  "linkedin": {
                    "required": [
                      "article_url"
                    ]
                  }
                }
              }
            }
          },
          {
            "required": [
              "destinations"
            ],
            "properties": {
              "destinations": {
                "contains": {
                  "required": [
                    "options"
                  ],
                  "properties": {
                    "options": {
                      "required": [
                        "article_url"
                      ]
                    }
                  }
                }
              }
            }
          }
        ]
      },
      "PostTarget": {
        "type": "object",
        "properties": {
          "account_id": {
            "type": "string"
          },
          "platform": {
            "type": "string",
            "enum": [
              "tiktok",
              "instagram",
              "facebook",
              "youtube",
              "pinterest",
              "linkedin"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "publishing",
              "published",
              "failed",
              "canceled"
            ]
          },
          "attempts": {
            "type": "integer",
            "minimum": 0
          },
          "provider_post_id": {
            "type": "string",
            "description": "Optional provider identifier. YouTube identifiers are refreshed while connected and can be removed after revocation, provider deletion, or expiry."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Optional provider URL; may be removed when its corresponding provider data is no longer retained."
          },
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            },
            "required": [
              "code",
              "message"
            ]
          }
        },
        "required": [
          "account_id",
          "platform",
          "status",
          "attempts"
        ]
      },
      "Post": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "profile_id": {
            "type": "string",
            "pattern": "^prf_[a-f0-9]{24}$"
          },
          "text": {
            "type": "string"
          },
          "media": {
            "$ref": "#/components/schemas/Media"
          },
          "media_items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MediaItem"
            }
          },
          "format": {
            "type": "string",
            "enum": [
              "feed",
              "reel",
              "story",
              "video"
            ],
            "description": "Format precedence: destination.format, destination.options.format, shared format, then the selected platform's platform_options.format. Every supplied option format must be valid for that platform, even when a higher-precedence format wins. Omitted formats preserve existing platform defaults."
          },
          "destinations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DestinationContent"
            }
          },
          "platform_options": {
            "$ref": "#/components/schemas/PlatformOptions"
          },
          "status": {
            "type": "string",
            "enum": [
              "scheduled",
              "queued",
              "publishing",
              "published",
              "partially_failed",
              "failed",
              "canceled"
            ]
          },
          "scheduled_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "targets": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PostTarget"
            }
          },
          "mode": {
            "type": "string",
            "enum": [
              "test",
              "live"
            ]
          }
        },
        "required": [
          "id",
          "text",
          "status",
          "scheduled_at",
          "created_at",
          "updated_at",
          "targets",
          "mode"
        ]
      },
      "Comment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "text": {
            "type": "string"
          },
          "author": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "account_id": {
            "type": "string"
          },
          "post_id": {
            "type": "string"
          },
          "parent_id": {
            "type": "string"
          },
          "like_count": {
            "type": "integer"
          }
        },
        "required": [
          "id",
          "text",
          "author",
          "created_at",
          "account_id",
          "post_id"
        ]
      },
      "Webhook": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "description": {
            "type": "string"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "post.published",
                "post.failed",
                "post.partially_failed",
                "post.canceled"
              ]
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "disabled"
            ]
          },
          "mode": {
            "type": "string",
            "enum": [
              "test",
              "live"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "url",
          "description",
          "events",
          "status",
          "mode",
          "created_at"
        ]
      },
      "WebhookCreated": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Webhook"
          },
          {
            "type": "object",
            "properties": {
              "secret": {
                "type": "string",
                "description": "Returned once. Store securely; cannot be retrieved."
              }
            },
            "required": [
              "secret"
            ]
          }
        ]
      },
      "Delivery": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "webhook_id": {
            "type": "string"
          },
          "event": {
            "type": "string",
            "enum": [
              "post.published",
              "post.failed",
              "post.partially_failed",
              "post.canceled"
            ]
          },
          "post_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "delivered",
              "failed"
            ]
          },
          "attempts": {
            "type": "integer"
          },
          "last_status": {
            "type": [
              "integer",
              "null"
            ]
          },
          "last_error": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "next_attempt_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "delivered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "webhook_id",
          "event",
          "post_id",
          "status",
          "attempts",
          "last_status",
          "last_error",
          "created_at",
          "next_attempt_at",
          "delivered_at"
        ]
      },
      "RequestLog": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "request_id": {
            "type": "string"
          },
          "method": {
            "type": "string"
          },
          "path": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "duration_ms": {
            "type": "number"
          },
          "key_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "request_id",
          "method",
          "path",
          "status",
          "duration_ms",
          "key_id",
          "created_at"
        ]
      },
      "WebhookEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "post.published",
              "post.failed",
              "post.partially_failed",
              "post.canceled"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "data": {
            "$ref": "#/components/schemas/Post"
          }
        },
        "required": [
          "id",
          "type",
          "created_at",
          "data"
        ]
      },
      "Profile": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^prf_[a-f0-9]{24}$"
          },
          "name": {
            "type": "string"
          },
          "external_id": {
            "type": "string"
          },
          "mode": {
            "type": "string",
            "enum": [
              "test",
              "live"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "name",
          "mode",
          "created_at",
          "updated_at"
        ]
      },
      "CreateProfile": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "pattern": "\\S",
            "description": "Trimmed customer or brand name."
          },
          "external_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "pattern": "\\S",
            "description": "Optional unique reference from your system, scoped to this workspace and mode."
          }
        },
        "required": [
          "name"
        ],
        "additionalProperties": false
      },
      "UpdateProfile": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "pattern": "\\S",
            "description": "Trimmed customer or brand name."
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 1,
            "maxLength": 200,
            "pattern": "\\S",
            "description": "Set null to remove the external reference."
          }
        },
        "additionalProperties": false,
        "minProperties": 1
      },
      "DeletedResource": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "deleted": {
            "type": "boolean",
            "const": true
          }
        },
        "required": [
          "id",
          "deleted"
        ]
      },
      "CreateConnectSession": {
        "type": "object",
        "properties": {
          "profile_id": {
            "type": "string",
            "pattern": "^prf_[a-f0-9]{24}$"
          },
          "platforms": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "tiktok",
                "instagram",
                "facebook",
                "youtube",
                "pinterest",
                "linkedin"
              ]
            },
            "minItems": 1,
            "maxItems": 6,
            "uniqueItems": true
          },
          "return_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "pattern": "^https://",
            "description": "HTTPS URL without credentials or fragments. Local HTTP is accepted only in development."
          },
          "embed_origin": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "pattern": "^https://",
            "description": "Exact HTTPS origin, such as https://app.example.com, without a path, query or trailing slash. Only this origin may embed the portal."
          },
          "expires_in": {
            "type": "integer",
            "minimum": 60,
            "maximum": 1800,
            "default": 1800,
            "description": "Lifetime in seconds."
          }
        },
        "required": [
          "profile_id",
          "platforms"
        ],
        "additionalProperties": false
      },
      "ConnectSession": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^cns_[a-f0-9]{24}$"
          },
          "profile_id": {
            "type": "string",
            "pattern": "^prf_[a-f0-9]{24}$"
          },
          "mode": {
            "type": "string",
            "enum": [
              "test",
              "live"
            ]
          },
          "platforms": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "tiktok",
                "instagram",
                "facebook",
                "youtube",
                "pinterest",
                "linkedin"
              ]
            }
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "completed",
              "revoked",
              "expired"
            ]
          },
          "account_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "^acc_[a-f0-9]{24}$"
            }
          },
          "return_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "pattern": "^https://",
            "description": "HTTPS URL without credentials or fragments. Local HTTP is accepted only in development."
          },
          "embed_origin": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "pattern": "^https://",
            "description": "HTTPS URL without credentials or fragments. Local HTTP is accepted only in development."
          }
        },
        "required": [
          "id",
          "profile_id",
          "mode",
          "platforms",
          "expires_at",
          "created_at",
          "status",
          "account_ids"
        ]
      },
      "ConnectSessionCreated": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ConnectSession"
          },
          {
            "type": "object",
            "properties": {
              "url": {
                "type": "string",
                "format": "uri",
                "description": "Private capability URL, returned only on creation. Give it to this customer; do not put it in logs, analytics or public pages. No API key or Castrook sign-in is needed in the portal."
              }
            },
            "required": [
              "url"
            ]
          }
        ]
      },
      "CreateMediaUpload": {
        "type": "object",
        "properties": {
          "filename": {
            "type": "string",
            "minLength": 1,
            "maxLength": 180,
            "pattern": "^[^\\u0000-\\u001f\\u007f]+$",
            "description": "Trimmed display filename; control characters are rejected."
          },
          "content_type": {
            "type": "string",
            "enum": [
              "image/jpeg",
              "image/png",
              "image/webp",
              "video/mp4",
              "video/quicktime",
              "video/webm"
            ]
          },
          "size": {
            "type": "integer",
            "minimum": 1,
            "maximum": 134217728,
            "description": "Exact file size in bytes, up to 128 MiB for videos or 20 MiB for images."
          },
          "sha256": {
            "type": "string",
            "pattern": "^[a-f0-9]{64}$",
            "description": "SHA-256 of the original file bytes, encoded as lowercase hex."
          },
          "profile_id": {
            "type": "string",
            "pattern": "^prf_[a-f0-9]{24}$"
          }
        },
        "required": [
          "filename",
          "content_type",
          "size",
          "sha256"
        ],
        "additionalProperties": false,
        "allOf": [
          {
            "if": {
              "properties": {
                "content_type": {
                  "enum": [
                    "image/jpeg",
                    "image/png",
                    "image/webp"
                  ]
                }
              },
              "required": [
                "content_type"
              ]
            },
            "then": {
              "properties": {
                "size": {
                  "maximum": 20971520
                }
              }
            }
          }
        ]
      },
      "MediaAsset": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^ast_[a-f0-9]{24}$"
          },
          "profile_id": {
            "type": "string",
            "pattern": "^prf_[a-f0-9]{24}$"
          },
          "filename": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "video",
              "image"
            ]
          },
          "content_type": {
            "type": "string",
            "enum": [
              "image/jpeg",
              "image/png",
              "image/webp",
              "video/mp4",
              "video/quicktime",
              "video/webm"
            ]
          },
          "size": {
            "type": "integer",
            "minimum": 0
          },
          "sha256": {
            "type": "string",
            "pattern": "^[a-f0-9]{64}$",
            "description": "SHA-256 of the original file bytes, encoded as lowercase hex."
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "ready",
              "deleting"
            ]
          },
          "mode": {
            "type": "string",
            "enum": [
              "test",
              "live"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "in_use": {
            "type": "boolean",
            "description": "Referenced by accepted work; deletion is blocked."
          },
          "preview_url": {
            "type": "string",
            "description": "Authenticated relative content URL. Never a public storage URL."
          },
          "duration_sec": {
            "type": "number",
            "exclusiveMinimum": 0
          },
          "width": {
            "type": "integer",
            "minimum": 0
          },
          "height": {
            "type": "integer",
            "minimum": 0
          }
        },
        "required": [
          "id",
          "filename",
          "type",
          "content_type",
          "size",
          "sha256",
          "status",
          "mode",
          "created_at",
          "expires_at",
          "in_use"
        ]
      },
      "MediaUpload": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Private expiring presigned PUT URL. Send only the returned storage headers, never your Castrook key."
          },
          "method": {
            "type": "string",
            "const": "PUT"
          },
          "headers": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Send exactly these headers, including the immutable-write condition."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "url",
          "method",
          "headers",
          "expires_at"
        ]
      },
      "MediaUploadTicket": {
        "type": "object",
        "properties": {
          "asset": {
            "$ref": "#/components/schemas/MediaAsset"
          },
          "upload": {
            "$ref": "#/components/schemas/MediaUpload"
          }
        },
        "required": [
          "asset",
          "upload"
        ]
      },
      "ReportingPeriod": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "lifetime",
              "date_range"
            ]
          },
          "start_date": {
            "type": "string",
            "format": "date"
          },
          "end_date": {
            "type": "string",
            "format": "date"
          }
        },
        "required": [
          "type"
        ],
        "description": "Inspect this before comparing counters. Pinterest reports a server-selected UTC date range bounded to 90 days and post creation; other providers report lifetime snapshots when available."
      },
      "PerformanceMetrics": {
        "type": "object",
        "properties": {
          "views": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          },
          "reach": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          },
          "likes": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          },
          "comments": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          },
          "shares": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          },
          "saves": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          }
        },
        "additionalProperties": false,
        "description": "Only returned, finite, nonnegative counters for the row reporting_period. Missing fields are unavailable; a returned zero is a real zero."
      },
      "AnalyticsRow": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "post_id": {
            "type": "string"
          },
          "account_id": {
            "type": "string",
            "pattern": "^acc_[a-f0-9]{24}$"
          },
          "platform": {
            "type": "string",
            "enum": [
              "tiktok",
              "instagram",
              "facebook",
              "youtube",
              "pinterest",
              "linkedin"
            ]
          },
          "profile_id": {
            "type": "string",
            "pattern": "^prf_[a-f0-9]{24}$"
          },
          "text": {
            "type": "string"
          },
          "post_created_at": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "type": "string",
            "enum": [
              "available",
              "unavailable",
              "pending"
            ]
          },
          "metrics": {
            "$ref": "#/components/schemas/PerformanceMetrics"
          },
          "unavailable_metrics": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "views",
                "reach",
                "likes",
                "comments",
                "shares",
                "saves"
              ]
            }
          },
          "reason": {
            "type": "string"
          },
          "fetched_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "simulated": {
            "type": "boolean",
            "description": "True only for test-mode data; do not report it as real engagement."
          },
          "refresh_error": {
            "type": "string"
          },
          "reporting_period": {
            "$ref": "#/components/schemas/ReportingPeriod"
          }
        },
        "required": [
          "id",
          "post_id",
          "account_id",
          "platform",
          "text",
          "post_created_at",
          "status",
          "metrics",
          "unavailable_metrics",
          "fetched_at",
          "simulated"
        ]
      },
      "AnalyticsSummary": {
        "type": "object",
        "properties": {
          "totals": {
            "type": "object",
            "properties": {
              "views": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 0,
                "description": "null when no destination supplies this metric."
              },
              "reach": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 0,
                "description": "null when no destination supplies this metric."
              },
              "likes": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 0,
                "description": "null when no destination supplies this metric."
              },
              "comments": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 0,
                "description": "null when no destination supplies this metric."
              },
              "shares": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 0,
                "description": "null when no destination supplies this metric."
              },
              "saves": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 0,
                "description": "null when no destination supplies this metric."
              }
            },
            "required": [
              "views",
              "reach",
              "likes",
              "comments",
              "shares",
              "saves"
            ],
            "additionalProperties": false
          },
          "coverage": {
            "type": "object",
            "properties": {
              "views": {
                "type": "object",
                "properties": {
                  "available": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "total": {
                    "type": "integer",
                    "minimum": 0
                  }
                },
                "required": [
                  "available",
                  "total"
                ],
                "additionalProperties": false
              },
              "reach": {
                "type": "object",
                "properties": {
                  "available": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "total": {
                    "type": "integer",
                    "minimum": 0
                  }
                },
                "required": [
                  "available",
                  "total"
                ],
                "additionalProperties": false
              },
              "likes": {
                "type": "object",
                "properties": {
                  "available": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "total": {
                    "type": "integer",
                    "minimum": 0
                  }
                },
                "required": [
                  "available",
                  "total"
                ],
                "additionalProperties": false
              },
              "comments": {
                "type": "object",
                "properties": {
                  "available": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "total": {
                    "type": "integer",
                    "minimum": 0
                  }
                },
                "required": [
                  "available",
                  "total"
                ],
                "additionalProperties": false
              },
              "shares": {
                "type": "object",
                "properties": {
                  "available": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "total": {
                    "type": "integer",
                    "minimum": 0
                  }
                },
                "required": [
                  "available",
                  "total"
                ],
                "additionalProperties": false
              },
              "saves": {
                "type": "object",
                "properties": {
                  "available": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "total": {
                    "type": "integer",
                    "minimum": 0
                  }
                },
                "required": [
                  "available",
                  "total"
                ],
                "additionalProperties": false
              }
            },
            "required": [
              "views",
              "reach",
              "likes",
              "comments",
              "shares",
              "saves"
            ],
            "additionalProperties": false
          },
          "post_count": {
            "type": "integer",
            "minimum": 0
          },
          "destination_count": {
            "type": "integer",
            "minimum": 0
          },
          "available_destinations": {
            "type": "integer",
            "minimum": 0
          },
          "last_refreshed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "mode": {
            "type": "string",
            "enum": [
              "test",
              "live"
            ]
          },
          "reporting_periods": {
            "type": "object",
            "properties": {
              "lifetime_destinations": {
                "type": "integer",
                "minimum": 0
              },
              "date_range_destinations": {
                "type": "integer",
                "minimum": 0
              }
            },
            "required": [
              "lifetime_destinations",
              "date_range_destinations"
            ],
            "description": "Counts destinations by snapshot measurement type. Totals may mix measurement windows; inspect destination reporting_period for exact dates."
          }
        },
        "required": [
          "totals",
          "coverage",
          "post_count",
          "destination_count",
          "available_destinations",
          "last_refreshed_at",
          "mode"
        ]
      },
      "AnalyticsRefresh": {
        "type": "object",
        "properties": {
          "post_id": {
            "type": "string"
          },
          "refreshed": {
            "type": "integer",
            "minimum": 0
          },
          "rows": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AnalyticsRow"
            }
          },
          "cached": {
            "type": "boolean"
          }
        },
        "required": [
          "post_id",
          "refreshed",
          "rows",
          "cached"
        ]
      },
      "UsageMeter": {
        "type": "object",
        "properties": {
          "used": {
            "type": "integer",
            "minimum": 0,
            "description": "Completed usage in the current period."
          },
          "reserved": {
            "type": "integer",
            "minimum": 0,
            "description": "Units held for accepted work not yet completed. Publications reserve one unit per destination."
          },
          "limit": {
            "type": "integer",
            "minimum": 0
          },
          "remaining": {
            "type": "integer",
            "minimum": 0,
            "description": "max(0, limit - used - reserved)."
          },
          "reset_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "used",
          "reserved",
          "limit",
          "remaining",
          "reset_at"
        ]
      },
      "UsageSummary": {
        "type": "object",
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "test",
              "live"
            ]
          },
          "plan": {
            "type": "string",
            "enum": [
              "free",
              "starter",
              "growth",
              "scale"
            ]
          },
          "plan_source": {
            "type": "string",
            "enum": [
              "subscription",
              "comp",
              "beta",
              "free"
            ],
            "description": "Where the current limits come from."
          },
          "catalog_version": {
            "type": "string"
          },
          "period": {
            "type": "object",
            "properties": {
              "start": {
                "type": "string",
                "format": "date-time"
              },
              "end": {
                "type": "string",
                "format": "date-time"
              }
            },
            "required": [
              "start",
              "end"
            ]
          },
          "reset_at": {
            "type": "string",
            "format": "date-time"
          },
          "grace_ends_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the current plan source stops applying (beta access, complimentary access or a failed-payment grace period), if it does."
          },
          "accounts": {
            "type": "object",
            "properties": {
              "used": {
                "type": "integer",
                "minimum": 0
              },
              "limit": {
                "type": "integer",
                "minimum": 0
              },
              "remaining": {
                "type": "integer",
                "minimum": 0
              }
            },
            "required": [
              "used",
              "limit",
              "remaining"
            ],
            "description": "Concurrently connected accounts in this mode. Test mode allows 10 simulated accounts."
          },
          "publications": {
            "$ref": "#/components/schemas/UsageMeter",
            "description": "Successful destination publications. Test mode allows 1,000 simulated publications per calendar month."
          },
          "api_requests": {
            "$ref": "#/components/schemas/UsageMeter",
            "description": "Metered accepted API-key operations. Cached resource/status/usage reads, cancellation, disconnect, webhook disabling and exact post replays are exempt. Test keys use a separate 10,000-request monthly allowance."
          },
          "media_bytes": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/UsageMeter"
              },
              {
                "type": "null"
              }
            ],
            "description": "New direct-upload bytes counted once per immutable asset, plus unmanaged source bytes read for destination delivery. Retries and managed-asset reuse do not count again (1 GB = 10^9 bytes). null in test mode."
          },
          "comment_requests": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/UsageMeter"
              },
              {
                "type": "null"
              }
            ],
            "description": "Live comment read and reply requests, counted within api_requests. null in test mode."
          },
          "new_connections": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/UsageMeter"
              },
              {
                "type": "null"
              }
            ],
            "description": "Live accounts connected (or reconnected after a disconnect) this period. Reauthorizing a connected account is free. null in test mode."
          },
          "rate_limit": {
            "type": "object",
            "properties": {
              "per_minute": {
                "type": "integer",
                "minimum": 0,
                "description": "API-key requests per minute for this workspace and mode."
              }
            },
            "required": [
              "per_minute"
            ]
          },
          "concurrent_ingests": {
            "type": "integer",
            "minimum": 0,
            "description": "Media transfers the workspace may run at once."
          }
        },
        "required": [
          "mode",
          "plan",
          "plan_source",
          "catalog_version",
          "period",
          "reset_at",
          "grace_ends_at",
          "accounts",
          "publications",
          "api_requests",
          "media_bytes",
          "comment_requests",
          "new_connections",
          "rate_limit",
          "concurrent_ingests"
        ]
      },
      "UsageLimitDetails": {
        "type": "object",
        "properties": {
          "metric": {
            "type": "string",
            "enum": [
              "publications",
              "media_bytes",
              "api_requests",
              "comment_requests",
              "new_connections"
            ]
          },
          "mode": {
            "type": "string",
            "enum": [
              "test",
              "live"
            ]
          },
          "plan": {
            "type": "string",
            "enum": [
              "free",
              "starter",
              "growth",
              "scale"
            ]
          },
          "limit": {
            "type": "integer",
            "minimum": 0
          },
          "used": {
            "type": "integer",
            "minimum": 0
          },
          "reserved": {
            "type": "integer",
            "minimum": 0
          },
          "remaining": {
            "type": "integer",
            "minimum": 0
          },
          "reset_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the allowance resets. Do not retry before this time."
          },
          "upgrade_url": {
            "type": "string",
            "format": "uri",
            "description": "Billing page where the plan can be changed."
          },
          "retry_after_seconds": {
            "type": "integer",
            "minimum": 0,
            "description": "Seconds until reset_at; also sent as Retry-After."
          }
        },
        "required": [
          "metric",
          "mode",
          "plan",
          "limit",
          "used",
          "reserved",
          "remaining",
          "reset_at",
          "upgrade_url",
          "retry_after_seconds"
        ]
      },
      "AccountLimitDetails": {
        "type": "object",
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "test",
              "live"
            ]
          },
          "plan": {
            "type": "string",
            "enum": [
              "free",
              "starter",
              "growth",
              "scale"
            ]
          },
          "limit": {
            "type": "integer",
            "minimum": 0
          },
          "connected": {
            "type": "integer",
            "minimum": 0
          },
          "upgrade_url": {
            "type": "string",
            "format": "uri",
            "description": "Billing page where the plan can be changed."
          }
        },
        "required": [
          "mode",
          "plan",
          "limit",
          "connected",
          "upgrade_url"
        ]
      },
      "RateLimitDetails": {
        "type": "object",
        "properties": {
          "retry_after_seconds": {
            "type": "integer",
            "minimum": 0,
            "description": "Seconds until the one-minute window resets; also sent as Retry-After."
          },
          "limit": {
            "type": "integer",
            "minimum": 0,
            "description": "Requests allowed per minute."
          }
        },
        "required": [
          "retry_after_seconds"
        ]
      },
      "UsageLimitError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "usage_limit_exceeded"
                ]
              },
              "message": {
                "type": "string"
              },
              "details": {
                "$ref": "#/components/schemas/UsageLimitDetails"
              }
            },
            "required": [
              "code",
              "message",
              "details"
            ]
          },
          "request_id": {
            "type": "string"
          }
        },
        "required": [
          "error",
          "request_id"
        ]
      },
      "AccountLimitError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "account_limit_reached",
                  "account_capacity_exceeded"
                ]
              },
              "message": {
                "type": "string"
              },
              "details": {
                "$ref": "#/components/schemas/AccountLimitDetails"
              }
            },
            "required": [
              "code",
              "message",
              "details"
            ]
          },
          "request_id": {
            "type": "string"
          }
        },
        "required": [
          "error",
          "request_id"
        ]
      },
      "RateLimitError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "rate_limit_exceeded"
                ]
              },
              "message": {
                "type": "string"
              },
              "details": {
                "$ref": "#/components/schemas/RateLimitDetails"
              }
            },
            "required": [
              "code",
              "message",
              "details"
            ]
          },
          "request_id": {
            "type": "string"
          }
        },
        "required": [
          "error",
          "request_id"
        ]
      },
      "AnalyticsConsent": {
        "type": "object",
        "properties": {
          "accepted": {
            "type": "boolean",
            "const": true
          },
          "privacy_policy_version": {
            "type": "string",
            "const": "2026-10-02"
          }
        },
        "required": [
          "accepted",
          "privacy_policy_version"
        ],
        "additionalProperties": false
      }
    }
  },
  "webhooks": {
    "postEvent": {
      "post": {
        "summary": "Terminal post status event",
        "description": "Verify HMAC-SHA256 over webhook-id + dot + webhook-timestamp + dot + exact raw body using the endpoint secret. Signature is v1=<lowercase hex>. Reject timestamps outside 300 seconds. Deduplicate webhook-id. Respond 2xx quickly.",
        "parameters": [
          {
            "name": "webhook-id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-timestamp",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEvent"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Event accepted"
          }
        }
      }
    }
  },
  "x-castrook-distribution": {
    "package_version": "0.5.0",
    "npm": "castrook",
    "python": "castrook",
    "mcp": "https://castrook.com/mcp",
    "guides": "https://castrook.com/llms-full.txt",
    "manifest": "https://castrook.com/agents.json"
  }
}
