Images

Attach post media

Attaches an existing image, video, PDF or library carousel (or a URL image, PDF or video) to a draft, without scheduling or AI generation. On a LinkedIn post an image is added after the draft's images (2 to 20 publish as one multi-image post; past 20 is 409 image_limit_reached) and replaces only a saved carousel, video or PDF; a carousel, video or PDF replaces everything. To swap an image, attach the new one and detach the old one by imageId. On an X post the media is added and what is there stays: an image to a tweet (max 4) or to the article, or one MP4 or MOV video alone in a tweet. Provide exactly one of url, assetId, imageId or carouselId.

POST/api/public/posts/:id/media
cURLPOST /api/public/posts/:id/media
curl -X POST https://postiv.ai/api/public/posts/POST_ID/media \
  -H "Authorization: Bearer pk_postiv_..." \
  -H "Content-Type: application/json" \
  -d '{"imageId":"IMAGE_ID","tweetIndex":0}'
200Response
{
  "success": true,
  "postId": "post1...",
  "status": "draft",
  "media": {
    "type": "image",
    "title": "launch.png",
    "assetId": "ast1...",
    "imageId": "img1...",
    "versionId": "v1...",
    "url": "https://postiv-user-media.s3..."
  },
  "channel": "x",
  "assetId": "ast1...",
  "tweetIndex": 0,
  "replacedAssetIds": [],
  "postVersionNumber": 3,
  "placedIn": "tweetIndex 0 (0-based)",
  "openInPostivUrl": "https://postiv.ai/app/create?postId=post1..."
}

Authorization

Requires an organization API key with the posts:write, images:write scope, sent as Authorization: Bearer or x-api-key.

Path parameters

idstringrequired

Draft post id. LinkedIn: an image is added after the draft's images (up to 20) and replaces only a carousel, video or PDF; a carousel, video or PDF replaces everything. X: an image is added (max 4 per tweet) or a video placed alone in a tweet, and existing media is kept.

Body parameters

urlstring

Public HTTP(S) image, PDF or video URL, downloaded into Postiv storage (JPEG, PNG, GIF or WebP up to 10MB; PDF up to 50MB; MP4, MOV, WebM, AVI, WMV or FLV up to 200MB. X takes MP4 and MOV only).

assetIduuid

Uploaded library image, video or PDF asset id from list_media (on X, an MP4 or MOV video alone in a tweet).

imageIduuid

Existing Postiv image id from list_media or generate_post_image. Reused without moving it off another post.

carouselIduuid

LinkedIn only: a draft library carousel is saved on the draft. Infographics go to schedule-post instead. An X post returns 400 x_media_not_supported.

fileNamestring

Optional display name for a URL import, 1-240 chars.

tweetIndexint

X posts only: the 0-based tweet that gets the image. Defaults to the first tweet.

articlePlacementstring

X articles only: 'cover' (default) or 'inline'.

One of:coverinline
afterParagraphint

X articles only, with articlePlacement 'inline': the 0-based paragraph the image follows.

Response

The attached image and, for X, where it was placed.

successboolean
postIduuid
statusstring

Post status.

mediaobject

{ type, title, fileName, s3Key, contentType, assetId, imageId, versionId, url }. assetId is the asset inside an X post (pass it to detach-post-media); imageId is the working image for edit-post-image.

replacedExistingMediaboolean

true if a saved carousel, video or PDF was replaced, or the draft's earlier media was replaced by a carousel, video or PDF (LinkedIn), or an image generated earlier for the same slot was swapped (X).

imageCountnumber

LinkedIn image attaches only: how many images the draft now has (up to 20).

channel / assetId / tweetIndex / tweetId / articlePlacement / afterParagraph / replacedAssetIds / postVersionNumber / placedInmixed

X posts only: where the image sits, the assets it replaced and the post's new version number.

openInPostivUrlstring

Errors

400invalid_media_source: not exactly one of url, assetId, imageId, carouselId / media_requires_scheduling: an infographic on a LinkedIn draft / invalid_image: not a decodable image or over 40 megapixels
400x_media_not_supported: an X post takes images and MP4/MOV videos only (no PDF, carousel, infographic or other video format)
400placement_not_supported: a placement field sent for a LinkedIn post / x_placement_invalid / x_placement_mismatch / x_tweet_not_found / x_article_paragraph_not_found: the placement does not fit the X post
404post_not_found / media_not_found
409post_media_locked: the X post is scheduled or published / x_tweet_image_limit: that tweet already has 4 images / x_tweet_media_conflict: a video and images in one tweet, or a video in an article

Good to know

  • The library asset itself goes into the post: your own image, or one shared with the workspace. Images added in the Postiv editor are never removed by this call.
  • MCP twin: attach_post_media.