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.

POST /posts — body
{
  "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 as mediaItems.
  • platformSpecific — options only that network understands. For example: title, privacy (public, unlisted, private), tags and madeForKids on YouTube; boardId on Pinterest (list boards with GET/org/accounts/{id}/boards); firstComment on Facebook, Instagram, Threads, LinkedIn and YouTube.

Now, later or queued

Choose exactly one; combining them is a conflicting_schedule error.

SendWhat happensResponse
publishNow: truePublishes immediately.201, 207 or 202
scheduledForPublishes at that time. Add timezone (IANA, default UTC) to give a local time.201, status scheduled
useQueue: trueTakes the next free slot in the posting queue of the member who owns the key.201
none of theseSaves a draft.201, status draft
Scheduled, local time
{
  "content": "Tonight at 7 — join us live.",
  "groupIds": ["GROUP_ID"],
  "scheduledFor": "2026-11-01T19:00:00",
  "timezone": "Asia/Kolkata"
}
Into a queue
{
  "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.

Only the failed targets
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.

Response
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.

Request and response
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": "…" }] }