---
title: "/posts/{post_id}/retract"
method: POST
path: "/projects/{project_id}/posts/{post_id}/retract"
tags: ["Posts"]
---

# /posts/{post_id}/retract

`POST /projects/{project_id}/posts/{post_id}/retract`

## Request

- Base URL: `https://api.productbyte.com/v1`
- URL: `https://api.productbyte.com/v1/projects/{project_id}/posts/{post_id}/retract`
- Auth: HTTP bearer

Removes the published copy from the channel and marks the post `deleted`. The ProductByte record stays, so the post keeps its history and analytics — use `DELETE` to forget the record instead.

Only a `published` post can be retracted, and only on channels that support deletion. Instagram and TikTok do not: they answer `API_CHANNEL_RETRACT_UNSUPPORTED` and the post must be removed in the provider's own app.

The channel is called synchronously. A `200` means the provider confirmed the removal; if it refuses, the post stays `published` and nothing is changed here.

Requires the `posts:write` scope.

## Path parameters

- `project_id` string, required: The project the post belongs to.
- `post_id` string, required: The published post to retract.

## Response `200`

The retracted post, now `deleted`.

- object
  - `id` string, required: Identifier for this post.
  - `channel_id` string, required: The channel it publishes to.
  - `campaign_id` string: The campaign it belongs to. Absent when the post is unassigned — posts still need a home internally, but that grouping is bookkeeping and is never reported as a campaign.
  - `status` 'draft' | 'scheduled' | 'publishing' | 'published' | 'failed' | 'deleted', required: Where the post actually got to. Poll this to learn the outcome.
  - `copy` string, required: The text published alongside the media.
  - `scheduled_at` string, date-time: When it publishes, if it is scheduled.
  - `published_at` string, date-time: When it published, once it has.
  - `permalink` string: Link to the published post, when the provider gives one.
  - `updated_at` string, date-time, required: When the post last changed. Pass the newest one back as `updated_since` to poll for what changed.
  - `replies` object[]: Comments queued under this post, in the order they publish. Absent when there are none.
    - `id` string, required: Identifier for this reply.
    - `copy` string, required: The comment text.
    - `delay_seconds` integer: Wait after the previous item published.
    - `status` 'pending' | 'publishing' | 'published' | 'failed' | 'skipped', required: Where this reply got to. `skipped` means the chain stopped before reaching it.
    - `published_at` string, date-time: When it published, once it has.
  - `settings` object: How this post publishes, as stored. Covers only what this API accepts — a post made in ProductByte may carry more, such as a Reel cover. `link_url` is the effective link: a campaign’s UTM tags are applied to it, so it can differ from the one sent.
    - `link_url` string: Destination URL. X shows it as a call to action on media and a Pin opens it; other channels ignore it.
    - `meta` object: Instagram and Facebook Page settings. A Reel cover and per-image alt text are set in ProductByte rather than here: both are keyed by internal asset ids this API does not expose.
      - `mode` 'feed_post' | 'story_post' | 'reel_post', required: Which Instagram surface to publish to. A Facebook Page takes `feed_post` only. A single video on `feed_post` publishes as a Reel — Instagram has no other container for it.
      - `title` string: Headline for a Facebook Page video. Ignored elsewhere.
      - `share_to_feed` boolean: Reels only. `false` keeps the Reel out of your profile feed, leaving it in the Reels tab.
      - `trial_reel` boolean: Reels only. Publishes to non-followers first. Accounts Instagram finds ineligible publish an ordinary Reel instead, reported back on the post.
      - `ai_generated` boolean: Discloses AI use. Instagram requires it for photorealistic video.
      - `collaborators` string[]: Up to 3 public Instagram usernames, without the leading @, invited to co-author. Instagram does not allow these on a trial reel or a Story.
    - `tiktok` object: TikTok settings.
      - `title` string: Title for the TikTok post. At most 90 characters.
      - `direct_post` object: Publishes straight to TikTok instead of sending the video to the creator’s inbox. Only on a `draft`. TikTok requires the creator to see its consent terms — the Branded Content Policy and Music Usage Confirmation — and to choose from the privacy options their account currently offers. An API call cannot show them either, so a Direct Post is prepared here and published from ProductByte, where those screens exist. Sending this with `publish_type` of `scheduled` or `immediate` is refused.
        - `privacy_level` 'PUBLIC_TO_EVERYONE' | 'MUTUAL_FOLLOW_FRIENDS' | 'FOLLOWER_OF_CREATOR' | 'SELF_ONLY', required: Who can see the post. Required, and must be one the creator currently allows.
        - `allow_comment` boolean, required: Whether viewers may comment.
        - `allow_duet` boolean: Video only.
        - `allow_stitch` boolean: Video only.
        - `auto_add_music` boolean: Photo posts only.
        - `is_aigc` boolean: Discloses AI-generated content.
        - `your_brand` boolean: The post promotes your own brand.
        - `branded_content` boolean: The post promotes a third party. Cannot be combined with `SELF_ONLY`.
    - `pinterest` object: Pinterest settings. Required on a Pinterest channel: every Pin goes on a board.
      - `board_id` string, required: The board the Pin goes on, one of the account’s boards.
      - `title` string: The Pin title. At most 100 characters.
      - `alt_text` string: What the picture shows, for screen readers. At most 500 characters.

## Other responses

- `401`: Missing, unknown, revoked, or expired key (`API_KEY_UNAUTHORIZED`).
- `403`: The key lacks `posts:write`, or the project is outside its allowlist.
- `404`: No such post in this project (`API_POST_NOT_FOUND`).
- `409`: The post is not `published`, so there is nothing on the channel to remove (`API_POST_NOT_RETRACTABLE`).
- `422`: The channel cannot delete its own posts (`API_CHANNEL_RETRACT_UNSUPPORTED`).
- `429`: Too many requests for this key (`API_RATE_LIMITED`). Back off and retry; `Retry-After` gives the window length.
- `502`: The channel refused the removal (`API_RETRACT_FAILED`). The post is unchanged and still published.

---

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