---
title: "Update a post"
method: PUT
path: "/posts/{id}"
tags: ["Posts"]
---

# Update a post

`PUT /posts/{id}`

## Request

- Base URL: `https://www.sociahive.com/api/v1`
- URL: `https://www.sociahive.com/api/v1/posts/{id}`
- Auth: one of:
  - API key in header `X-API-Key`
  - OAuth 2 (authorization code, authorization URL `/api/oauth/authorize`, token URL `/api/oauth/token`, scopes: `posts:write`)

Partial update. Every field is optional, but unrecognised top-level keys are rejected — send the caption nested under `content`, never as a top-level `text`.

## Path parameters

- `id` string, required: MongoDB ObjectId (24-character hex string)

## Request body

- object
  - `content` object
    - `text` string: Post body. Each target publishes its platform override text, else this text, and it must fit the platform: instagram 2200, facebook 63206, twitter 280, linkedin 3000, threads 500, bluesky 300, youtube 5000, pinterest 800, tiktok 2200. An X account with X Premium may post up to 4000 characters where long posts are on for the account (rolling out); each account's limit is `text_limit` in GET /connected-accounts. Checked when the post is scheduled or published (drafts always save): a text that does not fit is a 400 whose `targets` lists each target ({ platform, account_id, message }).
    - `media` object[]
      - `type` 'image' | 'video', required
      - `url` string, uri, required
      - `alt_text` string: Alt text for an image, sent to every platform that accepts alt text.
      - `cover_url` string, uri: Video only: the chosen cover image (an uploaded image or a grabbed frame). Sent to YouTube (custom thumbnail, unless yt_thumbnail_url is set), Instagram reels (cover_url) and Pinterest video pins (cover_image_url). `thumbnail_url` stays the automatic poster.
      - `cover_time_ms` integer: Video only: the time of the chosen cover frame, in milliseconds. Sent to Instagram reels (thumb_offset, when there is no cover_url) and TikTok Direct Post (video_cover_timestamp_ms).
  - `platform_overrides` object
  - `platforms` object[]
    - `platform` string, required: Platform id. Known values: `instagram`, `messenger`, `whatsapp`, `linkedin`, `twitter`, `tiktok`, `telegram`, `threads`, `youtube`, `bluesky`, `pinterest`, `facebook`, `discord`, `mastodon`, `lemmy`, `devto`, `hashnode`, `wordpress`, `listmonk`, `slack`, `tumblr`, `dribbble`, `twitch`, `kick`. Treat any other value as unknown, never as an error.
    - `account_id` string, required: MongoDB ObjectId (24-character hex string)
    - `content_type` string: Platform format, e.g. Instagram feed, carousel, reels, stories; Facebook story; X article for an X Article (rolling out). An X Article must be the post's only target, has no platform override and does not repeat; content.text is its body (up to 100000 characters) and content.media holds its images (images only).
    - `metadata` object: Platform options as flat string keys. X Article: x_article_title (required, one line, up to 200 characters), x_article_format (markdown, the default, or content_state: content.text is then X's raw block JSON with image data { media_url }), x_article_cover_url (https URL equal to one of the post's images).
  - `schedule_type` 'immediate' | 'scheduled'
  - `scheduled_at` string, date-time
  - `timezone` string
  - `status` 'draft' | 'scheduled' | 'publishing' | 'published' | 'partially_published' | 'failed' | 'pending_approval'
  - `tags` string[]
  - `recurrence` object, nullable: Send null to clear an existing recurrence
    - `frequency` 'daily' | 'weekly' | 'biweekly' | 'monthly', required
    - `days_of_week` integer[]
    - `end_date` string, date-time
    - `end_after_count` integer
  - `acknowledge_unconfirmed` boolean: Set true, after checking X, to re-arm an X Article whose last publish is unconfirmed (it may already be live). Without it such an edit answers 409 with code ARTICLE_UNCONFIRMED.

## Response `200`

Post updated

- PostEnvelope: Single-post envelope — `{ data: <post> }`.
  - `data` Post, required
    - `id` string, required: MongoDB ObjectId (24-character hex string)
    - `content` object, required
      - `text` string, required
      - `media` object[]
        - `type` 'image' | 'video' | 'gif', required
        - `url` string, uri, required
    - `text` string
    - `media` object[]
      - `type` 'image' | 'video' | 'gif', required
      - `url` string, uri, required
    - `status` 'draft' | 'scheduled' | 'publishing' | 'published' | 'partially_published' | 'failed' | 'pending_approval' | 'reminder_sent', required
    - `platforms` object[], required
      - `platform` string, required: Platform id. Known values: `instagram`, `messenger`, `whatsapp`, `linkedin`, `twitter`, `tiktok`, `telegram`, `threads`, `youtube`, `bluesky`, `pinterest`, `facebook`, `discord`, `mastodon`, `lemmy`, `devto`, `hashnode`, `wordpress`, `listmonk`, `slack`, `tumblr`, `dribbble`, `twitch`, `kick`. Treat any other value as unknown, never as an error.
      - `account_id` string, required: MongoDB ObjectId (24-character hex string)
      - `platform_post_id` string
      - `status` 'pending' | 'processing' | 'published' | 'failed' | 'reminder_sent'
      - `error` object
        - `code` string, required
        - `message` string, required
      - `published_at` string, date-time
    - `scheduled_at` string, date-time
    - `timezone` string
    - `created_at` string, date-time, required
    - `updated_at` string, date-time, required
    - `automation` object, nullable
      - `state` string, required: Derived state. Known values: `none`, `building`, `failed`, `off`, `armed`, `live`, `needs_attention`. Treat any other value as unknown, never as an error.
      - `flow_id` string, required
      - `flow_name` string, required
      - `door` string, required: How the automation was built. Known values: `funnel`, `template`, `prompt`. Treat any other value as unknown, never as an error.
      - `account_id` string, required
      - `armed` boolean, required: Turns on when the post goes live. Only a person sets this.
      - `bound` boolean, required: The trigger points at the published media id.
      - `error` object
        - `code` string, required: The fix to offer, e.g. `placeholder_text`, `link_placeholder`, `unbound_media`, `account_disconnected`, `tier_limit`. Open-ended.
        - `message` string, required
      - `allowed_actions` string[], required: What the user can do now. Known values: arm, unarm, turn_on, turn_off, remove, rebind, retry, fill_blanks, open. Ignore unknown values.
      - `flow_live_waiting_for_post` boolean
    - `source` string: Where the post came from (set by the server, never accepted from a client). Known values: `composer`, `rss`. Treat any other value as unknown, never as an error.

## Other responses

- `400`: Bad request — invalid parameters
- `401`: Authentication required — provide X-API-Key or Bearer token
- `403`: Insufficient scope for this operation
- `404`: Not found
- `409`: An X Article on this post may already be live (code ARTICLE_UNCONFIRMED). Check X, then resend with acknowledge_unconfirmed: true.
- `429`: Rate limit exceeded
- `500`: Internal server error

---

[API](https://skmtc.dev/sociahive/apis/sociahive-api.md) · [All operations](https://skmtc.dev/sociahive/apis/sociahive-api/llms.txt) · [OpenAPI document](https://skmtc.dev/sociahive/apis/sociahive-api/revisions/791b8548d288?raw)
