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
- Create an endpoint and store the returned
secret(shown once). - Subscribe to the event types you need.
- Respond with
2xxwithin 10 seconds. - Verify
Onepostly-Signatureon 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
| Method | Path | Notes |
|---|---|---|
GET | /v1/webhooks/events | Catalog of event types and groups |
GET | /v1/webhooks | List endpoints |
POST | /v1/webhooks | Create (201) |
GET | /v1/webhooks/:id | Get one |
PATCH | /v1/webhooks/:id | Update name, url, events, enabled |
DELETE | /v1/webhooks/:id | Remove (204) |
POST | /v1/webhooks/:id/test | Sends a webhook.test event |
GET | /v1/webhooks/:id/deliveries | Recent delivery attempts |
Event Catalog
Event names are platform-agnostic. Which platform fired an event lives in data.platform.
Post Webhooks
Publish lifecycle, per-platform results, external sync, and TikTok URL resolution
Comment Webhooks
Comment created and deleted events
Inbox Webhooks
Inbound and outbound direct messages on Instagram and Facebook
Engagement Webhooks
Likes, bookmarks, retweets, and quotes
Account Webhooks
Connected, disconnected, expired, and revoked account events
Posts
| Event | When |
|---|---|
post.scheduled | Post accepted with a future scheduledFor |
post.published | All destinations finished successfully |
post.failed | Post ended in a failed state |
post.partial | Some destinations published, others failed |
post.cancelled | Scheduled or queued post cancelled before publish |
post.platform.published | A destination went live on the platform |
post.platform.failed | A destination failed |
post.platform.deleted | Remote delete succeeded |
post.external.created | A native post appeared on a connected account |
post.external.updated | A tracked native post changed |
post.external.deleted | A tracked native post disappeared from the platform listing |
post.tiktok.url_resolved | A TikTok post's share URL became available |
Payloads and lifecycle details in Post Webhooks.
Comments
| Event | When |
|---|---|
comment.created | A comment was created via the API |
comment.deleted | A comment was deleted via the API |
List/read comment calls do not emit webhooks. Payloads in Comment Webhooks.
Messages
| Event | When |
|---|---|
message.received | New incoming DM on Instagram or Facebook |
message.sent | Outgoing DM is sent (including API sends) |
message.delivered | An outgoing DM is delivered |
message.read | The recipient reads an outgoing DM |
Payloads in Inbox Webhooks.
Engagement
| Event | When |
|---|---|
engagement.liked / engagement.unliked | Like added or removed |
engagement.bookmarked / engagement.unbookmarked | Bookmark added or removed |
engagement.retweeted / engagement.unretweeted | Retweet/repost added or removed |
engagement.quoted | Quote post created. Also emits post.platform.published for the new quote |
Payloads in Engagement Webhooks.
Accounts
| Event | When |
|---|---|
account.connected | Account connected or reconnected |
account.disconnected | Account removed |
account.expired | Access token expired and refresh failed |
account.revoked | Platform rejected the token (user revoked access) |
Payloads in Account Webhooks.
System
| Event | When |
|---|---|
webhook.test | Triggered 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": { }
}| Field | Meaning |
|---|---|
id | Event id. Duplicates share it, so use it as an idempotency key |
type | Event type string |
createdAt | ISO-8601 timestamp |
organizationId | Your workspace id |
data | Event-specific payload, shown on each subpage |
The payload examples on each subpage show only that event's data.
Request Headers
| Header | Value |
|---|---|
Content-Type | application/json |
User-Agent | Onepostly-Webhooks/1.0 |
Onepostly-Signature | t=<unix>,v1=<hex> |
Onepostly-Event | Event type, e.g. post.platform.published |
Onepostly-Delivery | Delivery attempt id |
Onepostly-Event-Id | Envelope 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
| Behavior | Detail |
|---|---|
| Method | POST |
| Timeout | 10 seconds |
| Success | HTTP 2xx |
| Max attempts | 5 |
| Retried on | 408, 429, 5xx, network errors |
| Not retried | Most 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
idas an idempotency key - Keep handlers fast. Queue work and return
200quickly - Subscribe to
account.expiredandaccount.revokedso you can prompt users to reconnect
See also Quickstart, Post Webhooks, Comment Webhooks, Inbox Webhooks, Engagement Webhooks, and Account Webhooks.