Webhooks

Post Webhooks

Events for scheduled posts, publish results, edits, cancels and deletes, and posts made outside Onepostly.

Follow each post from schedule to result, and keep outside posts in sync. Each event is about one account's post, with the whole post in data.object.

Events

EventFires when
post.scheduledA post is created with a future scheduledFor
post.publishedA post is live
post.failedA post failed to publish
post.cancelledYou deleted a post before it published
post.deletedYou deleted a live post, it was deleted on Facebook, or an outside post is gone
post.updatedYou edited a scheduled post, a platform id arrived, or an outside post changed
post.createdA new outside post was found

Event Order

A scheduled post sends post.scheduled on create, then post.published or post.failed. A post that publishes now skips post.scheduled.

Each post of a request finishes on its own, so one can fail while another publishes. Match them up by their shared groupId.

Deliveries can arrive out of order, so compare updatedAt, or call GET /v1/posts/{id} for the current state. See Post Status.

Payloads

Each example is the data of the webhook payload.

post.published

{
  "object": {
    "id": "pst_3c8a2d6e91b44f7a8e250d6b4c1f9a73",
    "groupId": "grp_7d2e5b1a9c4f4a68b3e07f1c2d9a6e45",
    "accountId": "acc_YOUR_X_ACCOUNT_ID",
    "platform": "x",
    "source": "onepostly",
    "status": "published",
    "text": "New release notes are up.",
    "mediaKind": "text",
    "media": [],
    "thread": [],
    "scheduledFor": null,
    "timezone": null,
    "options": {},
    "platformPostId": "1900876543210987654",
    "url": "https://x.com/i/web/status/1900876543210987654",
    "error": null,
    "publishedAt": "2027-03-15T09:00:04.000Z",
    "metrics": null,
    "createdAt": "2027-03-15T09:00:00.000Z",
    "updatedAt": "2027-03-15T09:00:04.000Z"
  }
}

platformPostId and url identify the post on the platform. publishedAt is when it went live. On TikTok and Facebook, the id and URL can change.

On TikTok, the event can come before the post is public. Then platformPostId is a publish id starting with v_pub_ or p_pub_. url is the profile URL for videos, or null for photos. A later post.updated brings the public id and URL, but only if the post goes public.

On Facebook, a video or Reel first has its video id as platformPostId. A post.updated soon brings the Page post id and url.

Update the values you stored when these arrive.

post.failed

The post has status: "failed" and the reason in error.

{
  "object": {
    "id": "pst_3c8a2d6e91b44f7a8e250d6b4c1f9a02",
    "groupId": "grp_7d2e5b1a9c4f4a68b3e07f1c2d9a6e45",
    "accountId": "acc_YOUR_INSTAGRAM_ACCOUNT_ID",
    "platform": "instagram",
    "source": "onepostly",
    "status": "failed",
    "text": "New release notes are up.",
    "mediaKind": "image",
    "media": [
      {
        "url": "https://cdn.onepostly.com/media/spring.jpg"
      }
    ],
    "thread": [],
    "scheduledFor": null,
    "timezone": null,
    "options": {},
    "platformPostId": null,
    "url": null,
    "error": {
      "code": "TOKEN_INVALID",
      "message": "Connection token was revoked. Reconnect the account to continue publishing."
    },
    "publishedAt": null,
    "metrics": null,
    "createdAt": "2027-03-15T09:00:00.000Z",
    "updatedAt": "2027-03-15T09:00:02.000Z"
  }
}

Branch on error.code, listed in Error Codes. error.message is for logs and your UI, and its wording can change, so never match on it.

TOKEN_INVALID means the account needs a reconnect. account.expired and account.revoked tell you once per account, not once per post.

If a thread failed part way, platformPostId points to the part that went live. To remove that part, delete the post.

post.scheduled, post.cancelled, post.deleted

These carry the post after the change, with status scheduled, cancelled or deleted. scheduledFor is local time in timezone. post.deleted fires once the platform confirms it, for a thread once every part is gone. It also fires when a post is deleted on its Facebook Page.

post.updated

This carries the post after the change. An edit keeps it scheduled with the new content. If it shared a groupId, it gets its own.

Posts Made Outside Onepostly

post.created, post.updated and post.deleted also report posts made on the platform itself, with source: "external". On Facebook, they fire on their own, usually within a minute. Elsewhere they fire when POST /v1/accounts/{id}/sync finds a change. See Posts Made Outside Onepostly.

EventFires when
post.createdA new post is found. A first sync reports recent posts, so upsert by id
post.updatedurl, text, mediaKind or publishedAt changed, or a deleted post is back
post.deletedThe post is gone, or you deleted it through the API, with status deleted
{
  "object": {
    "id": "pst_3c8a2d6e91b44f7a8e250d6b4c1f9a03",
    "groupId": null,
    "accountId": "acc_YOUR_X_ACCOUNT_ID",
    "platform": "x",
    "source": "external",
    "status": "published",
    "text": "Fresh from the studio.",
    "mediaKind": "image",
    "media": [],
    "thread": [],
    "scheduledFor": null,
    "timezone": null,
    "options": {},
    "platformPostId": "1901234567890123456",
    "url": "https://x.com/yourbrand/status/1901234567890123456",
    "error": null,
    "publishedAt": "2027-03-16T14:22:05.000Z",
    "metrics": null,
    "createdAt": "2027-03-16T15:03:47.120Z",
    "updatedAt": "2027-03-16T15:03:47.120Z"
  }
}

Outside posts have no groupId, but their id works with analytics, comments, engagement and deleting like any other post.