Publishing

Media uploads

Upload images, videos and documents with a presigned URL, attach them to posts with mediaItems, and add alt text, thumbnails and captions.

Posts carry media as URLs in mediaItems. If your file is already on a public HTTPS URL, use that URL directly. If it lives on your server or the user's device, upload it to Pozy first: you ask for an upload URL, send the file there, and put the returned public URL in the post. One upload can be reused by every platform in a post and by later posts.

#Upload a file

#Ask for an upload URL

POST /v1/media/presign takes the file name and its MIME type. Add size in bytes to check the 5 GB limit before you upload.

curl -X POST https://api.pozy.ai/v1/media/presign \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "filename": "demo.mp4", "contentType": "video/mp4", "size": 48213504 }'
Response
{
  "uploadUrl": "https://…signed-upload-url…",
  "publicUrl": "https://pozy.ai/media/temp/1730289600_r7t2_demo.mp4",
  "key": "temp/1730289600_r7t2_demo.mp4",
  "expiresIn": 3600
}

uploadUrl is valid for expiresIn seconds (one hour). publicUrl is the address you put in the post.

#PUT the file

Send the raw bytes to uploadUrl with the same Content-Type you declared. Do not add an Authorization header: the signature in the URL is the permission.

curl -X PUT "$UPLOAD_URL" \
  -H "Content-Type: video/mp4" \
  --data-binary @demo.mp4

A successful upload answers 200 with an empty body.

#Attach it to a post

Put publicUrl in mediaItems:

curl -X POST https://api.pozy.ai/v1/posts \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Two minutes, start to finish.",
    "mediaItems": [{ "type": "video", "url": "https://pozy.ai/media/temp/1730289600_r7t2_demo.mp4" }],
    "platforms": [
      { "platform": "youtube", "accountId": "6710c3e2a9f14b0d8e5c7a27", "platformSpecificData": { "title": "Pozy in two minutes" } },
      { "platform": "linkedin", "accountId": "6710c3e2a9f14b0d8e5c7a21" }
    ],
    "queuedFromProfile": "670f9b1d4c2e8a7f3b6d5e10"
  }'

#Uploads through the API: about 4 MB

A request to the API itself can carry about 4 MB. Endpoints that take the file in the request body are limited by that: POST /v1/media/upload-direct, porting and identity documents, SMS opt-in screenshots, RCS logos and banners, the WhatsApp profile photo and CSV bulk uploads. A larger body is refused with 413 before Pozy reads it, without the usual JSON error.

Use upload-direct for files up to 4 MB. For anything larger, use POST /v1/media/presign and send the file straight to storage as shown above: presigned uploads take files up to 5 GB.

#Keep uploads alive

Uploaded files sit in temporary storage for 7 days. When a post that uses the file is created or updated, Pozy copies it to permanent storage, so a post scheduled months ahead keeps its media.

The catch is the gap between upload and post. If your app uploads when the person picks a file but creates the post a week later, the file is gone. A draft still saves, but scheduling or publishing it fails with 400. Either create the post soon after the upload, or upload when the post is created.

#Accepted file types

Kind Formats contentType values
Images JPEG, PNG, WebP, GIF image/jpeg, image/png, image/webp, image/gif
Videos MP4, MPEG, MOV, AVI, WebM, M4V video/mp4, video/mpeg, video/quicktime, video/avi, video/x-msvideo, video/webm, video/x-m4v
Documents PDF (LinkedIn document posts) application/pdf
Audio MP3, M4A, AAC, OGG, WAV, WebM audio/mpeg, audio/mp4, audio/x-m4a, audio/aac, audio/ogg, audio/wav, audio/webm
Captions SRT, WebVTT application/x-subrip, text/vtt

Every type can be up to 5 GB at upload. Platforms are much stricter, and each has its own rules for count, size, duration and aspect ratio: see the media section of each platform page. Audio cannot be posted on its own. Use it as an attachment in inbox messages.

A presign request with a type outside this list, such as application/octet-stream, returns 400 with code: "invalid_field_value" and param: "contentType".

#Media item fields

Field What it does
type image, video, gif or document. Pozy infers it from the file extension when you leave it out, and rejects a type that contradicts the extension.
url Public HTTPS URL that returns the file itself.
altText Accessibility text for images. Used where the platform supports it, for example Instagram (cut at 1,000 characters) and Bluesky.
thumbnail Custom cover for a video, JPG or PNG up to 10 MB. Used for Facebook videos and Reels, YouTube and LinkedIn videos.
instagramThumbnail Cover image for an Instagram Reel.
subtitles Up to 20 caption files for a video, each { "url": "...", "language": "en" }.
title, filename, size, mimeType Optional details stored with the item.

Order matters: items are published in the order you list them, which sets the order of a carousel.

#Captions on videos

Attach SRT or WebVTT files, one per language, and Pozy converts them for each platform that accepts captions:

mediaItems
[
  {
    "type": "video",
    "url": "https://pozy.ai/media/temp/1730289600_r7t2_demo.mp4",
    "subtitles": [
      { "url": "https://pozy.ai/media/temp/1730289600_c1a9_demo.en.vtt", "language": "en" },
      { "url": "https://pozy.ai/media/temp/1730289600_c1b3_demo.es.srt", "language": "es" }
    ]
  }
]

Caption files must be public and under 1 MB. A platform that cannot take a track skips it and adds a warning to the response. A file Pozy cannot parse returns 400.

#Different media per platform

mediaItems applies to every entry. Use customMedia on one platforms[] entry to replace it there, for example a square crop for Instagram and a wide one elsewhere:

Request body (excerpt)
{
  "mediaItems": [{ "type": "image", "url": "https://pozy.ai/media/temp/1730289600_k3f9_wide.jpg" }],
  "platforms": [
    { "platform": "linkedin", "accountId": "6710c3e2a9f14b0d8e5c7a21" },
    {
      "platform": "instagram",
      "accountId": "6710c3e2a9f14b0d8e5c7a22",
      "customMedia": [{ "type": "image", "url": "https://pozy.ai/media/temp/1730289600_m2p7_square.jpg" }]
    }
  ]
}

#Prepare a file for a platform

When you are not sure a file meets a platform's rules, POST /v1/media/prepare runs the same conversion Pozy applies before publishing and gives you the result. Send the file URL and a target:

Request body
{ "url": "https://pozy.ai/media/temp/1730289600_r7t2_demo.mp4", "target": "instagram" }

target is one of instagram, instagram_story, facebook, facebook_reel, facebook_story, threads, tiktok, linkedin, twitter, bluesky, pinterest, reddit, snapchat, telegram, googlebusiness or youtube_thumbnail. The response says whether anything changed, and gives the new url with originalSize and outputSize. Images are converted to a format the platform accepts and compressed under its limit; videos are re-encoded or resized when needed.

The call is synchronous, so a long video can take a few minutes. A workspace can prepare up to 60 files per hour; past that you get 429.

#URLs that fail

The most common media failure is a URL that does not return the file. Links to Google Drive, Dropbox, OneDrive or iCloud pages return an HTML page, and platforms cannot publish a web page. Open the URL in a private browser window: if you see a page instead of the image or video, upload the file to Pozy instead. POST /v1/tools/validate/media checks a URL for you and reports its type, size and how it compares with each platform's limit (Validation).

Error Cause Fix
400 on presign Missing filename, a contentType outside the list, or size over 5 GB. Send the real MIME type.
Upload refused by storage uploadUrl is more than an hour old, or the Content-Type differs from the presign call. Ask for a new URL and send the same type.
400 when scheduling The upload is more than 7 days old and was never attached to a post. Upload again.
Entry failed with user_content The file breaks a platform rule (duration, size, ratio). Check the platform page, or run prepare first.

#Endpoints

Endpoint What it does
POST /v1/media/presign Get upload URL
POST /v1/media/prepare Prepare media for a platform
Endpoint What it does
POST /v1/tools/validate/media Validate media URL