Guides
Publishing posts
One POST/posts sends a post to every target you list. This guide covers how to shape it, when it goes out, and what comes back.
The shape of a post
A post is shared content and mediaItems, plus where it goes: a list of targets (one per account), a list of groupIds, or both. At least one is required, and a post can have at most 1,000 targets once groups are expanded (more answers 400 too_many_targets). Send an Idempotency-Key header with every request — see retrying safely.
{
"content": "The full session is live.", // shared text
"mediaItems": [{ "type": "video", "url": "https://cdn.example.org/talk.mp4" }],
"targets": [
{ "accountId": "FACEBOOK_ACCOUNT_ID" },
{
"accountId": "YOUTUBE_ACCOUNT_ID",
"customContent": "Full session — link in the description",
"platformSpecific": { "title": "Evening session", "privacy": "unlisted" }
}
],
"groupIds": ["GROUP_ID"], // optional: every account in a group
"publishNow": true // or scheduledFor, or useQueue
}Per-network overrides
Each target can change what its network gets without touching the others:
customContent— different text for this account.customMedia— different media for this account, same shape asmediaItems.platformSpecific— options only that network understands. For example:title,privacy(public,unlisted,private),tagsandmadeForKidson YouTube;boardIdon Pinterest (list boards with GET/org/accounts/{id}/boards);firstCommenton Facebook, Instagram, Threads, LinkedIn and YouTube.
Now, later or queued
Choose exactly one; combining them is a conflicting_schedule error.
| Send | What happens | Response |
|---|---|---|
publishNow: true | Publishes immediately. | 201, 207 or 202 |
scheduledFor | Publishes at that time. Add timezone (IANA, default UTC) to give a local time. | 201, status scheduled |
useQueue: true | Takes the next free slot in the posting queue of the member who owns the key. | 201 |
| none of these | Saves a draft. | 201, status draft |
{
"content": "Tonight at 7 — join us live.",
"groupIds": ["GROUP_ID"],
"scheduledFor": "2026-11-01T19:00:00",
"timezone": "Asia/Kolkata"
}{
"content": "From the archive.",
"targets": [{ "accountId": "INSTAGRAM_ACCOUNT_ID" }],
"useQueue": true
}Results
With publishNow and up to 10 targets the request waits and answers with each network's outcome. With more targets — or with a Prefer: respond-async header — the post is queued and you get 202 with its id. Then either read it with GET/posts/{id} (targets are paginated; filter with ?status=failed), check up to 100 posts at once with GET /posts?ids=id1,id2,… (every target's status, link and error in one call), or subscribe to webhooks and skip polling altogether.
curl "https://dashboard.lifebliss.app/api/v1/posts/POST_ID?status=failed" \
-H "Authorization: Bearer $PS_API_KEY"Partial results and retries
If some networks publish and others don't, the post is partial — answered with 207and announced as post.partial. The successes are live and stay live.
HTTP/1.1 207 Multi-Status
{
"postId": "c1d2…",
"status": "partial",
"targets": [
{ "platform": "facebook", "status": "published", "platformPostUrl": "https://www.facebook.com/…" },
{
"platform": "instagram",
"status": "failed",
"error": { "message": "Instagram needs an image or video.", "category": "user_content", "source": "user" }
}
]
}Fix what you can (the error category tells you whether it's worth it), then call POST/posts/{id}/retry. Only failed targets run again, so nothing is published twice.
Cancelling
DEL/posts/{id} cancels a draft or scheduled post that hasn't gone out yet. Only its author or an admin can cancel it.
Validate before you send
POST/validate checks text and media against each network's rules without publishing and without a key. error issues would block the post; warning issues wouldn't. GET/validate returns every network's limits, so you can show character counts as people type.
curl -X POST "https://dashboard.lifebliss.app/api/v1/validate" \
-H "Content-Type: application/json" \
-d '{ "content": "A long caption…", "platforms": ["bluesky", "threads"] }'
# → { "valid": false,
# "issues": [{ "platform": "bluesky", "level": "error",
# "field": "content", "message": "…" }] }