DocsReference
Webhooks
Subscribe a URL to receive real-time events when posts are published, partially published or fail. We sign every delivery with HMAC-SHA256 so you can verify the request came from us.
Event types
| Event | Fires when |
|---|---|
post.queued | Post has been accepted and tasks enqueued. |
post.published | All platforms succeeded (also emitted per platform). |
post.partial | At least one platform succeeded and at least one failed. |
post.failed | Every targeted platform failed, or a single platform terminally failed. |
account.connected | User connected a platform account. |
account.disconnected | User disconnected a platform account. |
Register a webhook
POST /v1/webhooks
curl https://api.letspost.app/v1/webhooks \
-H "Authorization: Bearer $SOCIALHUB_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your.app/letspost/webhook",
"events": ["post.published","post.failed","post.partial"]
}'The response includes a secret — save it. Use it to verify every future delivery.
Delivery format
POST https://your.app/letspost/webhook
Content-Type: application/json
X-LetsPost-Event: post.published
X-LetsPost-Signature: sha256=<hex-hmac>
{
"id": "evt_1abcd23_4ef5",
"type": "post.published",
"createdAt": "2026-04-16T20:30:01.000Z",
"data": { "postId": "pst_1", "published": 3, "failed": 0, "total": 3 }
}Verify the signature
import express from 'express';
import crypto from 'node:crypto';
const app = express();
// Use the raw body — json parsing changes whitespace and breaks the HMAC.
app.post('/letspost/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const header = req.header('X-LetsPost-Signature') || '';
const presented = header.replace(/^sha256=/, '');
const expected = crypto
.createHmac('sha256', process.env.SOCIALHUB_WEBHOOK_SECRET)
.update(req.body)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(presented), Buffer.from(expected))) {
return res.status(401).end();
}
const event = JSON.parse(req.body.toString());
console.log(event.type, event.data);
res.status(200).end();
});Retries & idempotency
- Return a
2xxwithin 5s. Non-2xx or timeout is treated as failure. - We do not retry failed deliveries today — add your own queue if you need strict delivery guarantees (see roadmap).
event.id - Deduplicate on
event.id— it is unique per delivery.
Listing & revoking
GET /v1/webhooks lists your webhooks. DELETE /v1/webhooks/{id} removes one.
Was this page helpful?
Something unclear? Email us — we read every message.