Skip to content

Webhooks ​

Outgoing webhooks exist: subscribe an endpoint to a handful of post events and Postlyra sends it a signed HTTP request when one happens. Set them up in Studio, under Settings, then the API tab. Only the brand owner can create or edit one.

Events ​

EventFires when
post.publishedA scheduled publication goes live
post.failedA scheduled publication fails, after retries are exhausted
post.expiredA scheduled publication's time passed without approval
post.approvedA post is approved, from Studio, MCP, the mobile app, or a public share link
post.commentedSomeone comments on a post's review thread

An endpoint picks which of these it wants; it never receives one it did not subscribe to.

Payload ​

Every event ships the same envelope:

json
{
  "post_id": 123,
  "status": "published",
  "publication": { "id": 456, "platform": "linkedin", "external_id": "urn:li:...", "permalink": "https://..." },
  "brand_id": 7,
  "occurred_at": "2026-08-25T09:00:03+00:00"
}

publication appears only on post.published, post.failed and post.expired. post.approved carries approved_via instead of publication. post.commented carries comment_id, kind (comment, approval or change_request) and body instead.

Verifying a delivery ​

Every request carries:

X-Postlyra-Signature: t=1756112345,v1=8f3c2a...
X-Postlyra-Event: post.published

v1 is HMAC-SHA256("{t}.{raw request body}", <your endpoint's secret>). The secret is shown once, when you create the endpoint.

js
const crypto = require("crypto")

function verify(rawBody, signatureHeader, secret) {
  const [tPart, v1Part] = signatureHeader.split(",")
  const timestamp = tPart.split("=")[1]
  const signature = v1Part.split("=")[1]
  const expected = crypto.createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex")
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))
}

Use the exact raw bytes received, before any JSON parsing, or the signature will not match.

Delivery, retries, and auto-disable ​

  • Three attempts, backed off 10 seconds, then 60, then 5 minutes.
  • 10 consecutive deliveries that exhaust all their retries disable the endpoint automatically. You get one notification, not one per attempt. Fix it and re-enable it in Studio, which resets the streak.
  • The delivery log, on the same Settings page, keyset-lists every attempt with its status, response code and error.
  • Send test fires a real signed delivery (event: "test") through the same code path a live event uses, so a passing test proves your signature verification actually works.

Inbound callbacks are a separate thing ​

Postlyra also receives callbacks from platforms it integrates with: Meta deauthorization and data-deletion requests, Instagram events, each platform's OAuth callback, and Stripe billing events. These are not something you subscribe to or call. They live on a separate host and verify signatures from the calling platform, documented here only so nobody wires an integration against them by mistake.

EndpointWho calls it
POST /webhooks/meta/deauthorizeMeta, when a user removes the app
POST /webhooks/meta/data-deletionMeta, on a data deletion request
GET and POST /webhooks/meta/instagramMeta, delivering Instagram events such as DMs and comments
GET /oauth/{platform}/callbackThe platform, finishing an OAuth connect
Stripe's webhookStripe, on billing events

Polling for anything else ​

The five post events above are the whole outgoing list. For anything not on it, poll instead.

What you wantEndpointSensible interval
New ideas in the queuelist_ideas on MCPMinutes
Whether a plan changedGET /subscription/statusHourly
Remaining allowancesGET /me/entitlementsHourly

Metrics sync once a night, so polling them more often than daily reads the same numbers repeatedly. See Rate limits.

Postlyra, by MAVA Design