{
  "openapi": "3.1.0",
  "info": {
    "title": "Postial API",
    "version": "1.0.0",
    "description": "Agency REST API. Bearer keys are managed at /app/settings/api. 60 requests per key per minute. Access expires at trial_end or current_period_end + 3 days, with past_due additionally limited to 7 days. Routing errors use JSON and no-store. Webhooks run separately from publishing (10 s budget, concurrency 4, 5 s timeout); disable pauses, enable resumes, delete cancels open deliveries. Entitlement pauses do not consume attempts. Register endpoints using webhooks:manage. Existing keys retain their scopes. POST /webhooks/{id}/test emits ping.",
    "license": {
      "name": "Proprietary",
      "identifier": "LicenseRef-Proprietary"
    }
  },
  "servers": [
    {
      "url": "https://postial.co/api/v1"
    }
  ],
  "tags": [
    {
      "name": "API v1",
      "description": "Workspace-scoped brands and publishing"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/me": {
      "get": {
        "operationId": "getMe",
        "summary": "Get workspace, plan and limits",
        "description": "Required scope: any valid key.",
        "tags": [
          "API v1"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Me"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Agency entitlement or scope missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown or foreign resource, including a channel outside the selected brand, or unknown API route.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "60 requests per minute exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds until the next request is allowed",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Internal failure",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Unsupported method; JSON method_not_allowed error, Allow and Cache-Control: no-store headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/brands": {
      "get": {
        "operationId": "listBrands",
        "summary": "List workspace brands",
        "description": "Required scope: brands:read.",
        "tags": [
          "API v1"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Brand"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Agency entitlement or scope missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown or foreign resource, including a channel outside the selected brand, or unknown API route.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "60 requests per minute exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds until the next request is allowed",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Internal failure",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Unsupported method; JSON method_not_allowed error, Allow and Cache-Control: no-store headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/brands/{id}/channels": {
      "get": {
        "operationId": "listChannels",
        "summary": "List brand channels",
        "description": "Required scope: brands:read.",
        "tags": [
          "API v1"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Channel"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Agency entitlement or scope missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown or foreign resource, including a channel outside the selected brand, or unknown API route.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "60 requests per minute exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds until the next request is allowed",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Internal failure",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Unsupported method; JSON method_not_allowed error, Allow and Cache-Control: no-store headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/posts": {
      "get": {
        "operationId": "listPosts",
        "summary": "List posts in ascending UUID order",
        "description": "Required scope: posts:read.",
        "tags": [
          "API v1"
        ],
        "parameters": [
          {
            "name": "brand_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "pending_approval",
                "changes_requested",
                "approved",
                "scheduled",
                "publishing",
                "published",
                "partially_failed",
                "failed",
                "skipped"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Post"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "uuid"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Agency entitlement or scope missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown or foreign resource, including a channel outside the selected brand, or unknown API route.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "60 requests per minute exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds until the next request is allowed",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Internal failure",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Unsupported method; JSON method_not_allowed error, Allow and Cache-Control: no-store headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createPost",
        "summary": "Create a draft or schedule a post",
        "description": "Required scope: posts:write.",
        "tags": [
          "API v1"
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            },
            "description": "Replays the same 201 response for 24 hours per key; changed payload returns 409."
          }
        ],
        "responses": {
          "201": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatedPost"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Agency entitlement or scope missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown or foreign resource, including a channel outside the selected brand, or unknown API route.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "60 requests per minute exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds until the next request is allowed",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Internal failure",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency conflict or invalid post state",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request exceeds 64 KiB",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Unsupported method; JSON method_not_allowed error, Allow and Cache-Control: no-store headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePost"
              },
              "example": {
                "brand_id": "11111111-1111-4111-8111-111111111111",
                "body": "Hello from Postial",
                "media_urls": [],
                "channel_ids": [
                  "22222222-2222-4222-8222-222222222222"
                ],
                "scheduled_at": "now",
                "requires_approval": true
              }
            }
          }
        }
      }
    },
    "/posts/{id}": {
      "get": {
        "operationId": "getPost",
        "summary": "Get post, targets and event history",
        "description": "Required scope: posts:read.",
        "tags": [
          "API v1"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PostDetail"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Agency entitlement or scope missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown or foreign resource, including a channel outside the selected brand, or unknown API route.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "60 requests per minute exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds until the next request is allowed",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Internal failure",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Unsupported method; JSON method_not_allowed error, Allow and Cache-Control: no-store headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deletePost",
        "summary": "Delete an unstarted draft or scheduled post",
        "description": "Required scope: posts:write.",
        "tags": [
          "API v1"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Post deleted"
          },
          "401": {
            "description": "Missing, invalid or revoked API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Agency entitlement or scope missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown or foreign resource, including a channel outside the selected brand, or unknown API route.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "60 requests per minute exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds until the next request is allowed",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Internal failure",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency conflict or invalid post state",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Unsupported method; JSON method_not_allowed error, Allow and Cache-Control: no-store headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "summary": "Reschedule an unstarted scheduled or approved post",
        "operationId": "reschedulePost",
        "tags": [
          "Posts"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "scheduled_at"
                ],
                "properties": {
                  "scheduled_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Future ISO timestamp with timezone. Preserves approval."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PostDetail"
                }
              }
            }
          },
          "409": {
            "description": "Publishing started or post is not scheduled/approved"
          },
          "422": {
            "description": "Invalid or past timestamp"
          },
          "401": {
            "description": "Invalid API key"
          },
          "403": {
            "description": "Requires posts:write and Agency access"
          }
        }
      }
    },
    "/posts/{id}/retry": {
      "post": {
        "operationId": "retryPost",
        "summary": "Retry eligible failed targets; inspect needs_review remotely first",
        "description": "Required scope: posts:write.",
        "tags": [
          "API v1"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PostDetail"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Agency entitlement or scope missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown or foreign resource, including a channel outside the selected brand, or unknown API route.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "60 requests per minute exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds until the next request is allowed",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Internal failure",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency conflict or invalid post state",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Unsupported method; JSON method_not_allowed error, Allow and Cache-Control: no-store headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/webhooks": {
      "get": {
        "operationId": "listWebhooks",
        "summary": "List webhook endpoints without secrets",
        "description": "Required scope: webhooks:manage. ",
        "tags": [
          "API v1"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookEndpoint"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Agency entitlement or scope missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown or foreign resource, including a channel outside the selected brand, or unknown API route.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "60 requests per minute exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds until the next request is allowed",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Internal failure",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Unsupported method; JSON method_not_allowed error, Allow and Cache-Control: no-store headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createWebhook",
        "summary": "Register a webhook endpoint",
        "description": "Required scope: webhooks:manage. Public HTTPS only; private/reserved DNS addresses and redirects are rejected. Maximum 10 non-deleted endpoints per workspace (including disabled); 422 at the limit. Signing secret is returned only in this response.",
        "tags": [
          "API v1"
        ],
        "parameters": [],
        "responses": {
          "201": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatedWebhookEndpoint"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Agency entitlement or scope missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown or foreign resource, including a channel outside the selected brand, or unknown API route.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "60 requests per minute exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds until the next request is allowed",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Internal failure",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Unsupported method; JSON method_not_allowed error, Allow and Cache-Control: no-store headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "maxLength": 2048
                  },
                  "events": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 4,
                    "items": {
                      "type": "string",
                      "enum": [
                        "post.published",
                        "post.failed",
                        "post.needs_review",
                        "approval.decided"
                      ]
                    }
                  }
                },
                "required": [
                  "url",
                  "events"
                ],
                "additionalProperties": false
              }
            }
          }
        }
      }
    },
    "/webhooks/{id}": {
      "delete": {
        "operationId": "deleteWebhook",
        "summary": "Delete endpoint and cancel open deliveries",
        "description": "Required scope: webhooks:manage. Destroys signing credentials, retains delivery logs. In-flight requests may finish.",
        "tags": [
          "API v1"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Successful response"
          },
          "401": {
            "description": "Missing, invalid or revoked API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Agency entitlement or scope missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown or foreign resource, including a channel outside the selected brand, or unknown API route.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "60 requests per minute exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds until the next request is allowed",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Internal failure",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Unsupported method; JSON method_not_allowed error, Allow and Cache-Control: no-store headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/webhooks/{id}/test": {
      "post": {
        "operationId": "testWebhook",
        "summary": "Queue a signed ping event",
        "description": "Required scope: webhooks:manage. Active endpoint required. Queues asynchronously; ping is not a subscription event.",
        "tags": [
          "API v1"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "event": {
                      "const": "ping",
                      "type": "string"
                    },
                    "status": {
                      "const": "pending",
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "event",
                    "status"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Agency entitlement or scope missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown or foreign resource, including a channel outside the selected brand, or unknown API route.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "60 requests per minute exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds until the next request is allowed",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Internal failure",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Unsupported method; JSON method_not_allowed error, Allow and Cache-Control: no-store headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/webhooks/{id}/deliveries": {
      "get": {
        "operationId": "listWebhookDeliveries",
        "summary": "List latest deliveries",
        "description": "Required scope: webhooks:manage. Newest first, ordered by created_at then id descending. Logs remain readable after deletion; no secrets or request payloads are returned.",
        "tags": [
          "API v1"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookDelivery"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Agency entitlement or scope missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown or foreign resource, including a channel outside the selected brand, or unknown API route.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "60 requests per minute exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds until the next request is allowed",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Internal failure",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Unsupported method; JSON method_not_allowed error, Allow and Cache-Control: no-store headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/media": {
      "post": {
        "summary": "Upload an image",
        "description": "Requires posts:write. JPEG/PNG/WebP/GIF; 5 MiB per image. Agency storage 2 GiB. 30 uploads/minute per user/workspace (per process), plus API key budget. Returned URL is public; use it in post media_urls.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  },
                  "brand_id": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            },
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "type": "string",
                    "contentEncoding": "base64"
                  },
                  "brand_id": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Uploaded: {id,url,width,height,bytes}",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "id",
                    "url",
                    "width",
                    "height",
                    "bytes"
                  ],
                  "properties": {
                    "id": {
                      "type": "string",
                      "pattern": "^[A-Za-z0-9_-]{43}$"
                    },
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "width": {
                      "type": "integer",
                      "minimum": 1
                    },
                    "height": {
                      "type": "integer",
                      "minimum": 1
                    },
                    "bytes": {
                      "type": "integer",
                      "maximum": 5242880
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "API key required"
          },
          "403": {
            "description": "Agency and posts:write required"
          },
          "413": {
            "description": "Image exceeds 5 MiB"
          },
          "422": {
            "description": "Invalid image, brand or exhausted workspace storage"
          },
          "429": {
            "description": "Rate limit exceeded"
          }
        },
        "operationId": "uploadMedia",
        "tags": [
          "API v1"
        ]
      }
    },
    "/posts/bulk": {
      "post": {
        "operationId": "createBulkPosts",
        "summary": "Plan up to 200 posts with individual results",
        "description": "Requires posts:write. Array of 1\u2013200 posts, at most one image per post and 2 MiB per request. Each row uses composer validation; invalid rows do not prevent valid rows being saved. HTTP 200 returns zero-based results in input order, including errors. Idempotency-Key replays the exact result for 24 hours; changed input or reuse on another endpoint returns 409. Bulk editor is included on all plans; public API access retains the Agency requirement.",
        "tags": [
          "API v1"
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            },
            "description": "Replays the same 201 response for 24 hours per key; changed payload returns 409."
          }
        ],
        "responses": {
          "401": {
            "description": "Missing, invalid or revoked API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Agency entitlement or scope missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown or foreign resource, including a channel outside the selected brand, or unknown API route.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "60 requests per minute exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds until the next request is allowed",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Internal failure",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency conflict or invalid post state",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request exceeds 2 MiB",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Unsupported method; JSON method_not_allowed error, Allow and Cache-Control: no-store headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Individual row results, including partial or complete validation failure.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/BulkResult"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "array",
                "minItems": 1,
                "maxItems": 200,
                "items": {
                  "$ref": "#/components/schemas/BulkPost"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "sm_live_<random>"
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            },
            "required": [
              "code",
              "message"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "Brand": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "timezone": {
            "type": "string"
          },
          "color": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "name"
        ]
      },
      "Channel": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "provider": {
            "type": "string",
            "enum": [
              "bluesky",
              "mastodon",
              "telegram",
              "x",
              "threads"
            ]
          },
          "display_name": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "url": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "provider",
          "status"
        ]
      },
      "Target": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "channel_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string"
          },
          "remote_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_error_human": {
            "type": [
              "string",
              "null"
            ]
          },
          "attempts": {
            "type": "integer"
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "Event": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "target_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "type": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Post": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "brand_id": {
            "type": "string",
            "format": "uuid"
          },
          "body": {
            "type": "string"
          },
          "media_urls": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "link_url": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 2048
          },
          "scheduled_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "pending_approval",
              "changes_requested",
              "approved",
              "scheduled",
              "publishing",
              "published",
              "partially_failed",
              "failed",
              "skipped"
            ]
          },
          "requires_approval": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "media_alt": {
            "type": "object",
            "additionalProperties": {
              "type": "string",
              "maxLength": 1000
            },
            "description": "Image URL to alt text mapping; passed to Bluesky and Mastodon."
          }
        },
        "required": [
          "id",
          "brand_id",
          "body",
          "status"
        ]
      },
      "CreatePost": {
        "type": "object",
        "properties": {
          "brand_id": {
            "type": "string",
            "format": "uuid"
          },
          "body": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100000
          },
          "media_urls": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri"
            },
            "maxItems": 4,
            "default": []
          },
          "link_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048
          },
          "channel_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "maxItems": 100,
            "default": []
          },
          "scheduled_at": {
            "oneOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "const": "now"
              }
            ],
            "description": "Omit to save a draft. Otherwise use now or a future ISO 8601 timestamp with timezone."
          },
          "requires_approval": {
            "type": "boolean",
            "default": false
          },
          "media_alt": {
            "type": "object",
            "additionalProperties": {
              "type": "string",
              "maxLength": 1000
            },
            "description": "Image URL to alt text mapping; passed to Bluesky and Mastodon."
          }
        },
        "required": [
          "brand_id",
          "body"
        ],
        "additionalProperties": false
      },
      "Webhook": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "event": {
            "type": "string",
            "enum": [
              "post.published",
              "post.failed",
              "post.needs_review",
              "approval.decided",
              "ping"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "data": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ApprovalEventData"
              },
              {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "post_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "status": {
                    "type": "string"
                  },
                  "target_id": {
                    "type": "string",
                    "format": "uuid"
                  }
                },
                "required": [
                  "post_id"
                ]
              },
              {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "test": {
                    "const": true
                  }
                },
                "required": [
                  "test"
                ]
              }
            ]
          }
        },
        "required": [
          "id",
          "event",
          "created_at",
          "data"
        ],
        "description": "Approval events contain only the documented technical metadata; names and comments are available in authorized GET /posts/{id} approvals[]. Test events contain data.test=true.",
        "examples": [
          {
            "id": "44444444-4444-4444-8444-444444444444",
            "event": "post.published",
            "created_at": "2026-09-09T12:00:00Z",
            "data": {
              "post_id": "33333333-3333-4333-8333-333333333333",
              "status": "published"
            }
          },
          {
            "id": "44444444-4444-4444-8444-444444444444",
            "event": "post.failed",
            "created_at": "2026-09-09T12:00:00Z",
            "data": {
              "post_id": "33333333-3333-4333-8333-333333333333",
              "status": "partially_failed"
            }
          },
          {
            "id": "44444444-4444-4444-8444-444444444444",
            "event": "post.needs_review",
            "created_at": "2026-09-09T12:00:00Z",
            "data": {
              "post_id": "33333333-3333-4333-8333-333333333333",
              "target_id": "22222222-2222-4222-8222-222222222222"
            }
          },
          {
            "id": "44444444-4444-4444-8444-444444444444",
            "event": "approval.decided",
            "created_at": "2026-09-09T12:00:00Z",
            "data": {
              "post_id": "33333333-3333-4333-8333-333333333333",
              "brand_id": "11111111-1111-4111-8111-111111111111",
              "decision": "approved",
              "decided_at": "2026-09-09T12:00:00Z",
              "has_comment": false,
              "post_url": "https://postial.co/app/posts/33333333-3333-4333-8333-333333333333"
            }
          },
          {
            "id": "44444444-4444-4444-8444-444444444444",
            "event": "ping",
            "created_at": "2026-09-09T12:00:00Z",
            "data": {
              "test": true
            }
          }
        ]
      },
      "PostDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Post"
          },
          {
            "type": "object",
            "properties": {
              "targets": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Target"
                }
              },
              "events": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Event"
                }
              },
              "approvals": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "decision",
                    "reviewer_name",
                    "comment",
                    "decided_at",
                    "created_at"
                  ],
                  "properties": {
                    "decision": {
                      "type": "string",
                      "enum": [
                        "approved",
                        "changes_requested"
                      ]
                    },
                    "reviewer_name": {
                      "type": "string"
                    },
                    "comment": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "decided_at": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Compatibility alias for created_at."
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              },
              "approval_url": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "uri",
                "description": "Decision capability. Included only for keys with posts:write; omitted entirely from posts:read-only responses. May be null for an approval draft without a token."
              }
            },
            "required": [
              "targets",
              "events",
              "approvals"
            ]
          }
        ]
      },
      "CreatedPost": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Post"
          },
          {
            "type": "object",
            "properties": {
              "approval_url": {
                "type": "string",
                "format": "uri",
                "description": "Decision capability. Included only for keys with posts:write; omitted entirely from posts:read-only responses. May be null for an approval draft without a token."
              }
            }
          }
        ]
      },
      "Me": {
        "type": "object",
        "properties": {
          "workspace": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "name": {
                "type": "string"
              },
              "slug": {
                "type": "string"
              }
            }
          },
          "plan": {
            "const": "agency"
          },
          "limits": {
            "type": "object",
            "properties": {
              "brands": {
                "type": "integer"
              },
              "seats": {
                "type": "integer"
              },
              "requests_per_minute": {
                "const": 60
              }
            }
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "ApprovalEventData": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "post_id",
          "brand_id",
          "decision",
          "decided_at",
          "has_comment",
          "post_url"
        ],
        "properties": {
          "post_id": {
            "type": "string",
            "format": "uuid"
          },
          "brand_id": {
            "type": "string",
            "format": "uuid"
          },
          "decision": {
            "type": "string"
          },
          "decided_at": {
            "type": "string",
            "format": "date-time"
          },
          "has_comment": {
            "type": "boolean"
          },
          "post_url": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "WebhookEndpoint": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "post.published",
                "post.failed",
                "post.needs_review",
                "approval.decided"
              ]
            }
          },
          "active": {
            "type": "boolean"
          }
        },
        "required": [
          "id",
          "url",
          "events",
          "active"
        ]
      },
      "CreatedWebhookEndpoint": {
        "allOf": [
          {
            "$ref": "#/components/schemas/WebhookEndpoint"
          },
          {
            "type": "object",
            "properties": {
              "secret": {
                "type": "string",
                "description": "One-time signing secret. Store securely; never returned by reads."
              }
            },
            "required": [
              "secret"
            ]
          }
        ]
      },
      "WebhookDelivery": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "event": {
            "type": "string",
            "enum": [
              "post.published",
              "post.failed",
              "post.needs_review",
              "approval.decided",
              "ping"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "delivered",
              "failed",
              "paused",
              "canceled"
            ]
          },
          "attempts": {
            "type": "integer"
          },
          "response_status": {
            "type": [
              "integer",
              "null"
            ]
          },
          "pause_reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "next_attempt_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "event",
          "status",
          "attempts",
          "response_status",
          "pause_reason",
          "next_attempt_at",
          "created_at"
        ]
      },
      "BulkPost": {
        "type": "object",
        "properties": {
          "brand_id": {
            "type": "string",
            "format": "uuid"
          },
          "body": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100000
          },
          "media_urls": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri"
            },
            "maxItems": 1,
            "default": []
          },
          "link_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048
          },
          "channel_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "maxItems": 100,
            "default": []
          },
          "scheduled_at": {
            "oneOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "const": "now"
              }
            ],
            "description": "Omit to save a draft. Otherwise use now or a future ISO 8601 timestamp with timezone."
          },
          "requires_approval": {
            "type": "boolean",
            "default": false
          },
          "media_alt": {
            "type": "object",
            "additionalProperties": {
              "type": "string",
              "maxLength": 1000
            },
            "description": "Image URL to alt text mapping; passed to Bluesky and Mastodon."
          }
        },
        "required": [
          "brand_id",
          "body"
        ],
        "additionalProperties": false
      },
      "BulkResult": {
        "type": "object",
        "required": [
          "index",
          "status"
        ],
        "properties": {
          "index": {
            "type": "integer",
            "minimum": 0
          },
          "status": {
            "type": "integer",
            "description": "201 for a saved row; otherwise its HTTP error status."
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "post_status": {
            "type": "string",
            "enum": [
              "draft",
              "scheduled",
              "pending_approval"
            ]
          },
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            },
            "required": [
              "code",
              "message"
            ]
          }
        }
      }
    }
  },
  "webhooks": {
    "postEvent": {
      "post": {
        "operationId": "receiveWebhook",
        "summary": "Receive a signed Postial event",
        "description": "Verify X-Postial-Signature against the raw body. The X-SocialMint-Signature is a deprecated legacy compatibility header (sent until 2026-12-31). At-least-once delivery; deduplicate payload.id.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Webhook"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Accepted"
          }
        }
      }
    }
  }
}
