{
  "openapi": "3.1.0",
  "info": {
    "title": "Postbase API",
    "version": "1.0.0",
    "description": "Schedule and manage social posts on X, LinkedIn, Bluesky, Mastodon and YouTube. Authenticate with an API key from Postbase → Developers, sent as `Authorization: Bearer pb_live_…`.",
    "contact": {
      "url": "https://www.postbase.so"
    },
    "license": {
      "name": "AGPL-3.0",
      "identifier": "AGPL-3.0-only"
    }
  },
  "servers": [
    {
      "url": "https://www.postbase.so/api/v1"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Posts"
    },
    {
      "name": "Media"
    },
    {
      "name": "Analytics"
    },
    {
      "name": "Webhooks"
    },
    {
      "name": "Channels"
    },
    {
      "name": "Account"
    }
  ],
  "paths": {
    "/me": {
      "get": {
        "summary": "Check a key",
        "description": "The workspace this key acts for.",
        "operationId": "getMe",
        "tags": [
          "Account"
        ],
        "responses": {
          "200": {
            "description": "The workspace",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "workspace": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "name": {
                          "type": [
                            "string",
                            "null"
                          ]
                        }
                      }
                    },
                    "channels": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/channels": {
      "get": {
        "summary": "List channels",
        "operationId": "listChannels",
        "tags": [
          "Channels"
        ],
        "responses": {
          "200": {
            "description": "Connected channels",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "channels": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Channel"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/posts": {
      "get": {
        "summary": "List posts",
        "operationId": "listPosts",
        "tags": [
          "Posts"
        ],
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "scheduled",
                "publishing",
                "published",
                "failed"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Up to 100 posts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "posts": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PostSummary"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create a post",
        "description": "Save a draft, or schedule a post or thread. Send an Idempotency-Key so a retry can't post twice.",
        "operationId": "createPost",
        "tags": [
          "Posts"
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "schema": {
              "type": "string",
              "maxLength": 255
            },
            "description": "Repeat a request with the same key to get the first post back instead of a new one."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "body": {
                    "type": "string",
                    "description": "The post text, for a single post."
                  },
                  "thread": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Several posts to publish as a thread, in order. Takes precedence over body."
                  },
                  "channel_ids": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "description": "Channels to publish to, from GET /channels."
                  },
                  "channel_bodies": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    },
                    "description": "Text for particular channels instead of body, keyed by channel id. Each is a single post."
                  },
                  "scheduled_at": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "When to publish: ISO 8601 with Z or an offset (2026-10-01T09:00:00+01:00). Omit or null to save a draft.",
                    "format": "date-time"
                  },
                  "schedule_in_minutes": {
                    "type": "number",
                    "description": "Publish this many minutes from now, on the server's clock. Instead of scheduled_at."
                  },
                  "media_ids": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "description": "Files from GET /media to attach, in order."
                  },
                  "media_urls": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uri"
                    },
                    "description": "Public https links to images or a video, attached after media_ids. Each is downloaded into the media library (up to 10 per request, 200 MB each).",
                    "maxItems": 10
                  },
                  "youtube": {
                    "type": "object",
                    "properties": {
                      "title": {
                        "type": "string",
                        "maxLength": 100
                      },
                      "privacy": {
                        "type": "string",
                        "enum": [
                          "public",
                          "unlisted",
                          "private"
                        ]
                      },
                      "made_for_kids": {
                        "type": "boolean",
                        "description": "Required to schedule to YouTube."
                      }
                    }
                  },
                  "idempotency_key": {
                    "type": "string",
                    "maxLength": 255,
                    "description": "Same as the Idempotency-Key header."
                  }
                }
              },
              "example": {
                "body": "Launching today 🚀",
                "channel_ids": [
                  "<channel-id>"
                ],
                "scheduled_at": "2026-10-01T09:00:00+01:00"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "post": {
                      "$ref": "#/components/schemas/CreatedPost"
                    }
                  }
                }
              }
            }
          },
          "200": {
            "description": "A repeat of an earlier request (same idempotency key)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "post": {
                      "$ref": "#/components/schemas/CreatedPost"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "No active plan",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/posts/{id}": {
      "get": {
        "summary": "Get a post",
        "description": "The post and each channel's status, live URL, error and latest metrics.",
        "operationId": "getPost",
        "tags": [
          "Posts"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The post",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "post": {
                      "$ref": "#/components/schemas/Post"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "summary": "Edit a post",
        "description": "Edit or reschedule a draft or scheduled post. Only the fields you send change; scheduled_at null turns it into a draft.",
        "operationId": "updatePost",
        "tags": [
          "Posts"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "body": {
                    "type": "string",
                    "description": "The post text, for a single post."
                  },
                  "thread": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Several posts to publish as a thread, in order. Takes precedence over body."
                  },
                  "channel_ids": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "description": "Channels to publish to, from GET /channels."
                  },
                  "channel_bodies": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    },
                    "description": "Text for particular channels instead of body, keyed by channel id. Each is a single post."
                  },
                  "scheduled_at": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "When to publish: ISO 8601 with Z or an offset (2026-10-01T09:00:00+01:00). Omit or null to save a draft.",
                    "format": "date-time"
                  },
                  "schedule_in_minutes": {
                    "type": "number",
                    "description": "Publish this many minutes from now, on the server's clock. Instead of scheduled_at."
                  },
                  "media_ids": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "description": "Files from GET /media to attach, in order."
                  },
                  "media_urls": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uri"
                    },
                    "description": "Public https links to images or a video, attached after media_ids. Each is downloaded into the media library (up to 10 per request, 200 MB each).",
                    "maxItems": 10
                  },
                  "youtube": {
                    "type": "object",
                    "properties": {
                      "title": {
                        "type": "string",
                        "maxLength": 100
                      },
                      "privacy": {
                        "type": "string",
                        "enum": [
                          "public",
                          "unlisted",
                          "private"
                        ]
                      },
                      "made_for_kids": {
                        "type": "boolean",
                        "description": "Required to schedule to YouTube."
                      }
                    }
                  }
                }
              },
              "example": {
                "scheduled_at": "2026-10-02T09:00:00+01:00"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The edited post",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "post": {
                      "$ref": "#/components/schemas/Post"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "No active plan",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict with the post's state",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/posts/{id}/cancel": {
      "post": {
        "summary": "Cancel a post",
        "description": "Stop a scheduled post; it goes back to being a draft.",
        "operationId": "cancelPost",
        "tags": [
          "Posts"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Cancelled",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "id": {
                      "type": "string"
                    },
                    "still_sending": {
                      "type": "integer",
                      "description": "Channels already mid-send, which will finish"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict with the post's state",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/posts/{id}/retry": {
      "post": {
        "summary": "Retry a post",
        "description": "Send the post again to the channels where it failed. Channels that published are left alone.",
        "operationId": "retryPost",
        "tags": [
          "Posts"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Retrying",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "retrying": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Channel ids being sent again"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "No active plan",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict with the post's state",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/media": {
      "get": {
        "summary": "List media",
        "operationId": "listMedia",
        "tags": [
          "Media"
        ],
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "image",
                "video"
              ]
            }
          },
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Part of a file name"
          }
        ],
        "responses": {
          "200": {
            "description": "The newest 50 files",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "media": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Media"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Add media from a URL",
        "description": "Download a file from a public https link into the media library.",
        "operationId": "addMedia",
        "tags": [
          "Media"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "name": {
                    "type": "string"
                  }
                },
                "required": [
                  "url"
                ]
              },
              "example": {
                "url": "https://example.com/launch.png"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Added",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "media": {
                      "$ref": "#/components/schemas/Media"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/analytics": {
      "get": {
        "summary": "Get analytics",
        "description": "Published posts in a date range with each channel's latest metrics, plus totals for the whole range.",
        "operationId": "getAnalytics",
        "tags": [
          "Analytics"
        ],
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "First day (YYYY-MM-DD). Defaults to 29 days before to."
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Last day (YYYY-MM-DD). Defaults to today (UTC). Up to 366 days."
          },
          {
            "name": "post_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Just this post (ignores the dates)."
          },
          {
            "name": "channel_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "platform",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "date",
                "impressions",
                "likes",
                "comments",
                "shares",
                "saves",
                "engagement"
              ],
              "default": "date"
            }
          },
          {
            "name": "order",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "desc",
                "asc"
              ],
              "default": "desc"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Posts and totals",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "from": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date"
                    },
                    "to": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date"
                    },
                    "totals": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/Metrics"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "posts": {
                              "type": "integer"
                            }
                          }
                        }
                      ]
                    },
                    "posts": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AnalyticsPost"
                      }
                    },
                    "has_more": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/webhooks": {
      "get": {
        "summary": "List webhooks",
        "operationId": "listWebhooks",
        "tags": [
          "Webhooks"
        ],
        "responses": {
          "200": {
            "description": "Endpoints",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhooks": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Webhook"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Add a webhook",
        "description": "Register an https endpoint. The signing secret is returned in this response only.",
        "operationId": "createWebhook",
        "tags": [
          "Webhooks"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "events": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "post.published",
                        "post.partial",
                        "post.failed",
                        "channel.needs_reconnect"
                      ]
                    }
                  },
                  "description": {
                    "type": "string"
                  }
                },
                "required": [
                  "url",
                  "events"
                ]
              },
              "example": {
                "url": "https://example.com/webhooks/postbase",
                "events": [
                  "post.published",
                  "post.partial",
                  "post.failed"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Added",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhook": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/Webhook"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "secret": {
                              "type": "string",
                              "description": "whsec_… Store it: it isn't shown again."
                            }
                          }
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/webhooks/{id}": {
      "get": {
        "summary": "List deliveries",
        "description": "An endpoint's recent deliveries, newest first.",
        "operationId": "listWebhookDeliveries",
        "tags": [
          "Webhooks"
        ],
        "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": "Deliveries",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhook": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "url": {
                          "type": "string"
                        },
                        "events": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "deliveries": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Delivery"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete a webhook",
        "operationId": "deleteWebhook",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "id": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/webhooks/{id}/test": {
      "post": {
        "summary": "Send a test event",
        "description": "Send a webhook.test event now and report the response. Not retried.",
        "operationId": "testWebhook",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "event_id": {
                      "type": "string"
                    },
                    "delivered": {
                      "type": "boolean"
                    },
                    "response_status": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    },
                    "error": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {
    "post.published": {
      "post": {
        "summary": "post.published",
        "description": "The post went out on every channel.",
        "parameters": [
          {
            "name": "Postbase-Event-Id",
            "in": "header",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Postbase-Event-Type",
            "in": "header",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Postbase-Timestamp",
            "in": "header",
            "schema": {
              "type": "string"
            },
            "description": "Unix seconds"
          },
          {
            "name": "Postbase-Signature",
            "in": "header",
            "schema": {
              "type": "string"
            },
            "description": "sha256=<hex HMAC-SHA256 of `${timestamp}.${raw body}` with the endpoint's secret>"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookEvent"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "data": {
                        "type": "object",
                        "properties": {
                          "post": {
                            "$ref": "#/components/schemas/Post"
                          }
                        }
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Return any 2xx within 10 seconds. Anything else is retried with backoff (1 min, 5 min, 30 min, 2 h, 6 h)."
          }
        }
      }
    },
    "post.partial": {
      "post": {
        "summary": "post.partial",
        "description": "The post went out on some channels and failed on others (no more automatic retries).",
        "parameters": [
          {
            "name": "Postbase-Event-Id",
            "in": "header",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Postbase-Event-Type",
            "in": "header",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Postbase-Timestamp",
            "in": "header",
            "schema": {
              "type": "string"
            },
            "description": "Unix seconds"
          },
          {
            "name": "Postbase-Signature",
            "in": "header",
            "schema": {
              "type": "string"
            },
            "description": "sha256=<hex HMAC-SHA256 of `${timestamp}.${raw body}` with the endpoint's secret>"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookEvent"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "data": {
                        "type": "object",
                        "properties": {
                          "post": {
                            "$ref": "#/components/schemas/Post"
                          }
                        }
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Return any 2xx within 10 seconds. Anything else is retried with backoff (1 min, 5 min, 30 min, 2 h, 6 h)."
          }
        }
      }
    },
    "post.failed": {
      "post": {
        "summary": "post.failed",
        "description": "The post failed on every channel (no more automatic retries).",
        "parameters": [
          {
            "name": "Postbase-Event-Id",
            "in": "header",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Postbase-Event-Type",
            "in": "header",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Postbase-Timestamp",
            "in": "header",
            "schema": {
              "type": "string"
            },
            "description": "Unix seconds"
          },
          {
            "name": "Postbase-Signature",
            "in": "header",
            "schema": {
              "type": "string"
            },
            "description": "sha256=<hex HMAC-SHA256 of `${timestamp}.${raw body}` with the endpoint's secret>"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookEvent"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "data": {
                        "type": "object",
                        "properties": {
                          "post": {
                            "$ref": "#/components/schemas/Post"
                          }
                        }
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Return any 2xx within 10 seconds. Anything else is retried with backoff (1 min, 5 min, 30 min, 2 h, 6 h)."
          }
        }
      }
    },
    "channel.needs_reconnect": {
      "post": {
        "summary": "channel.needs_reconnect",
        "description": "A channel lost access (revoked or expired) and needs reconnecting in Postbase.",
        "parameters": [
          {
            "name": "Postbase-Event-Id",
            "in": "header",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Postbase-Event-Type",
            "in": "header",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Postbase-Timestamp",
            "in": "header",
            "schema": {
              "type": "string"
            },
            "description": "Unix seconds"
          },
          {
            "name": "Postbase-Signature",
            "in": "header",
            "schema": {
              "type": "string"
            },
            "description": "sha256=<hex HMAC-SHA256 of `${timestamp}.${raw body}` with the endpoint's secret>"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookEvent"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "data": {
                        "type": "object",
                        "properties": {
                          "channel": {
                            "type": "object",
                            "properties": {
                              "id": {
                                "type": "string"
                              },
                              "platform": {
                                "type": "string"
                              },
                              "handle": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "status": {
                                "type": "string"
                              },
                              "error": {
                                "type": "string"
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Return any 2xx within 10 seconds. Anything else is retried with backoff (1 min, 5 min, 30 min, 2 h, 6 h)."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "A pb_live_ API key"
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "description": "A readable message"
          },
          "code": {
            "type": "string",
            "description": "A stable code, e.g. not_found"
          }
        },
        "required": [
          "error"
        ]
      },
      "Channel": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "platform": {
            "type": "string",
            "enum": [
              "x",
              "linkedin",
              "bluesky",
              "mastodon",
              "youtube",
              "tiktok",
              "instagram",
              "facebook"
            ]
          },
          "handle": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "platform"
        ]
      },
      "Media": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "description": "MIME type"
          },
          "size_bytes": {
            "type": "integer"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "url",
          "type"
        ]
      },
      "PostSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "body": {
            "type": "string"
          },
          "scheduled_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "status": {
            "type": "string"
          },
          "post_targets": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "channel_id": {
                  "type": "string"
                },
                "status": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "CreatedPost": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "body": {
            "type": "string"
          },
          "scheduled_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "scheduled",
              "publishing",
              "published",
              "failed"
            ]
          },
          "media": {
            "type": "integer",
            "description": "How many files are attached"
          },
          "warning": {
            "type": "string",
            "description": "Something to tell the user, such as X charging for links"
          },
          "idempotent_replay": {
            "type": "boolean",
            "description": "True when this repeats an earlier request with the same idempotency key"
          }
        },
        "required": [
          "id",
          "status"
        ]
      },
      "PostChannel": {
        "type": "object",
        "properties": {
          "channel_id": {
            "type": "string",
            "format": "uuid"
          },
          "platform": {
            "type": [
              "string",
              "null"
            ]
          },
          "handle": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "scheduled",
              "publishing",
              "published",
              "failed"
            ]
          },
          "url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "The live post, once published"
          },
          "error": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why it failed"
          },
          "warning": {
            "type": [
              "string",
              "null"
            ],
            "description": "It published, with something to know"
          },
          "retry_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When Postbase will retry a failed channel on its own"
          },
          "body": {
            "type": [
              "string",
              "null"
            ],
            "description": "This channel's own text, if any"
          },
          "metrics": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": {
              "type": "number"
            },
            "description": "Latest likes, impressions, etc."
          }
        },
        "required": [
          "channel_id",
          "status"
        ]
      },
      "Post": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "scheduled",
              "publishing",
              "published",
              "failed"
            ]
          },
          "body": {
            "type": "string"
          },
          "thread": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "scheduled_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "media": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "url": {
                  "type": "string",
                  "format": "uri"
                },
                "type": {
                  "type": "string"
                }
              }
            }
          },
          "channels": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PostChannel"
            }
          },
          "summary": {
            "type": "object",
            "properties": {
              "published": {
                "type": "integer"
              },
              "failed": {
                "type": "integer"
              },
              "pending": {
                "type": "integer"
              }
            }
          },
          "warning": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "status",
          "channels"
        ]
      },
      "Metrics": {
        "type": "object",
        "properties": {
          "impressions": {
            "type": [
              "number",
              "null"
            ]
          },
          "likes": {
            "type": "number"
          },
          "comments": {
            "type": "number"
          },
          "shares": {
            "type": "number"
          },
          "saves": {
            "type": "number"
          },
          "engagement": {
            "type": "number",
            "description": "likes + comments + shares + saves"
          }
        }
      },
      "AnalyticsPost": {
        "type": "object",
        "properties": {
          "post_id": {
            "type": "string",
            "format": "uuid"
          },
          "body": {
            "type": "string"
          },
          "published_at": {
            "type": "string",
            "format": "date-time"
          },
          "channels": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "channel_id": {
                  "type": "string"
                },
                "platform": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "handle": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "url": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "uri"
                },
                "metrics": {
                  "type": "object",
                  "additionalProperties": {
                    "type": "number"
                  }
                },
                "metrics_updated_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                }
              }
            }
          },
          "totals": {
            "$ref": "#/components/schemas/Metrics"
          }
        }
      },
      "Webhook": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "post.published",
                "post.partial",
                "post.failed",
                "channel.needs_reconnect"
              ]
            }
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "secret_hint": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_delivery": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "pending",
                  "delivered",
                  "failed"
                ]
              },
              "response_status": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "error": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        },
        "required": [
          "id",
          "url",
          "events"
        ]
      },
      "Delivery": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "event_id": {
            "type": "string"
          },
          "event_type": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "delivered",
              "failed"
            ]
          },
          "attempts": {
            "type": "integer"
          },
          "response_status": {
            "type": [
              "integer",
              "null"
            ]
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "next_attempt_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "delivered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "WebhookEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique per event: use it to ignore repeats"
          },
          "type": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "workspace_id": {
            "type": "string",
            "format": "uuid"
          },
          "data": {
            "type": "object"
          }
        },
        "required": [
          "id",
          "type",
          "created_at",
          "data"
        ]
      }
    }
  }
}
