{
  "openapi": "3.1.0",
  "info": {
    "title": "Postprism API",
    "version": "2026-10-10",
    "description": "Create, schedule and update social media posts for LinkedIn, X, TikTok, YouTube, Pinterest and Bluesky, upload media and read analytics. API keys are available on the Freelancer and Agency plans. Docs: https://postprism.app/docs"
  },
  "externalDocs": {
    "url": "https://postprism.app/docs"
  },
  "servers": [
    {
      "url": "https://studio.postprism.app"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Posts",
      "description": "Create, schedule, update and delete posts."
    },
    {
      "name": "Media",
      "description": "Upload images and video to attach to posts."
    },
    {
      "name": "Analytics",
      "description": "Engagement counts for published posts."
    },
    {
      "name": "Platforms",
      "description": "Social accounts connected to a workspace."
    },
    {
      "name": "Workspaces",
      "description": "Workspaces you belong to."
    }
  ],
  "paths": {
    "/api/v1/posts": {
      "get": {
        "operationId": "listPosts",
        "tags": [
          "Posts"
        ],
        "summary": "List posts",
        "description": "Returns posts in the workspace, most recently created first. Filter by status and page through the results with limit and offset.",
        "externalDocs": {
          "url": "https://postprism.app/docs/api/list-posts"
        },
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Only return posts with this status.",
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "scheduled",
                "publishing",
                "published",
                "failed"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many posts to return. Default 50, maximum 100.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "How many posts to skip. Default 0.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "workspace_id",
            "in": "query",
            "required": false,
            "description": "Act on another workspace you belong to. Defaults to the workspace your API key was created in.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PostSummary"
                      }
                    },
                    "error": {
                      "type": "null"
                    },
                    "notice": {
                      "type": "string",
                      "description": "Present when something needs the user, for example a TikTok post saved as a draft until it is reviewed in Postprism."
                    }
                  },
                  "required": [
                    "data",
                    "error"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "id": "3f1c2a9e-0000-4000-8000-00000000a001",
                      "base_content": "Our autumn menu is here: maple oat latte, pumpkin cold brew and a new almond croissant.",
                      "platforms": [
                        "linkedin",
                        "twitter"
                      ],
                      "platform_content": {
                        "linkedin": {
                          "content": "Our autumn menu is here. Three new drinks, one new pastry, all made in-house at Juniper Roasters. Which one are you trying first?"
                        },
                        "twitter": {
                          "content": "Autumn menu, now pouring. Maple oat latte first?",
                          "hashtags": [
                            "#autumnmenu",
                            "#coffeeshop"
                          ]
                        }
                      },
                      "status": "scheduled",
                      "scheduled_at": "2026-10-16T09:30:00Z",
                      "published_at": null,
                      "media": [
                        {
                          "url": "https://…/launch-photo.webp",
                          "type": "image"
                        }
                      ],
                      "ai_generated": false,
                      "created_at": "2026-10-10T14:02:11Z"
                    }
                  ],
                  "error": null
                }
              }
            }
          },
          "401": {
            "description": "The Authorization header is missing, malformed, or the key was revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The workspace_id you passed (or your key’s default workspace) is not one you belong to.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "You went over the hourly limit. See Rate limits.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createPost",
        "tags": [
          "Posts"
        ],
        "summary": "Create a post",
        "description": "Creates a post in the workspace. Without scheduled_at it is saved as a draft that waits on the Postprism calendar for someone to review and schedule. With scheduled_at it is scheduled and publishes automatically at that time.\n\nWrite one version per platform in platform_content. That is the text that gets published, so a short X post and a longer LinkedIn post can go out together.",
        "externalDocs": {
          "url": "https://postprism.app/docs/api/create-post"
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "base_content": {
                    "type": "string",
                    "description": "The shared draft. It is what you see first in Postprism and the starting point for each version, but it is not what gets published."
                  },
                  "platforms": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Platform keys to publish to, from GET /api/v1/platforms (for example [\"linkedin\", \"twitter\"])."
                  },
                  "platform_content": {
                    "type": "object",
                    "additionalProperties": {
                      "$ref": "#/components/schemas/PlatformContent"
                    },
                    "description": "One version per platform, keyed by platform key. Each needs content. Optional for drafts, but a scheduled post fails to publish to any platform without an entry here."
                  },
                  "scheduled_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When to publish, as an ISO 8601 date-time (use UTC, for example 2026-10-16T09:30:00Z). With it the post is scheduled; without it the post is saved as a draft."
                  },
                  "media": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/MediaItem"
                    },
                    "description": "Up to 10 images or videos, each uploaded first with POST /api/v1/media. They are attached to every platform version."
                  },
                  "workspace_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Create the post in another workspace you belong to. Defaults to your key’s workspace."
                  }
                },
                "required": [
                  "base_content",
                  "platforms"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Post"
                    },
                    "error": {
                      "type": "null"
                    },
                    "notice": {
                      "type": "string",
                      "description": "Present when something needs the user, for example a TikTok post saved as a draft until it is reviewed in Postprism."
                    }
                  },
                  "required": [
                    "data",
                    "error"
                  ]
                },
                "example": {
                  "data": {
                    "id": "3f1c2a9e-0000-4000-8000-00000000a001",
                    "workspace_id": "7b2d4e6f-0000-4000-8000-00000000b001",
                    "user_id": "9a8b7c6d-0000-4000-8000-00000000c001",
                    "base_content": "Our autumn menu is here: maple oat latte, pumpkin cold brew and a new almond croissant.",
                    "platforms": [
                      "linkedin",
                      "twitter"
                    ],
                    "platform_content": {
                      "linkedin": {
                        "content": "Our autumn menu is here. Three new drinks, one new pastry, all made in-house at Juniper Roasters. Which one are you trying first?"
                      },
                      "twitter": {
                        "content": "Autumn menu, now pouring. Maple oat latte first?",
                        "hashtags": [
                          "#autumnmenu",
                          "#coffeeshop"
                        ]
                      }
                    },
                    "media": [
                      {
                        "url": "https://…/launch-photo.webp",
                        "type": "image"
                      }
                    ],
                    "status": "scheduled",
                    "scheduled_at": "2026-10-16T09:30:00Z",
                    "published_at": null,
                    "ai_generated": false,
                    "created_at": "2026-10-10T14:02:11Z",
                    "updated_at": "2026-10-10T14:02:11Z"
                  },
                  "error": null
                }
              }
            }
          },
          "400": {
            "description": "base_content is empty or platforms is missing or empty. A media URL was not uploaded through the API by you. More than 10 files were attached. A per-platform media URL (or a YouTube thumbnailUrl) was not uploaded through the API. A platform setting failed validation. The message names the field.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "The Authorization header is missing, malformed, or the key was revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The workspace_id you passed (or your key’s default workspace) is not one you belong to.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "You went over the hourly limit. See Rate limits.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/posts/{id}": {
      "get": {
        "operationId": "getPost",
        "tags": [
          "Posts"
        ],
        "summary": "Get a post",
        "description": "Returns a single post, including its status. Poll this after the scheduled time to see whether a post moved to published or failed.",
        "externalDocs": {
          "url": "https://postprism.app/docs/api/get-post"
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The post ID.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "workspace_id",
            "in": "query",
            "required": false,
            "description": "Act on another workspace you belong to. Defaults to the workspace your API key was created in.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Post"
                    },
                    "error": {
                      "type": "null"
                    },
                    "notice": {
                      "type": "string",
                      "description": "Present when something needs the user, for example a TikTok post saved as a draft until it is reviewed in Postprism."
                    }
                  },
                  "required": [
                    "data",
                    "error"
                  ]
                },
                "example": {
                  "data": {
                    "id": "3f1c2a9e-0000-4000-8000-00000000a001",
                    "workspace_id": "7b2d4e6f-0000-4000-8000-00000000b001",
                    "user_id": "9a8b7c6d-0000-4000-8000-00000000c001",
                    "base_content": "Our autumn menu is here: maple oat latte, pumpkin cold brew and a new almond croissant.",
                    "platforms": [
                      "linkedin",
                      "twitter"
                    ],
                    "platform_content": {
                      "linkedin": {
                        "content": "Our autumn menu is here. Three new drinks, one new pastry, all made in-house at Juniper Roasters. Which one are you trying first?"
                      },
                      "twitter": {
                        "content": "Autumn menu, now pouring. Maple oat latte first?",
                        "hashtags": [
                          "#autumnmenu",
                          "#coffeeshop"
                        ]
                      }
                    },
                    "media": [
                      {
                        "url": "https://…/launch-photo.webp",
                        "type": "image"
                      }
                    ],
                    "status": "scheduled",
                    "scheduled_at": "2026-10-16T09:30:00Z",
                    "published_at": null,
                    "ai_generated": false,
                    "created_at": "2026-10-10T14:02:11Z",
                    "updated_at": "2026-10-10T14:02:11Z"
                  },
                  "error": null
                }
              }
            }
          },
          "401": {
            "description": "The Authorization header is missing, malformed, or the key was revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No post with that ID exists in the workspace. The workspace_id you passed (or your key’s default workspace) is not one you belong to.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "You went over the hourly limit. See Rate limits.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "operationId": "updatePost",
        "tags": [
          "Posts"
        ],
        "summary": "Update a post",
        "description": "Only the fields you send change. platform_content and media are replaced as a whole, so send the full value you want.\n\nChanging scheduled_at does not change status. To schedule a draft, send both scheduled_at and \"status\": \"scheduled\". To pull a scheduled post back to drafts, send \"status\": \"draft\" (and \"scheduled_at\": null to clear the time).",
        "externalDocs": {
          "url": "https://postprism.app/docs/api/update-post"
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The post ID.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "workspace_id",
            "in": "query",
            "required": false,
            "description": "Act on another workspace you belong to. Defaults to the workspace your API key was created in.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "base_content": {
                    "type": "string",
                    "description": "The shared draft."
                  },
                  "platforms": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Platform keys to publish to."
                  },
                  "platform_content": {
                    "type": "object",
                    "additionalProperties": {
                      "$ref": "#/components/schemas/PlatformContent"
                    },
                    "description": "Replaces every per-platform version."
                  },
                  "scheduled_at": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date-time",
                    "description": "New publish time in UTC, or null to clear it."
                  },
                  "status": {
                    "type": "string",
                    "description": "scheduled queues the post for its scheduled_at; draft stops it from publishing.",
                    "enum": [
                      "draft",
                      "scheduled"
                    ]
                  },
                  "media": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/MediaItem"
                    },
                    "description": "Replaces the whole attachment list (up to 10)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Post"
                    },
                    "error": {
                      "type": "null"
                    },
                    "notice": {
                      "type": "string",
                      "description": "Present when something needs the user, for example a TikTok post saved as a draft until it is reviewed in Postprism."
                    }
                  },
                  "required": [
                    "data",
                    "error"
                  ]
                },
                "example": {
                  "data": {
                    "id": "3f1c2a9e-0000-4000-8000-00000000a001",
                    "workspace_id": "7b2d4e6f-0000-4000-8000-00000000b001",
                    "user_id": "9a8b7c6d-0000-4000-8000-00000000c001",
                    "base_content": "Our autumn menu is here: maple oat latte, pumpkin cold brew and a new almond croissant.",
                    "platforms": [
                      "linkedin",
                      "twitter"
                    ],
                    "platform_content": {
                      "linkedin": {
                        "content": "Our autumn menu is here. Three new drinks, one new pastry, all made in-house at Juniper Roasters. Which one are you trying first?"
                      },
                      "twitter": {
                        "content": "Autumn menu, now pouring. Maple oat latte first?",
                        "hashtags": [
                          "#autumnmenu",
                          "#coffeeshop"
                        ]
                      }
                    },
                    "media": [
                      {
                        "url": "https://…/launch-photo.webp",
                        "type": "image"
                      }
                    ],
                    "status": "scheduled",
                    "scheduled_at": "2026-10-16T09:30:00Z",
                    "published_at": null,
                    "ai_generated": false,
                    "created_at": "2026-10-10T14:02:11Z",
                    "updated_at": "2026-10-10T14:02:11Z"
                  },
                  "error": null
                }
              }
            }
          },
          "400": {
            "description": "A media URL was not uploaded through the API by you. More than 10 files were attached. A per-platform media URL (or a YouTube thumbnailUrl) was not uploaded through the API. A platform setting failed validation. The message names the field.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "The Authorization header is missing, malformed, or the key was revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No post with that ID exists in the workspace. The workspace_id you passed (or your key’s default workspace) is not one you belong to.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "You went over the hourly limit. See Rate limits.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deletePost",
        "tags": [
          "Posts"
        ],
        "summary": "Delete a post",
        "description": "Deletes the post from Postprism, so a scheduled post will not publish. It does not remove anything already published on a social network.\n\nReturns 204 No Content with an empty body.",
        "externalDocs": {
          "url": "https://postprism.app/docs/api/delete-post"
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The post ID.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "workspace_id",
            "in": "query",
            "required": false,
            "description": "Act on another workspace you belong to. Defaults to the workspace your API key was created in.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted. No body."
          },
          "401": {
            "description": "The Authorization header is missing, malformed, or the key was revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The workspace_id you passed (or your key’s default workspace) is not one you belong to.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "You went over the hourly limit. See Rate limits.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The post could not be deleted. Try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/media": {
      "post": {
        "operationId": "uploadMedia",
        "tags": [
          "Media"
        ],
        "summary": "Upload media",
        "description": "Upload a file, then pass the returned url in media when you create or update a post. Every attachment has to come through this endpoint; links to files hosted elsewhere are rejected.\n\nSend the raw file as the request body with its MIME type as Content-Type (best for scripts and large files), or send JSON with the file base64-encoded (best for agents that can only send JSON).\n\nAccepted: JPEG, PNG, GIF and WebP images (up to 10 MB) and MP4 and MOV videos (up to 100 MB). Base64 uploads are limited to 10 MB. The file contents must match the declared type. Images are converted to WebP when stored; platforms that need JPEG receive JPEG automatically.",
        "externalDocs": {
          "url": "https://postprism.app/docs/api/upload-media"
        },
        "parameters": [
          {
            "name": "X-Filename",
            "in": "header",
            "required": false,
            "description": "Raw uploads only: the original file name (up to 300 characters).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "image/jpeg": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            },
            "image/png": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            },
            "image/gif": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            },
            "image/webp": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            },
            "video/mp4": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            },
            "video/quicktime": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            },
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "filename": {
                    "type": "string",
                    "description": "The original file name, for example launch-photo.png."
                  },
                  "mime_type": {
                    "type": "string",
                    "description": "The file’s MIME type. It must match the file contents.",
                    "enum": [
                      "image/jpeg",
                      "image/png",
                      "image/gif",
                      "image/webp",
                      "video/mp4",
                      "video/quicktime"
                    ]
                  },
                  "data": {
                    "type": "string",
                    "description": "The file, base64-encoded. A data:<type>;base64, prefix is allowed."
                  }
                },
                "required": [
                  "filename",
                  "mime_type",
                  "data"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/UploadedMedia"
                    },
                    "error": {
                      "type": "null"
                    },
                    "notice": {
                      "type": "string",
                      "description": "Present when something needs the user, for example a TikTok post saved as a draft until it is reviewed in Postprism."
                    }
                  },
                  "required": [
                    "data",
                    "error"
                  ]
                },
                "example": {
                  "data": {
                    "url": "https://…/launch-photo.webp",
                    "type": "image",
                    "storage_path": "…",
                    "file_name": "launch-photo.webp",
                    "file_size": 184320,
                    "mime_type": "image/webp"
                  },
                  "error": null
                }
              }
            }
          },
          "400": {
            "description": "No file was sent. JSON uploads only: data could not be decoded. filename, mime_type and data are all required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "The Authorization header is missing, malformed, or the key was revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "The file is over 10 MB (images or base64) or 100 MB (raw video).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "The file type is not accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The bytes do not match the declared type, or the image could not be read.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Uploads have their own, lower hourly limit. See Rate limits.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/analytics": {
      "get": {
        "operationId": "getAnalytics",
        "tags": [
          "Analytics"
        ],
        "summary": "Get analytics",
        "description": "Returns analytics records for posts in the workspace, most recent first. Each record holds views, likes, comments and shares for one post on one platform.",
        "externalDocs": {
          "url": "https://postprism.app/docs/api/get-analytics"
        },
        "parameters": [
          {
            "name": "post_id",
            "in": "query",
            "required": false,
            "description": "Only records for this post.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "platform",
            "in": "query",
            "required": false,
            "description": "Only records for this platform key.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many records to return. Default 50, maximum 100.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "workspace_id",
            "in": "query",
            "required": false,
            "description": "Act on another workspace you belong to. Defaults to the workspace your API key was created in.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AnalyticsRecord"
                      }
                    },
                    "error": {
                      "type": "null"
                    },
                    "notice": {
                      "type": "string",
                      "description": "Present when something needs the user, for example a TikTok post saved as a draft until it is reviewed in Postprism."
                    }
                  },
                  "required": [
                    "data",
                    "error"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "id": "5c6d7e8f-0000-4000-8000-00000000e001",
                      "post_id": "3f1c2a9e-0000-4000-8000-00000000a001",
                      "platform": "linkedin",
                      "views": 2410,
                      "likes": 86,
                      "comments": 12,
                      "shares": 7,
                      "recorded_at": "2026-10-17T09:00:00Z"
                    }
                  ],
                  "error": null
                }
              }
            }
          },
          "401": {
            "description": "The Authorization header is missing, malformed, or the key was revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The workspace_id you passed (or your key’s default workspace) is not one you belong to.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "You went over the hourly limit. See Rate limits.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/platforms": {
      "get": {
        "operationId": "listPlatforms",
        "tags": [
          "Platforms"
        ],
        "summary": "List connected platforms",
        "description": "Returns the social accounts connected in the workspace. Use each platform value as a key in platforms and platform_content when you create a post.\n\nAccounts are connected in Postprism, through each network’s own sign-in; the API cannot connect new accounts.",
        "externalDocs": {
          "url": "https://postprism.app/docs/api/list-platforms"
        },
        "parameters": [
          {
            "name": "workspace_id",
            "in": "query",
            "required": false,
            "description": "Act on another workspace you belong to. Defaults to the workspace your API key was created in.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PlatformConnection"
                      }
                    },
                    "error": {
                      "type": "null"
                    },
                    "notice": {
                      "type": "string",
                      "description": "Present when something needs the user, for example a TikTok post saved as a draft until it is reviewed in Postprism."
                    }
                  },
                  "required": [
                    "data",
                    "error"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "id": "1e2f3a4b-0000-4000-8000-00000000d001",
                      "platform": "twitter",
                      "platform_username": "juniperroasters",
                      "is_connected": true,
                      "created_at": "2026-09-02T10:15:00Z",
                      "updated_at": "2026-09-02T10:15:00Z"
                    },
                    {
                      "id": "1e2f3a4b-0000-4000-8000-00000000d002",
                      "platform": "linkedin",
                      "platform_username": "Maya Lindqvist",
                      "is_connected": true,
                      "created_at": "2026-09-02T10:18:00Z",
                      "updated_at": "2026-09-02T10:18:00Z"
                    }
                  ],
                  "error": null
                }
              }
            }
          },
          "401": {
            "description": "The Authorization header is missing, malformed, or the key was revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The workspace_id you passed (or your key’s default workspace) is not one you belong to.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "You went over the hourly limit. See Rate limits.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/workspace": {
      "get": {
        "operationId": "getWorkspace",
        "tags": [
          "Workspaces"
        ],
        "summary": "Get your workspace",
        "description": "Returns the workspace your API key was created in, which is the default for every other endpoint, and your Postprism plan.",
        "externalDocs": {
          "url": "https://postprism.app/docs/api/get-workspace"
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WorkspaceInfo"
                    },
                    "error": {
                      "type": "null"
                    },
                    "notice": {
                      "type": "string",
                      "description": "Present when something needs the user, for example a TikTok post saved as a draft until it is reviewed in Postprism."
                    }
                  },
                  "required": [
                    "data",
                    "error"
                  ]
                },
                "example": {
                  "data": {
                    "workspace_id": "7b2d4e6f-0000-4000-8000-00000000b001",
                    "name": "Juniper Roasters",
                    "plan": "freelancer"
                  },
                  "error": null
                }
              }
            }
          },
          "401": {
            "description": "The Authorization header is missing, malformed, or the key was revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "You went over the hourly limit. See Rate limits.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/workspaces": {
      "get": {
        "operationId": "listWorkspaces",
        "tags": [
          "Workspaces"
        ],
        "summary": "List workspaces",
        "description": "Returns every workspace you are a member of, with your role. Pass an id as workspace_id to the post, analytics and platform endpoints to act on that workspace, so one key can work for several brands or clients.",
        "externalDocs": {
          "url": "https://postprism.app/docs/api/list-workspaces"
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Workspace"
                      }
                    },
                    "error": {
                      "type": "null"
                    },
                    "notice": {
                      "type": "string",
                      "description": "Present when something needs the user, for example a TikTok post saved as a draft until it is reviewed in Postprism."
                    }
                  },
                  "required": [
                    "data",
                    "error"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "id": "7b2d4e6f-0000-4000-8000-00000000b001",
                      "name": "Juniper Roasters",
                      "role": "owner",
                      "is_active": true
                    },
                    {
                      "id": "7b2d4e6f-0000-4000-8000-00000000b002",
                      "name": "Northfield Dental",
                      "role": "editor",
                      "is_active": false
                    }
                  ],
                  "error": null
                }
              }
            }
          },
          "401": {
            "description": "The Authorization header is missing, malformed, or the key was revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "You went over the hourly limit. See Rate limits.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "An API key from the Developer page in Postprism. Keys start with pp_live_."
      }
    },
    "schemas": {
      "MediaItem": {
        "type": "object",
        "description": "A file attached to a post.",
        "properties": {
          "url": {
            "type": "string",
            "description": "A url returned by POST /api/v1/media. Links to files hosted anywhere else are rejected."
          },
          "type": {
            "type": "string",
            "description": "Whether the file is an image or a video.",
            "enum": [
              "image",
              "video"
            ]
          }
        },
        "required": [
          "url",
          "type"
        ]
      },
      "PlatformContent": {
        "type": "object",
        "description": "The version of the post for one platform, keyed by the platform key (for example linkedin). This is the text that gets published.",
        "properties": {
          "content": {
            "type": "string",
            "description": "The text published to this platform. Postprism does not fall back to base_content at publish time, so set this for every platform in platforms."
          },
          "hashtags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Hashtags appended after the text when the post publishes (for YouTube they become video tags instead)."
          },
          "settings": {
            "type": "object",
            "additionalProperties": true,
            "description": "Platform-specific options for YouTube, Pinterest, LinkedIn and TikTok. See the platform guides."
          }
        },
        "required": [
          "content"
        ]
      },
      "PostSummary": {
        "type": "object",
        "description": "A post as returned by GET /api/v1/posts.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The post ID."
          },
          "base_content": {
            "type": "string",
            "description": "The shared draft the per-platform versions start from."
          },
          "platforms": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Platform keys the post targets."
          },
          "platform_content": {
            "type": "object",
            "additionalProperties": {
              "$ref": "#/components/schemas/PlatformContent"
            },
            "description": "Per-platform versions, keyed by platform key."
          },
          "status": {
            "type": "string",
            "description": "Where the post is in its lifecycle.",
            "enum": [
              "draft",
              "scheduled",
              "publishing",
              "published",
              "failed"
            ]
          },
          "scheduled_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the post publishes, in UTC. null for drafts."
          },
          "published_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the post was published, or null."
          },
          "media": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MediaItem"
            },
            "description": "Attached images and videos."
          },
          "ai_generated": {
            "type": "boolean",
            "description": "Whether the post was written with Postprism AI."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the post was created."
          }
        }
      },
      "Post": {
        "description": "A full post, as returned when you create, read or update one. It has every PostSummary field plus the ones below. Ignore any other fields; they are not part of the stable API.",
        "allOf": [
          {
            "$ref": "#/components/schemas/PostSummary"
          },
          {
            "type": "object",
            "properties": {
              "workspace_id": {
                "type": "string",
                "format": "uuid",
                "description": "The workspace the post belongs to."
              },
              "user_id": {
                "type": "string",
                "format": "uuid",
                "description": "The member who created the post."
              },
              "updated_at": {
                "type": "string",
                "format": "date-time",
                "description": "When the post last changed."
              }
            }
          }
        ]
      },
      "UploadedMedia": {
        "type": "object",
        "description": "The file you uploaded, ready to attach to a post.",
        "properties": {
          "url": {
            "type": "string",
            "description": "Pass this unchanged as media[].url when you create or update a post. Treat it as opaque."
          },
          "type": {
            "type": "string",
            "description": "Detected from the file contents.",
            "enum": [
              "image",
              "video"
            ]
          },
          "file_name": {
            "type": "string",
            "description": "The stored file name. Images are converted to WebP, so their name ends in .webp."
          },
          "file_size": {
            "type": "integer",
            "description": "Size of the stored file in bytes."
          },
          "mime_type": {
            "type": "string",
            "description": "MIME type of the stored file (image/webp for every image)."
          },
          "storage_path": {
            "type": "string",
            "description": "An opaque reference to the file. You do not need it."
          }
        }
      },
      "AnalyticsRecord": {
        "type": "object",
        "description": "Engagement counts for one post on one platform.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The record ID."
          },
          "post_id": {
            "type": "string",
            "format": "uuid",
            "description": "The post it belongs to."
          },
          "platform": {
            "type": "string",
            "description": "The platform key the counts are for."
          },
          "views": {
            "type": "integer",
            "description": "Views or impressions."
          },
          "likes": {
            "type": "integer",
            "description": "Likes or reactions."
          },
          "comments": {
            "type": "integer",
            "description": "Comments."
          },
          "shares": {
            "type": "integer",
            "description": "Shares or reposts."
          },
          "recorded_at": {
            "type": "string",
            "format": "date-time",
            "description": "When these counts were recorded."
          }
        }
      },
      "PlatformConnection": {
        "type": "object",
        "description": "A social account connected to the workspace.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The connection ID."
          },
          "platform": {
            "type": "string",
            "description": "The platform key to use in platforms and platform_content. A LinkedIn Company Page appears as linkedin:org:<page id>, next to the personal linkedin profile."
          },
          "platform_username": {
            "type": [
              "string",
              "null"
            ],
            "description": "The account name on the platform."
          },
          "is_connected": {
            "type": "boolean",
            "description": "Always true: only live connections are listed."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the account was connected."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the connection last changed."
          }
        }
      },
      "Workspace": {
        "type": "object",
        "description": "A workspace you belong to.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The workspace ID."
          },
          "name": {
            "type": "string",
            "description": "The workspace name."
          },
          "role": {
            "type": "string",
            "description": "Your role in it.",
            "enum": [
              "owner",
              "admin",
              "editor",
              "viewer"
            ]
          },
          "is_active": {
            "type": "boolean",
            "description": "true for the workspace your API key (or MCP connection) acts on by default."
          }
        }
      },
      "WorkspaceInfo": {
        "type": "object",
        "description": "The workspace your API key acts on by default.",
        "properties": {
          "workspace_id": {
            "type": "string",
            "format": "uuid",
            "description": "The workspace ID."
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "The workspace name."
          },
          "plan": {
            "type": "string",
            "description": "Your Postprism plan.",
            "enum": [
              "solo",
              "freelancer",
              "agency"
            ]
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "data": {
            "type": "null"
          },
          "error": {
            "type": "string",
            "description": "A readable error message."
          }
        },
        "required": [
          "data",
          "error"
        ]
      }
    }
  }
}