Webhooks

Subscribe to publish, platform, comment, message, engagement, and account lifecycle events.

Webhooks push events to your HTTPS endpoint so you do not need to poll GET /v1/posts/:id. Every delivery is signed; verify the signature before trusting the body.

Quick Start

  1. Create an endpoint and store the returned secret (shown once).
  2. Subscribe to the event types you need.
  3. Respond with 2xx within 10 seconds.
  4. Verify Onepostly-Signature on every request.
import { Configuration, WebhooksApi } from "@onepostly/sdk";

const webhooksApi = new WebhooksApi(
  new Configuration({ apiKey: process.env.ONEPOSTLY_API_KEY })
);

const response = await webhooksApi.createWebhook({
  createWebhookBody: {
    "name": "Post events",
    "url": "https://example.com/hooks/onepostly",
    "secret": "s7a9Kx2mQ8vB4nR6tY3wZ1pL5oN0uMf",
    "events": [
      "post.platform.published"
    ],
    "enabled": true
  },
});
console.log(response);
{
  "webhook": {
    "id": "dlv_1",
    "name": "Post events",
    "url": "https://example.com/hooks/onepostly",
    "enabled": true,
    "events": [
      "post.platform.published"
    ],
    "secret": "s7a9Kx2mQ8vB4nR6tY3wZ1pL5oN0uMf"
  }
}

secret is returned only on create. List and get responses never include it.

Manage Endpoints

MethodPathNotes
GET/v1/webhooks/eventsCatalog of event types and groups
GET/v1/webhooksList endpoints
POST/v1/webhooksCreate (201)
GET/v1/webhooks/:idGet one
PATCH/v1/webhooks/:idUpdate name, url, events, enabled
DELETE/v1/webhooks/:idRemove (204)
POST/v1/webhooks/:id/testSends a webhook.test event
GET/v1/webhooks/:id/deliveriesRecent delivery attempts

Event Catalog

Event names are platform-agnostic. Which platform fired an event lives in data.platform.

Posts

EventWhen
post.scheduledPost accepted with a future scheduledFor
post.publishedAll destinations finished successfully
post.failedPost ended in a failed state
post.partialSome destinations published, others failed
post.cancelledScheduled or queued post cancelled before publish
post.platform.publishedA destination went live on the platform
post.platform.failedA destination failed
post.platform.deletedRemote delete succeeded
post.external.createdA native post appeared on a connected account
post.external.updatedA tracked native post changed
post.external.deletedA tracked native post disappeared from the platform listing
post.tiktok.url_resolvedA TikTok post's share URL became available

Payloads and lifecycle details in Post Webhooks.

Comments

EventWhen
comment.createdA comment was created via the API
comment.deletedA comment was deleted via the API

List/read comment calls do not emit webhooks. Payloads in Comment Webhooks.

Messages

EventWhen
message.receivedNew incoming DM on Instagram or Facebook
message.sentOutgoing DM is sent (including API sends)
message.deliveredAn outgoing DM is delivered
message.readThe recipient reads an outgoing DM

Payloads in Inbox Webhooks.

Engagement

EventWhen
engagement.liked / engagement.unlikedLike added or removed
engagement.bookmarked / engagement.unbookmarkedBookmark added or removed
engagement.retweeted / engagement.unretweetedRetweet/repost added or removed
engagement.quotedQuote post created. Also emits post.platform.published for the new quote

Payloads in Engagement Webhooks.

Accounts

EventWhen
account.connectedAccount connected or reconnected
account.disconnectedAccount removed
account.expiredAccess token expired and refresh failed
account.revokedPlatform rejected the token (user revoked access)

Payloads in Account Webhooks.

System

EventWhen
webhook.testTriggered by the test endpoint

Envelope

Every delivery body is this envelope:

{
  "id": "event_post_platform_published_DEST_ID",
  "type": "post.platform.published",
  "createdAt": "2026-07-15T12:00:01.000Z",
  "organizationId": "WORKSPACE_ID",
  "data": { }
}
FieldMeaning
idEvent id. Duplicates share it, so use it as an idempotency key
typeEvent type string
createdAtISO-8601 timestamp
organizationIdYour workspace id
dataEvent-specific payload, shown on each subpage

The payload examples on each subpage show only that event's data.

Request Headers

HeaderValue
Content-Typeapplication/json
User-AgentOnepostly-Webhooks/1.0
Onepostly-Signaturet=<unix>,v1=<hex>
Onepostly-EventEvent type, e.g. post.platform.published
Onepostly-DeliveryDelivery attempt id
Onepostly-Event-IdEnvelope id

Verify Signatures

The signature is HMAC-SHA256 over timestamp + "." + rawBody using your endpoint secret.

import { createHmac, timingSafeEqual } from "node:crypto";

function verifyOnepostlySignature({ secret, rawBody, header, toleranceSec = 300 }) {
  const parts = Object.fromEntries(
    header.split(",").map((p) => p.split("=").map((s) => s.trim())),
  );
  const timestamp = Number(parts.t);
  const v1 = parts.v1;
  if (!timestamp || !v1) return false;
  if (Math.abs(Math.floor(Date.now() / 1000) - timestamp) > toleranceSec) {
    return false;
  }

  const signedPayload = timestamp + "." + rawBody;
  const expected = createHmac("sha256", secret)
    .update(signedPayload, "utf8")
    .digest("hex");

  const a = Buffer.from(v1, "hex");
  const b = Buffer.from(expected, "hex");
  return a.length === b.length && timingSafeEqual(a, b);
}

Verify against the raw request body. Do not re-serialize JSON first.

Delivery And Retries

BehaviorDetail
MethodPOST
Timeout10 seconds
SuccessHTTP 2xx
Max attempts5
Retried on408, 429, 5xx, network errors
Not retriedMost other 4xx

Publishing never fails because a webhook endpoint is down. Deliveries are best-effort with retries.

Inspect recent attempts:

curl "https://api.onepostly.com/v1/webhooks/WEBHOOK_ID/deliveries?limit=20" \
  -H "x-api-key: op_YOUR_KEY"
{
  "deliveries": [
    {
      "id": "DLV_ID",
      "endpointId": "WH_ID",
      "eventId": "evt_123",
      "eventType": "post.published",
      "status": "success",
      "attemptCount": 1,
      "responseStatus": 200,
      "lastError": null,
      "createdAt": "2026-08-25T10:00:00.000Z",
      "deliveredAt": "2026-08-25T10:00:00.001Z"
    }
  ]
}

Each row includes status (pending, success, failed), attemptCount, responseStatus, and lastError.

Test Endpoint

Send a webhook.test event to one endpoint (regardless of its subscription):

curl -X POST https://api.onepostly.com/v1/webhooks/WEBHOOK_ID/test \
  -H "x-api-key: op_YOUR_KEY"
{
  "ok": true
}

Best Practices

  • Prefer webhooks over polling for publish status
  • Always verify signatures; reject unsigned or stale requests
  • Treat envelope id as an idempotency key
  • Keep handlers fast. Queue work and return 200 quickly
  • Subscribe to account.expired and account.revoked so you can prompt users to reconnect

See also Quickstart, Post Webhooks, Comment Webhooks, Inbox Webhooks, Engagement Webhooks, and Account Webhooks.