Publishing
Every post status and per-platform status, how a post moves between them, what you can do in each, and which webhook fires on each change.
A post has one overall status and one status per entry in platforms. The overall status is a summary of the entries: a post is published only when every entry published, and partial when some did and some did not. This page lists every status, the moves between them and the webhook that tells you about each move.
#Overall statuses
| Status | What it means |
|---|---|
draft |
Saved, not going anywhere. The default when you send no scheduling field. |
pending_approval |
Saved with requiresApproval: true. It never publishes until someone approves it. |
scheduled |
Has a future publish time, set by you or by a queue. |
publishing |
Short-lived. Pozy is sending it to the platforms right now. |
published |
Every entry published. |
partial |
Some entries published, at least one failed. |
failed |
Every entry failed. |
cancelled |
Nothing will publish: either its account was disconnected before the publish time, or you removed every published copy with unpublish. |
#How a post moves
The main path is short:
draft ──► scheduled ──► publishing ──┬──► published
├──► partial
└──► failedEverything else is a side door into or out of that path:
| From | To | What moves it |
|---|---|---|
draft |
scheduled |
An update with isDraft: false plus a time, publishNow or queuedFromProfile. |
draft |
pending_approval |
An update with requiresApproval: true and a time. |
pending_approval |
scheduled |
An approval. If the time has passed, it publishes within a couple of minutes. |
pending_approval |
draft |
A rejection. |
scheduled |
cancelled |
The account was disconnected before the publish time. |
failed, partial |
scheduled |
A retry. Only the failed entries go out again. |
published, partial |
cancelled |
Every published copy was removed with unpublish. Unpublishing some copies leaves the post partial. |
cancelled |
scheduled |
An update with a new time. A cancelled post is editable like a draft. |
A publishNow post, or one whose scheduledFor is already in the past, goes straight from the request to publishing and comes back as published, partial or failed.
#Per-platform statuses
Each entry in post.platforms has its own status:
| Entry status | What it means |
|---|---|
pending |
Waiting for the publish time. |
processing |
Media is being prepared for this platform. |
uploading |
Pozy is uploading to the platform. Some platforms, like TikTok and YouTube, take minutes here. |
published |
Live. platformPostId, platformPostUrl and publishedAt are set. |
failed |
The platform refused it or it could not be sent. errorMessage, errorCategory and errorSource explain why. |
cancelled |
Removed from the platform with unpublish, or cancelled before publishing. |
An entry can be deleted on the platform after it published, for example when the owner removes it in the Instagram app. The entry keeps status: "published" and gains removedFromPlatformAt, and a post.platform.deleted webhook fires. Detection runs in the background roughly every hour, so expect some delay.
#Read the status
curl https://api.pozy.ai/v1/posts/6721a8c04e9b2f1d7c3a5e88 \
-H "Authorization: Bearer $POZY_API_KEY"const res = await fetch("https://api.pozy.ai/v1/posts/6721a8c04e9b2f1d7c3a5e88", {
headers: { Authorization: `Bearer ${process.env.POZY_API_KEY}` },
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);
for (const entry of data.post.platforms) {
console.log(entry.platform, entry.status, entry.errorCategory ?? "");
}resp = requests.get(
"https://api.pozy.ai/v1/posts/6721a8c04e9b2f1d7c3a5e88",
headers={"Authorization": f"Bearer {os.environ['POZY_API_KEY']}"},
timeout=30,
)
for entry in resp.json()["post"]["platforms"]:
print(entry["platform"], entry["status"], entry.get("errorCategory", "")){
"post": {
"_id": "6721a8c04e9b2f1d7c3a5e88",
"status": "partial",
"scheduledFor": "2026-11-03T08:30:00.000Z",
"platforms": [
{
"platform": "linkedin",
"accountId": "6710c3e2a9f14b0d8e5c7a21",
"status": "published",
"platformPostUrl": "https://www.linkedin.com/feed/update/urn:li:share:7254471109021356032"
},
{
"platform": "instagram",
"accountId": "6710c3e2a9f14b0d8e5c7a22",
"status": "failed",
"errorMessage": "The video is too short to publish as a Reel.",
"errorCategory": "user_content",
"errorSource": "user"
}
]
}
}For anything that changes after creation, prefer webhooks to polling. Use GET /v1/posts with updatedSince to reconcile after downtime.
#Why an entry failed
Failed entries carry a readable errorMessage and two fields you can branch on. errorSource says who can fix it, and errorCategory says what kind of problem it is. Some entries also carry platformError with the platform's own code, subcode and message.
errorCategory |
What happened | What to do |
|---|---|---|
auth_expired |
The account's access was revoked or expired. | Ask the owner to reconnect. Account health spots this before a post fails. |
user_content |
The post breaks a platform rule: length, format, media size or duration. | Fix the content, then retry or update the post. |
user_abuse |
The platform flagged the activity as spam or too frequent. | Slow down and space posts out. |
account_issue |
Something on the account blocks it, such as the wrong account type. | Fix it on the platform. |
platform_rejected |
The platform refused the content on policy grounds. | Change the content. |
platform_error |
The platform had an outage or a server error. | Retry later. |
platform_rate_limit |
The platform is throttling requests. | Pozy retries these on its own. |
quota_exhausted |
A daily platform quota ran out. | It works again after the platform's quota resets. |
system_error |
Something failed on Pozy's side. | Retry. Contact support@pozy.ai if it keeps happening. |
unknown |
Not classified. | Read errorMessage. |
errorSource |
Meaning |
|---|---|
user |
You or your user must change something before a retry can work. |
platform |
A platform-side problem. Retrying later usually works. |
system |
A Pozy-side problem. Retrying usually works. |
#Retry a failed post
POST /v1/posts/{postId}/retry publishes the failed entries of a failed or partial post again. Entries that already published are left alone, so a retry never double-posts.
curl -X POST https://api.pozy.ai/v1/posts/6721a8c04e9b2f1d7c3a5e88/retry \
-H "Authorization: Bearer $POZY_API_KEY"const res = await fetch("https://api.pozy.ai/v1/posts/6721a8c04e9b2f1d7c3a5e88/retry", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.POZY_API_KEY}` },
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);
console.log(data.post.status);resp = requests.post(
"https://api.pozy.ai/v1/posts/6721a8c04e9b2f1d7c3a5e88/retry",
headers={"Authorization": f"Bearer {os.environ['POZY_API_KEY']}"},
timeout=60,
)
print(resp.json()["post"]["status"])Fix user errors before you retry, otherwise the same error comes back. For a content fix, update the post with PUT /v1/posts/{postId} first, then retry.
#What you can do in each status
| Action | Allowed when the post is | Notes |
|---|---|---|
Update (PUT /v1/posts/{postId}) |
draft, scheduled, failed, partial, cancelled |
On a published post only the recycling settings can change. |
Approve or reject (POST .../review) |
pending_approval |
Any other status returns 409. |
Retry (POST .../retry) |
failed, partial |
Only failed entries are sent again. |
Edit published text (POST .../edit) |
published, partial |
Text only, on platforms that allow edits. |
Unpublish (POST .../unpublish) |
published, partial |
Removes the copy from one platform. |
Delete (DELETE /v1/posts/{postId}) |
Anything except published |
Removes the record from Pozy. It does not touch the platform and sends no webhook. |
#Webhooks for each change
| Event | Fires when |
|---|---|
post.scheduled |
The post enters scheduled: created with a time, added to a queue, a draft promoted, an approval, or a retry. Editing the time of a post that is already scheduled does not fire it again. |
post.platform.published |
One entry published. Fires per entry, without waiting for the others. |
post.platform.failed |
One entry failed for good. Errors Pozy is still retrying stay quiet. |
post.published |
Every entry published. |
post.partial |
Some entries published, others failed. |
post.failed |
Every entry failed. |
post.cancelled |
A scheduled post was cancelled because its account was disconnected and nothing had published. |
post.recycled |
A recycling schedule created a new copy of a published post. |
post.tiktok.url_resolved |
A published TikTok entry got its public URL. TikTok assigns it a few minutes after upload, so post.published can arrive with an empty TikTok URL. |
post.platform.deleted |
A published entry was found deleted on the platform. |
Exactly one of post.published, post.partial and post.failed fires each time a post publishes. Per-entry events usually arrive first, but do not rely on the order: see Webhook idempotency and ordering.
#Endpoints
| Endpoint | What it does |
|---|---|
GET /v1/posts/{postId} |
Get post |
GET /v1/posts |
List posts |
POST /v1/posts/{postId}/retry |
Retry failed post |
POST /v1/posts/{postId}/review |
Approve or reject a post |
PUT /v1/posts/{postId} |
Update post |
DELETE /v1/posts/{postId} |
Delete post |
POST /v1/posts/{postId}/unpublish |
Unpublish post |
POST /v1/posts/{postId}/edit |
Edit published post |
#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. |