Publishing

Create a post

Publish, schedule, queue or save a post to one or many connected accounts with a single POST /v1/posts request.

Every post in Pozy starts with one request to POST /v1/posts. The same body publishes right away, schedules for later, drops into a queue or saves a draft, and it can target any mix of connected accounts. This page walks through that body field by field.

You need an API key and at least one connected account. GET /v1/accounts lists the account ids you can post to (Connecting accounts).

#Publish your first post

This request publishes a text post to one LinkedIn account straight away:

curl -X POST https://api.pozy.ai/v1/posts \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "We just shipped scheduled exports. Details on the blog.",
    "platforms": [
      { "platform": "linkedin", "accountId": "6710c3e2a9f14b0d8e5c7a21" }
    ],
    "publishNow": true
  }'

With publishNow, the request waits for the platform to answer, so the response already holds the result and the public URL:

Response
{
  "post": {
    "_id": "6721a8c04e9b2f1d7c3a5e88",
    "content": "We just shipped scheduled exports. Details on the blog.",
    "status": "published",
    "platforms": [
      {
        "platform": "linkedin",
        "accountId": "6710c3e2a9f14b0d8e5c7a21",
        "status": "published",
        "platformPostId": "urn:li:share:7254471109021356032",
        "platformPostUrl": "https://www.linkedin.com/feed/update/urn:li:share:7254471109021356032",
        "publishedAt": "2026-10-30T10:02:11.000Z"
      }
    ],
    "createdAt": "2026-10-30T10:02:09.000Z"
  }
}

#The request body

These fields sit at the top level of the body. Everything except platforms is optional, and platforms itself is optional for drafts.

Field Type What it does
content string The text of the post. Optional when you attach media or every platform entry has its own customContent.
mediaItems array Images, videos, GIFs or documents shared by every platform entry. See Media.
platforms array One entry per account to publish to. See the next section.
publishNow boolean Publish inside this request.
scheduledFor date-time When to publish. Read with timezone. See Scheduling and timezones.
timezone string IANA zone name such as Europe/Madrid. Defaults to UTC.
queuedFromProfile string A profile id. The post takes the next free slot in that profile's queue. See Queues.
queueId string Pick a specific queue of that profile instead of its default one.
isDraft boolean Save without publishing. Wins over every scheduling field.
requiresApproval boolean Hold the post in pending_approval until someone approves it. See Drafts and approvals.
dryRun boolean Check TikTok publishing eligibility without saving anything. See Validation.
title string A title stored with the post. Platform titles (YouTube, Pinterest, Reddit) go in that platform's platformSpecificData.
tags array of strings Your own labels, for filtering with GET /v1/posts?tags=. They are never published.
hashtags, mentions array of strings Stored on the post for reporting. To publish a hashtag or mention, write it in the text.
metadata object Free-form data you want back later, such as your own record id. Echoed in every post webhook.
recycling object Republish the post on a repeating schedule. See Queues.
tiktokSettings, facebookSettings object Settings applied to every TikTok or Facebook entry. See Platform settings.

#The platforms array

Each entry in platforms sends the post to one connected account. Two entries can point at the same platform when you want to publish to two accounts there.

Field Required What it does
platform Yes The platform id: instagram, tiktok, youtube, facebook, linkedin, twitter (X), threads, pinterest, bluesky, googlebusiness, snapchat, telegram, discord, slack and others.
accountId Yes The connected account to publish from.
customContent No Replaces content for this entry only.
customMedia No Replaces mediaItems for this entry only.
scheduledFor No Replaces the top-level scheduledFor for this entry only.
platformSpecificData No Options that only this platform understands, such as an Instagram first comment or a YouTube title. The shape depends on platform.
Warning

Any account in your workspace is accepted, whichever profile it belongs to. If you publish on behalf of your own customers, only pass account ids you have stored against that customer, or use a profile-scoped API key so a mistake is refused.

#Publish now, schedule, queue or draft

The fields you send decide what happens to the post and which status it starts in:

You send What happens First status
publishNow: true Publishes during the request. The response carries the result. published, partial or failed
scheduledFor in the future Publishes at that time. scheduled
scheduledFor in the past Publishes during the request, like publishNow. published, partial or failed
queuedFromProfile Takes the next free slot of the profile's queue. scheduled
requiresApproval: true and a time Waits for approval, then follows the time. pending_approval
isDraft: true, or no scheduling field at all Saved, never published. draft

When more than one applies, isDraft wins over everything, and publishNow wins over scheduledFor.

Tip

A post created with publishNow can fail on one platform and succeed on another. The request still returns a 2xx response (207 when at least one platform failed), so always branch on post.status and on each platforms[].status instead of on whether the call threw.

#Post to several platforms at once

One request can carry different text, media and options per platform. Here LinkedIn gets the long version, X gets a short one, and Instagram gets a square image and a first comment:

curl -X POST https://api.pozy.ai/v1/posts \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: launch-exports-2026-11-03" \
  -d '{
    "content": "Scheduled exports are live. Pick a time, pick a format, and the file lands in your inbox every week.",
    "mediaItems": [{ "type": "image", "url": "https://pozy.ai/media/temp/1730289600_k3f9_exports-wide.jpg" }],
    "scheduledFor": "2026-11-03T09:30:00",
    "timezone": "Europe/Madrid",
    "metadata": { "campaignId": "cmp_2041" },
    "platforms": [
      { "platform": "linkedin", "accountId": "6710c3e2a9f14b0d8e5c7a21" },
      {
        "platform": "twitter",
        "accountId": "6710c3e2a9f14b0d8e5c7a23",
        "customContent": "Scheduled exports are live. Weekly files, zero clicks."
      },
      {
        "platform": "instagram",
        "accountId": "6710c3e2a9f14b0d8e5c7a22",
        "customMedia": [{ "type": "image", "url": "https://pozy.ai/media/temp/1730289600_m2p7_exports-square.jpg" }],
        "platformSpecificData": { "firstComment": "Setup guide: link in bio" }
      }
    ]
  }'

Per-entry fields replace the top-level ones for that entry only. The LinkedIn entry above uses the shared text and image, the X entry swaps the text and keeps the image, and the Instagram entry keeps the text and swaps the image.

#Read the response

The response holds the saved post. The fields you will use most:

Field Meaning
post._id The post id. Use it for every later call: get, update, retry, unpublish.
post.status The overall status. See Post lifecycle.
post.scheduledFor The publish time as a UTC instant, ending in Z.
post.platforms[].status Status of each entry: pending, processing, uploading, published, failed or cancelled.
post.platforms[].platformPostUrl Public URL once that entry is published.
post.platforms[].errorMessage, errorCategory Why an entry failed.
warnings Things Pozy changed or ignored, such as a field placed on the wrong platform entry. The post was still saved.

Read warnings in development. A field put in the wrong place is dropped rather than rejected, and the warning is the only sign.

#Avoid duplicate posts

Networks and job runners retry. To make a retry safe, send an Idempotency-Key header (up to 255 characters) that is unique per logical post. A second request with the same key within 24 hours returns the original post instead of creating a new one, even if the body changed.

Separately, Pozy refuses the same text and media sent to the same account twice within 24 hours with a 409. The error names the existing post so you can link to it:

409 Conflict
{
  "error": "This exact content is already scheduled, publishing, or was posted to this account within the last 24 hours.",
  "details": {
    "accountId": "6710c3e2a9f14b0d8e5c7a21",
    "platform": "linkedin",
    "existingPostId": "6721a8c04e9b2f1d7c3a5e88"
  }
}

To repost on purpose, change the text, the media or the account. More on retries in Idempotency.

#Find out what happened

A scheduled or queued post publishes later, so the create response cannot tell you the outcome. Subscribe to post webhooks instead of polling:

  • post.platform.published and post.platform.failed fire per account as soon as each one finishes.
  • post.published, post.partial or post.failed fires once, after every account has finished.

Put your own id in metadata when you create the post and it comes back in each of those events, so your handler never has to look the post up. See Set up webhooks.

#Endpoints

Endpoint What it does
GET /v1/posts List posts
POST /v1/posts Create post
GET /v1/posts/{postId} Get post
PUT /v1/posts/{postId} Update post
DELETE /v1/posts/{postId} Delete post
POST /v1/posts/bulk-upload Bulk upload from CSV
POST /v1/posts/{postId}/retry Retry failed post
POST /v1/posts/{postId}/review Approve or reject a post
POST /v1/posts/{postId}/unpublish Unpublish post
POST /v1/posts/{postId}/pin Pin post
POST /v1/posts/{postId}/unpin Unpin post
POST /v1/posts/{postId}/edit Edit published post
POST /v1/posts/{postId}/update-metadata Update post metadata

#Webhook events

Event When it fires
post.scheduled Post scheduled.
post.published Post published.
post.failed Post failed.
post.partial Post partial.
post.cancelled Post cancelled.
post.recycled Post recycled.
post.platform.published Post platform published.
post.platform.failed Post platform failed.
post.platform.deleted Post platform deleted.
post.tiktok.url_resolved TikTok post URL resolved.
post.tracking_completed Post tracking completed.
post.authenticity_changed Post authenticity changed.
post.external.created External post created.
post.external.updated External post updated.
post.external.deleted External post deleted.