Skip to content

Posts

A versioned API for posting content from an outside tool: n8n, Make, Zapier, or your own script. Create a post, optionally schedule it to connected channels, and check on it later.

All of it lives under /v1, for example https://api.postlyra.com/v1/posts.

This still lands in your review queue

Creating a post here never publishes it immediately. A scheduled publication only goes out through the normal queue, at its scheduled time, and only once the post is approved: by you in Studio, by the mobile app, or (only with approve: true below) by the call itself, recorded in your name. There is no way to publish the instant this call returns. See Approve and schedule.

GET /v1/posts

Keyset-listed (?before=&before_id=), newest first. Returns { "items": [...], "hasMore": false }.

POST /v1/posts

Creates a post, optionally scheduling it.

bash
curl -X POST https://api.postlyra.com/v1/posts \
  -H "Authorization: Bearer $POSTLYRA_TOKEN" \
  -H "X-Postlyra-Brand: $BRAND_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "text",
    "body": "The post text.",
    "submit": true,
    "publications": [
      { "account_id": 42, "scheduled_at": "2026-08-25T15:00:00Z" }
    ]
  }'
FieldMeaning
kindtext (default), reel, carousel or video. A format, not a network
titleOptional working title
bodyThe post text
threadOptional array of follow-up posts, for platforms that support threading
content_profile_idOptional identity for a carousel's slides. See Identities
pillarOptional content pillar key
submittrue lands the post awaiting_approval instead of draft
approvetrue creates the post already approved, recorded as your account in approved_via: "api". Implies submit. Refused with 403 tier_denied on a token minted below elevated_write access; see Authentication
publicationsOptional list of {account_id, scheduled_at, caption, first_comment, compose_options} targets

publications[].account_id comes from GET /v1/channels. Each target needs a different account and a future scheduled_at; send an explicit timezone offset, since a bare timestamp is read as UTC.

A connector preflight failure (a caption too long, a required image missing) refuses the whole call, nothing gets created:

json
{ "code": "preflight_failed", "issues": { "42": ["Caption exceeds 300 characters."] } }

On success: 201 with { "post": { ... } }.

GET /v1/posts/{post}

The post plus its publications.

DELETE /v1/posts/{post}/publications/{publication}

Cancels a pending publication (marks it skipped; nothing is ever hard-deleted, so the history stays intact). 422 on one that already fired.

GET /v1/channels

The brand's connected, active channels and what each one accepts, the same data the composer uses:

json
{
  "channels": [
    {
      "account_id": 42,
      "platform": "linkedin",
      "platform_label": "LinkedIn",
      "handle": "@yourbrand",
      "caption_limit": 3000,
      "max_media_items": 9,
      "supports_thread": false,
      "supports_first_comment": true
    }
  ],
  "account_groups": [{ "id": 1, "name": "Founder", "account_ids": [42] }]
}

GET /v1/slots/next?account_id=

The next free posting slot on that channel's own cadence, the same one Settings → Content configures. See Posting schedule.

Media

/v1/media/... is the same media library every other surface uses, mounted under /v1 too.

Postlyra, by MAVA Design