Webhooks
Postline sends signed webhook requests when posts are scheduled, published or fail. Learn the events, payload, signature check and retries.
Add a webhook
Only owners and admins can manage webhooks.
- Go to Settings → API → Webhooks.
- Enter a name and your URL, for example
https://hooks.example.com/postline. - Pick the events you want.
- Choose Add webhook.
- Copy the signing secret (it starts with
whsec_). You won't see it again.
Choose Send test to send a test delivery. The list shows the result of the last delivery for each webhook. A workspace can have up to 20 webhooks. The URL must be a public address.
Events
| Event | Sent when |
|---|---|
post.scheduled | A post is scheduled |
post.approval_requested | An editor submits a post for approval |
post.published | A scheduled post has gone out. Also sent when it went out on only some channels |
post.failed | A post failed on every channel |
channel.reconnect_needed | A channel lost its connection and must be reconnected |
New webhooks start with post.published and post.failed selected. A test delivery uses the event name test.
The request
Postline sends a POST request with a JSON body:
{
"event": "post.published",
"createdAt": "2026-11-03T09:30:05.000Z",
"data": { ... }
}
For post events, data is the post: its id, content, status, date and a targets list with each channel's status, url and error. For channel.reconnect_needed, data has channelId, name, network and error.
Headers:
| Header | Value |
|---|---|
Content-Type | application/json |
X-Postline-Event | The event name |
X-Postline-Timestamp | Unix time in seconds when the request was signed |
X-Postline-Signature | sha256= followed by the hex HMAC-SHA256 signature |
Verify the signature
The signature is HMAC-SHA256 of timestamp.body, using your webhook secret as the key. timestamp is the X-Postline-Timestamp header and body is the raw request body.
- Read the raw body before parsing it as JSON.
- Compute
sha256=+ hex(HMAC-SHA256(secret, timestamp + "." + body)). - Compare it with
X-Postline-Signatureusing a constant-time comparison. - Optionally reject requests with an old timestamp.
Node.js example:
import crypto from 'node:crypto';
function isValid(secret, timestamp, rawBody, signature) {
const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest('hex');
const a = Buffer.from(expected);
const b = Buffer.from(signature ?? '');
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
Responses and retries
Answer with any 2xx status to confirm delivery. Postline waits up to 10 seconds for a response and doesn't follow redirects.
If a delivery fails, Postline retries it up to 3 more times, waiting about 10, 20 and then 40 seconds. It doesn't retry when your server answers with a 4xx status, except 429.