---
title: "Create a new social media post"
method: POST
path: "/api/v1/workspaces/{workspace_id}/posts"
tags: ["📝 Posts"]
---

# Create a new social media post

`POST /api/v1/workspaces/{workspace_id}/posts`

Create a new social media post with simplified payload structure

## Path parameters

- `workspace_id` string, required

## Request body

- object
  - `content` object, required
    - `text` string, required
    - `media` object — Media content for the post
      - `images` string[]
      - `video` string, url
      - `media_ids` string[] — Array of media library IDs (from GET /media endpoint). Media already in the library will be used directly without re-uploading. Can be combined with images/video URLs.
  - `platform_overrides` object — Optional per-platform content overrides, keyed by platform name (facebook, instagram, twitter, linkedin, pinterest, youtube, tiktok, gmb, tumblr, threads, bluesky, telegram). `content.text` merges independently with the top-level `content.text` — an override with only `media`/`post_type` still keeps the common text. `content.media` (images + video together) is atomic: an override that includes a `media` key defines that platform's media ENTIRELY — whichever of `images`/`video` it doesn't set is empty/none for that platform, not inherited from the common `content.media`. An override with no `media` key at all inherits the common images/video wholesale. This matters because a platform can never publish mixed images+video (only Facebook/Instagram support it) — e.g. a TikTok override supplying only `video` gets no images at all, rather than inheriting the common images and becoming an invalid mixed post. `content.post_type` overrides the top-level `post_type` for that platform only (same allowed values); omitted, that platform falls back to the top-level `post_type`. Omit `platform_overrides` entirely to publish the same `content` to every platform.
    - `facebook` object
      - `content` object
        - `text` string
        - `post_type` 'feed' | 'feed+reel' | 'reel' | 'carousel' | 'story' | 'feed+story' | 'feed+reel+story' | 'reel+story' | 'carousel+story' | 'video' | 'shorts' | 'poll' — Overrides the top-level post_type for Facebook only.
        - `media` object
          - `images` string[]
          - `video` string, url
    - `instagram` object
      - `content` object
        - `text` string
        - `post_type` 'feed' | 'feed+reel' | 'reel' | 'carousel' | 'story' | 'feed+story' | 'feed+reel+story' | 'reel+story' | 'carousel+story' | 'video' | 'shorts' | 'poll' — Overrides the top-level post_type for Instagram only.
        - `media` object
          - `images` string[]
          - `video` string, url
    - `tiktok` object
      - `content` object
        - `text` string
        - `post_type` 'video' | 'carousel' — Overrides the top-level post_type for TikTok only.
        - `media` object
          - `images` string[]
          - `video` string, url
  - `accounts` string[], required — Array of account IDs to post to (platform will be automatically detected). Optional if content_category_id is provided.
  - `content_category_id` string — Optional content category ID. When provided, accounts from the category will be used. Can be combined with accounts array for merged unique accounts.
  - `post_type` 'feed' | 'feed+reel' | 'reel' | 'carousel' | 'story' | 'feed+story' | 'feed+reel+story' | 'reel+story' | 'carousel+story' | 'video' | 'shorts' | 'poll' — Optional post type. See docs for platform-specific supported values. Defaults to 'feed' when omitted. For Facebook carousel posts you may set post_type='carousel', but you MUST also provide facebook_options.carousel.is_carousel_post=true with at least 2 cards — otherwise it is rejected with 400. For a LinkedIn poll set post_type='poll' and provide linkedin_options.poll (text-only); the poll is ignored for any other post_type.
  - `post_video_title` string — Optional video title for platforms that support it (YouTube, LinkedIn). Will be set in common_sharing_details video title, YouTube options, and LinkedIn options.
  - `hide_client` boolean — Optional. When true, hides draft posts from client users. Ignored and stored as false for non-draft posts.
  - `scheduling` object, required
    - `publish_type` 'scheduled' | 'draft' | 'queued' | 'content_category', required
    - `scheduled_at` string, date-time
  - `first_comment` object — Optional first comment for the post
    - `message` string, required
    - `accounts` string[], required — Account IDs for first comment (must be subset of main accounts)
  - `gmb_options` object — Optional Google Business Profile specific options
    - `topic_type` 'STANDARD' | 'EVENT' | 'OFFER'
    - `start_date` string, date
    - `end_date` string, date
    - `title` string
    - `action_type` 'BOOK' | 'ORDER' | 'LEARN_MORE'
    - `cta_link` string, uri
  - `youtube_options` object — Optional YouTube specific options
    - `title` string
    - `privacy_status` 'public' | 'private'
    - `category` 'CARS_VEHICLES' | 'COMEDY' | 'EDUCATION' | 'ENTERTAINMENT' | 'FILM_ANIMATION' | 'GAMING' | 'HOW_TO_STYLE' | 'MUSIC' | 'NEWS_POLITICS' | 'NON_PROFITS_ACTIVISM' | 'PEOPLE_BLOGS' | 'PETS_ANIMALS' | 'SCIENCE_TECHNOLOGY' | 'SPORT' | 'TRAVEL_EVENTS'
    - `tags` string[]
    - `license` 'youtube' | 'creativeCommon'
    - `made_for_kids` boolean
  - `tiktok_options` object — Optional TikTok specific options
    - `privacy_level` 'PUBLIC_TO_EVERYONE' | 'MUTUAL_FOLLOW_FRIENDS' | 'SELF_ONLY'
    - `disable_comment` boolean
    - `disable_duet` boolean
    - `disable_stitch` boolean
    - `auto_add_music` boolean
    - `brand_content_toggle` boolean
    - `brand_organic_toggle` boolean
    - `disclose_commercial_content` boolean
    - `is_aigc` boolean
  - `twitter_options` object — Optional Twitter/X thread options
    - `has_threaded_tweets` boolean
    - `threaded_tweets` object[]
      - `message` string
      - `media` string[] — Media URLs or media library IDs. Twitter/X allows multiple images or one video only.
  - `threads_options` object — Optional Threads multi-post options
    - `has_multi_threads` boolean
    - `multi_threads` object[]
      - `message` string
      - `media` string[] — Media URLs or media library IDs. Threads allows mixed images and videos in the same item.
  - `pinterest_options` object — Optional Pinterest specific options (saved to sharing details)
    - `title` string
    - `link` string, uri
  - `facebook_options` object — Optional Facebook specific options.
    - `facebook_background_id` string, nullable — Facebook text-format preset ID for colored-background text posts. Only applies to plain text posts — the request is rejected if media (images, video, or media_ids) is also provided. Use GET /api/v1/facebook/text-backgrounds to list valid preset IDs.
    - `collaborators` string[] — Facebook Reel collaborators to invite. Each item is a Facebook Page identifier — a numeric Page ID (recommended), an @username, or a Page URL (resolved to a Page ID at publish time). Applies only to Reels published to Facebook Pages; personal profiles are not valid collaborators. Facebook allows up to 10 invites per Page per 24 hours.
    - `carousel` object — Optional Facebook carousel post. When enabled, the post is published as a multi-card carousel on the selected Facebook Pages instead of a regular feed post. Carousel is image-only — video is not allowed. If `content.media.images` is empty, card images are auto-mirrored so non-Facebook accounts in the same post still receive images.
      - `is_carousel_post` boolean — Set true to publish as carousel.
      - `cards` object[] — Between 2 and 10 carousel cards. Required when is_carousel_post is true.
        - `image` string, uri, required — Card image URL. External URLs are uploaded to the media library; library URLs are used as-is.
        - `title` string
        - `description` string
        - `link` string, uri, required — Destination URL when the card is tapped.
      - `accounts` string[] — Optional subset of `accounts[]` (workspace account IDs) to publish as carousel. Only Facebook accounts in this subset will use carousel mode; other Facebook accounts in `accounts[]` will publish normally. Omit to apply carousel to ALL Facebook accounts in the post.
      - `call_to_action` 'NO_BUTTON' | 'ADD_TO_CART' | 'APPLY_NOW' | 'BET_NOW' | 'BOOK_TRAVEL' | 'BUY_NOW' | 'BUY_TICKETS' | 'CALL_NOW' | 'CONTACT_US' | 'DOWNLOAD' | 'GET_DIRECTIONS' | 'GET_OFFER' | 'GET_QUOTE' | 'GO_LIVE' | 'INSTALL_MOBILE_APP' | 'LEARN_MORE' | 'LIKE_PAGE' | 'LISTEN_MUSIC' | 'OPEN_LINK' | 'ORDER_NOW' | 'PLAY_GAME' | 'REGISTER_NOW' | 'REQUEST_TIME' | 'SAVE' | 'MESSAGE_PAGE' | 'WHATSAPP_MESSAGE' | 'SHOP_NOW' | 'SIGN_UP' | 'SUBSCRIBE' | 'USE_APP' | 'WATCH_MORE' | 'WATCH_VIDEO' — CTA button shown on every card. Maps to Facebook Graph `child_attachments.call_to_action.type`.
      - `end_card` boolean — Include a Facebook end card after the last card.
      - `end_card_url` string, uri, nullable — Destination URL for the end card. Falls back to the first card's link when omitted.
  - `instagram_options` object — Optional Instagram specific options.
    - `collaborators` string[] — Instagram collaborators (co-authors) to invite by username. Up to 3. Only public Instagram profiles connected via Facebook can be added; not applicable to Story posts. Cannot be combined with trial_reel.
    - `trial_reel` object — Publish as a trial reel — shown to non-followers first, so it does not appear on the profile grid or in follower feeds. Requires post_type=reel exactly (not feed+reel, which shares to the feed) and a video. Collaborators and share-to-story are not supported alongside it and are dropped. Only available to public professional Instagram accounts that meet Instagram's own trial reel requirements; publishing fails for accounts that do not, and the Instagram error is reported per account. Not available when the workspace publishes Instagram via the mobile app.
      - `enabled` boolean — Publish this reel as a trial reel.
      - `graduation_strategy` 'MANUAL' | 'SS_PERFORMANCE' — How the trial reel is shared with followers. SS_PERFORMANCE lets Instagram graduate it automatically if it performs well; MANUAL requires graduating it by hand in the Instagram app — there is no API for graduating a trial reel.
  - `linkedin_options` object — Optional LinkedIn specific options. A LinkedIn carousel is created automatically when 2+ images are attached (rendered as a PDF document) and applies to every LinkedIn account in accounts[] — no extra flags needed; use title to name that document. Provide poll to publish a text-only poll instead.
    - `title` string, nullable — Title for the LinkedIn document/carousel post (shown on the generated PDF).
    - `accounts` string[] — Optional subset of accounts[] (LinkedIn account IDs) the carousel targets. Only LinkedIn accounts in this subset publish as a carousel; omit to apply the carousel to ALL LinkedIn accounts in the post. Must be LinkedIn accounts present in accounts[]. Ignored for non-carousel posts.
    - `poll` object — LinkedIn poll. Only used when post_type='poll' (ignored otherwise). Poll posts are text-only — the request is rejected if images or a video are also provided. Applies to every LinkedIn account in accounts[].
      - `question` string, required
      - `options` string[], required — Between 2 and 4 poll options, each up to 30 characters.
      - `duration` 'ONE_DAY' | 'THREE_DAYS' | 'SEVEN_DAYS' | 'FOURTEEN_DAYS'
  - `labels` string[] — Optional array of label IDs to attach to the post. Maximum 20 labels. Must be valid label IDs for the workspace.
  - `campaign_id` string, nullable — Optional campaign (folder) ID to assign the post to. Must be a valid campaign ID for the workspace.
  - `approval` object — Optional legacy single-level approval. Sends the post for review by the listed approvers. Mutually exclusive with approval_workflow.
    - `approvers` string[] — User IDs who can approve the post. Must be workspace team members and cannot include the post creator.
    - `approve_option` 'anyone' | 'everyone' — Whether any single approver or all approvers must approve
    - `notes` string, nullable — Optional notes for approvers
  - `approval_workflow` object — Optional multi-level approval workflow. Mutually exclusive with approval. On create, provide workflow_id to attach a workflow (from GET .../approval-workflows) — a failed attach is surfaced as a non-fatal `warning`, not a request error.
    - `workflow_id` string — ID of the workflow to attach (from GET .../approval-workflows).
    - `notes` string, nullable — Optional notes stored with the workflow submission

## Response `201`

Post created successfully

- object
  - `status` boolean
  - `message` string
  - `data` object
    - `id` string
    - `post_url` string
    - `warning` string, nullable — Present only when approval_workflow.workflow_id was provided but the workflow attach failed. The post itself was created successfully; retry the attach via Update Post.

## Other responses

- `400` — Validation failed
- `401` — Unauthorized - Invalid API key
- `403` — Forbidden - No access to workspace
- `429` — Too many requests
- `500` — Internal server error

---

[API](https://skmtc.dev/contentstudio/apis/contentstudio-api-v1.md) · [All operations](https://skmtc.dev/contentstudio/apis/contentstudio-api-v1/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/contentstudio/contentstudio-api-v1/revisions/ec0c85521883/schema)
