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.
/api/public/posts/:id/mediacurl -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}'{
"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
idstringrequiredDraft 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
urlstringPublic 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).
assetIduuidUploaded library image, video or PDF asset id from list_media (on X, an MP4 or MOV video alone in a tweet).
imageIduuidExisting Postiv image id from list_media or generate_post_image. Reused without moving it off another post.
carouselIduuidLinkedIn 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.
fileNamestringOptional display name for a URL import, 1-240 chars.
tweetIndexintX posts only: the 0-based tweet that gets the image. Defaults to the first tweet.
articlePlacementstringX articles only: 'cover' (default) or 'inline'.
coverinlineafterParagraphintX articles only, with articlePlacement 'inline': the 0-based paragraph the image follows.
Response
The attached image and, for X, where it was placed.
successbooleanpostIduuidstatusstringPost 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.
replacedExistingMediabooleantrue 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).
imageCountnumberLinkedIn image attaches only: how many images the draft now has (up to 20).
channel / assetId / tweetIndex / tweetId / articlePlacement / afterParagraph / replacedAssetIds / postVersionNumber / placedInmixedX posts only: where the image sits, the assets it replaced and the post's new version number.
openInPostivUrlstringErrors
| 400 | invalid_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 |
| 400 | x_media_not_supported: an X post takes images and MP4/MOV videos only (no PDF, carousel, infographic or other video format) |
| 400 | placement_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 |
| 404 | post_not_found / media_not_found |
| 409 | post_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.