> ## Documentation Index
> Fetch the complete documentation index at: https://docs.postbreeze.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Update a post

> Edits an existing post. Works on drafts, scheduled posts, and posts that previously failed or were cancelled — basically anything that hasn't been published yet. Posts that are currently publishing, already published, or partially published can't be edited and will return a `400`.

The body is the same shape as `POST /posts`.

The list of accounts and the list of media are replaced wholesale by what you send. Anything that was attached before is swapped out for the new lists. The history of past publish attempts is kept around for the audit log, but the per-account rows themselves are rebuilt from scratch.

If the post was scheduled, editing it cancels the pending publish and Postbreeze re-queues it with the new content and time. We do this so you can change a scheduled post without having to cancel and recreate it.

Only workspace **owners**, **admins**, and **editors** can edit posts.



## OpenAPI

````yaml /openapi.json patch /posts/{postId}
openapi: 3.0.0
info:
  title: Postbreeze API
  description: >-
    Public REST API for scheduling and managing social posts. Authenticate every
    request with `Authorization: Bearer pb_live_…`. All endpoints are scoped to
    a single workspace; an API key issued for workspace A cannot access
    workspace B.
  version: 1.0.0
  contact: {}
servers:
  - url: http://localhost:4100/api/v1
security:
  - bearer: []
tags: []
paths:
  /posts/{postId}:
    patch:
      tags:
        - Posts
      summary: Update a post
      description: >-
        Edits an existing post. Works on drafts, scheduled posts, and posts that
        previously failed or were cancelled — basically anything that hasn't
        been published yet. Posts that are currently publishing, already
        published, or partially published can't be edited and will return a
        `400`.


        The body is the same shape as `POST /posts`.


        The list of accounts and the list of media are replaced wholesale by
        what you send. Anything that was attached before is swapped out for the
        new lists. The history of past publish attempts is kept around for the
        audit log, but the per-account rows themselves are rebuilt from scratch.


        If the post was scheduled, editing it cancels the pending publish and
        Postbreeze re-queues it with the new content and time. We do this so you
        can change a scheduled post without having to cancel and recreate it.


        Only workspace **owners**, **admins**, and **editors** can edit posts.
      operationId: PostsV1Controller_update
      parameters:
        - name: postId
          required: true
          in: path
          description: Prefixed cuid of the post to update.
          schema:
            example: post_01HZX5T2K9Q3RB6N6JZP3RYV0M
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreatePostFlatDto'
      responses:
        '200':
          description: The updated post.
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    description: Prefixed cuid identifier.
                  workspaceId:
                    type: string
                    description: Workspace this post belongs to.
                  createdByUserId:
                    type: string
                    description: User id of the author.
                  status:
                    type: string
                    enum:
                      - DRAFT
                      - NEEDS_APPROVAL
                      - SCHEDULED
                      - PUBLISHING
                      - PARTIALLY_PUBLISHED
                      - PUBLISHED
                      - FAILED
                      - CANCELED
                    description: >-
                      Aggregate publish state. Derived from per-target statuses
                      (`PARTIALLY_PUBLISHED` when some succeed and some fail).
                  caption:
                    type: string
                    nullable: true
                    description: >-
                      Default caption applied to every target unless overridden
                      via `targets[].captionOverride`.
                  scheduledAt:
                    type: string
                    format: date-time
                    nullable: true
                    description: >-
                      ISO-8601 publish time. Doubles as the calendar pin for
                      drafts (set via `draftScheduledAt`).
                  timezone:
                    type: string
                    nullable: true
                    description: >-
                      IANA timezone the post was composed in. Cosmetic — used by
                      the UI.
                  workflowId:
                    type: string
                    nullable: true
                    description: >-
                      Temporal workflow id driving this post's publish, when
                      scheduled. Null on drafts and after cancellation.
                  pendingWorkflowId:
                    type: string
                    nullable: true
                    description: >-
                      Workflow id staged during the schedule transaction.
                      Visible while a Temporal start is in flight; cleared on
                      commit.
                  approvedByUserId:
                    type: string
                    nullable: true
                    description: >-
                      User id of the approver. Set when an `NEEDS_APPROVAL` post
                      is approved.
                  approvedAt:
                    type: string
                    format: date-time
                    nullable: true
                    description: When the post was approved, if applicable.
                  createdViaApi:
                    type: boolean
                    description: >-
                      True when the post was authored by an API-key or OAuth
                      request. Cookie-session dashboard posts are false.
                  createdViaFlatShape:
                    type: boolean
                    description: >-
                      True when the request used the flat `platforms[]` body
                      shape. False for the nested `targets[]` shape and
                      dashboard posts.
                  deletedAt:
                    type: string
                    format: date-time
                    nullable: true
                    description: >-
                      Soft-delete timestamp. Listings and `GET /posts/:id`
                      exclude rows where this is non-null.
                  createdAt:
                    type: string
                    format: date-time
                    description: When the post was created.
                  updatedAt:
                    type: string
                    format: date-time
                    description: When the post was last modified.
                  targets:
                    description: >-
                      One fan-out target per connected account this post
                      publishes to.
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          description: Prefixed cuid identifier of this target.
                        postId:
                          type: string
                          description: Owning post id.
                        socialAccountId:
                          type: string
                          description: >-
                            Connected social account this target publishes to.
                            Same id returned from `GET /social-accounts`.
                        platform:
                          type: string
                          enum:
                            - INSTAGRAM
                            - FACEBOOK_PAGE
                            - X
                            - LINKEDIN_PERSON
                            - LINKEDIN_COMPANY
                            - TIKTOK_BUSINESS
                            - TIKTOK_PERSONAL
                            - YOUTUBE
                            - PINTEREST
                            - THREADS
                            - BLUESKY
                          description: >-
                            Platform of the bound social account, denormalised
                            for convenience.
                        status:
                          type: string
                          enum:
                            - DRAFT
                            - SCHEDULED
                            - PUBLISHING
                            - PUBLISHED
                            - FAILED
                            - SKIPPED
                            - CANCELED
                          description: >-
                            Per-target publish state. Independent of the parent
                            `Post.status` so one target can FAIL while others
                            PUBLISH.
                        captionOverride:
                          type: string
                          nullable: true
                          description: >-
                            Override of the post-level caption for this target
                            only. Null when the parent caption is used.
                        firstComment:
                          type: string
                          nullable: true
                          description: >-
                            First-comment text posted as a reply after the main
                            post lands. Null when no first comment was
                            requested.
                        firstCommentError:
                          type: string
                          nullable: true
                          description: >-
                            Set when the main post landed but the first-comment
                            leg failed (rate limit, missing scope, comments
                            disabled). Cleared on a successful retry.
                        firstCommentAttempt:
                          type: integer
                          description: >-
                            Monotonic count of first-comment attempts (publisher
                            leg + manual retries). Powers the retry-cooldown
                            gate without scanning `PublishAttempt`.
                        platformOptions:
                          type: object
                          additionalProperties: true
                          nullable: true
                          description: >-
                            Per-platform options discriminated on `platform`.
                            Shape mirrors what was passed in at create / update
                            time.
                        mediaIds:
                          description: >-
                            Per-target media override. Empty array means the
                            target inherits the post-level `media` ordering.
                          type: array
                          items:
                            type: string
                        scheduledFor:
                          type: string
                          format: date-time
                          nullable: true
                          description: >-
                            When this target is scheduled to publish. Null on
                            drafts.
                        publishedAt:
                          type: string
                          format: date-time
                          nullable: true
                          description: >-
                            When this target actually published. Null until the
                            publisher lands.
                        externalPostId:
                          type: string
                          nullable: true
                          description: >-
                            Stable per-platform post identifier returned by the
                            platform after publish. Null until the publisher
                            lands.
                        externalUrl:
                          type: string
                          nullable: true
                          description: >-
                            Public URL of the published post on the platform,
                            when the platform exposes one.
                        errorCode:
                          type: string
                          nullable: true
                          description: >-
                            Machine-readable error code from the last failed
                            publish attempt.
                        errorMessage:
                          type: string
                          nullable: true
                          description: >-
                            Human-readable error message from the last failed
                            publish attempt.
                        attemptCount:
                          type: integer
                          description: >-
                            Monotonic count of publish attempts the worker has
                            made for this target.
                        lastAttemptAt:
                          type: string
                          format: date-time
                          nullable: true
                          description: >-
                            Timestamp of the most recent publish attempt,
                            success or failure.
                        createdAt:
                          type: string
                          format: date-time
                          description: When this target row was created.
                        updatedAt:
                          type: string
                          format: date-time
                          description: When this target row was last modified.
                  media:
                    description: >-
                      Library media attached to the post, ordered by `order`.
                      Per-target overrides on `targets[].mediaIds` win over this
                      list.
                    type: array
                    items:
                      type: object
                      properties:
                        postId:
                          type: string
                          description: Owning post id.
                        mediaAssetId:
                          type: string
                          description: >-
                            Library `MediaAsset` id. The asset itself (storage
                            key, kind, etc.) is fetched via `GET /media/:id`.
                        order:
                          type: integer
                          description: Ordering position within the post (0-indexed).
                        altText:
                          type: string
                          nullable: true
                          description: >-
                            Per-post alt text override. Falls back to
                            `MediaAsset.altText` when null.
        '400':
          description: Validation error
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: integer
                    example: 400
                  code:
                    type: string
                    example: VALIDATION_FAILED
                  message:
                    type: string
                  requestId:
                    type: string
        '401':
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: integer
                    example: 401
                  message:
                    type: string
                    example: Unauthorized
                  requestId:
                    type: string
components:
  schemas:
    CreatePostFlatDto:
      type: object
      properties:
        content:
          type: string
          maxLength: 10000
          description: >-
            Default caption applied to every platform unless overridden via
            `platforms[].captionOverride`.
          example: Just shipped a new feature 🚀
        scheduledFor:
          type: string
          description: >-
            ISO-8601 publish time. Post is created as SCHEDULED and a Temporal
            workflow is started. Omit for a draft.
          example: '2026-06-15T09:00:00.000Z'
        draftScheduledFor:
          type: string
          description: >-
            ISO-8601 time to pin the post on the calendar without actually
            scheduling it (status stays DRAFT). Ignored when `scheduledFor` is
            set.
        timezone:
          type: string
          description: >-
            IANA timezone the user composed in (e.g. `Europe/Stockholm`). Used
            to render `scheduledFor` correctly in the UI; not used by the
            publisher.
        platforms:
          description: One entry per platform to publish to. Order is preserved in the UI.
          example:
            - accountId: soc_01HZW2K9R5T8V3N7M4P0J1Q6BD
              platformOptions:
                platform: INSTAGRAM
                kind: FEED
          type: array
          items:
            $ref: '#/components/schemas/FlatPlatformTargetDto'
        mediaItems:
          description: >-
            Media to ingest by URL. The server fetches each URL (SSRF-guarded)
            and stores it in your media library. Resulting `MediaAsset` ids are
            prepended to `mediaIds`.
          type: array
          items:
            $ref: '#/components/schemas/FlatMediaItemDto'
        mediaIds:
          description: >-
            IDs of media already in your library (returned from `POST
            /media/presign` or `POST /media/from-url`).
          type: array
          items:
            type: string
        mediaAltText:
          type: object
          description: >-
            Per-asset alt-text overrides keyed by `MediaAsset.id`. Falls back to
            `MediaAsset.altText` when an entry is absent.
      required:
        - platforms
    FlatPlatformTargetDto:
      type: object
      properties:
        accountId:
          type: string
          description: >-
            ID of the connected social account to publish to (returned from `GET
            /social-accounts`).
          example: soc_01HZW2K9R5T8V3N7M4P0J1Q6BD
        captionOverride:
          type: string
          maxLength: 10000
          description: Override the post-level caption for this platform only.
        firstComment:
          type: string
          maxLength: 2000
          description: >-
            Auto-posted as the first reply after the main post lands
            (IG/FB/LinkedIn/X/YouTube/Threads).
        platformOptions:
          type: object
          description: >-
            Per-platform options keyed by discriminator `platform`. Shape
            mirrors the legacy nested route — see `docs/platforms/<name>.mdx`
            for each platform's schema.
          example:
            platform: INSTAGRAM
            kind: FEED
        mediaIds:
          description: >-
            Override the post-level media for this platform only (e.g. send a
            video to TikTok but images to IG).
          type: array
          items:
            type: string
      required:
        - accountId
    FlatMediaItemDto:
      type: object
      properties:
        type:
          type: string
          enum:
            - IMAGE
            - VIDEO
            - GIF
            - DOCUMENT
          description: >-
            Media kind hint. Accepts either uppercase enum (`"IMAGE"`,
            `"VIDEO"`, `"GIF"`) or lowercase (`"image"`, `"video"`, `"gif"`) —
            the server normalises before validation.
        url:
          type: string
          example: https://cdn.example.com/launch-hero.jpg
          description: >-
            HTTPS URL the server will fetch. SSRF-protected — private/reserved
            IPs are rejected.
        altText:
          type: string
          maxLength: 1000
          description: Alt text persisted alongside this asset.
      required:
        - type
        - url
  securitySchemes:
    bearer:
      scheme: bearer
      bearerFormat: API key
      type: http
      description: Your Postbreeze API key

````