Webhooks
The other direction: instead of telling tinyposter to post, let tinyposter tell YOU when something happens. Point a webhook at Make, n8n, Zapier, or your own server and get a signed call the moment a post publishes, fails, is canceled, or an account changes.
What you get
Add an endpoint at /dashboard/webhooks (or via the API). When a subscribed event fires, we POST a small JSON body to your URL, signed so you can prove it came from us. Use it to log every published post to a sheet, ping Slack when something fails, or kick off any downstream flow.
Events
post.created: a post was created (scheduled or sending now).post.published: a post went live on its platforms.post.failed: a post failed to publish (the quota is refunded automatically).post.canceled: a scheduled post was canceled.account.connected: a social account was connected to a brand.account.disconnected: a social account was removed or expired.ping: sent only by the “Send test” button, to confirm setup.
Subscribe to specific events, or to * (all events).
Payload
Every delivery is a JSON body with this envelope:
{
"id": "f1e2d3c4-...", // event id, dedupe on this
"type": "post.published",
"created": "2026-06-24T14:00:00.000Z",
"data": {
"post": {
"id": "9a8b7c6d-...",
"status": "published",
"text": "Friday recap ✨",
"platforms": ["TWITTER", "LINKEDIN"],
"title": null,
"scheduled_at": "2026-06-24T14:00:00.000Z",
"published_at": "2026-06-24T14:00:05.000Z",
"error": null,
"brand_id": "..."
}
}
}Account events carry a data.account object instead (platform, username, status, brand_id).
Headers
Content-Type: application/json
User-Agent: tinyposter-webhooks/1
X-Tinyposter-Event: post.published
X-Tinyposter-Delivery: <unique delivery id>
X-Tinyposter-Signature: t=1750772400,v1=9f86d08...Verify the signature
The signature is HMAC-SHA256 over {timestamp}.{raw request body} using your endpoint secret (whsec_…, shown once when you create the endpoint). Recompute it and compare to the v1 value. Reject anything that doesn't match.
import crypto from "node:crypto";
function verify(rawBody, header, secret) {
// header looks like: "t=1750772400,v1=abc123..."
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const expected = crypto
.createHmac("sha256", secret)
.update(parts.t + "." + rawBody)
.digest("hex");
// constant-time compare
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}
app.post("/tp-webhook", express.raw({ type: "application/json" }), (req, res) => {
const ok = verify(req.body.toString(), req.get("X-Tinyposter-Signature"), process.env.TP_WEBHOOK_SECRET);
if (!ok) return res.sendStatus(400);
const event = JSON.parse(req.body.toString());
// ... handle event.type ...
res.sendStatus(200);
});2xx as success. Do slow work after responding. Anything else (or a timeout after 10s) is retried with backoff: ~1m, 5m, 30m, 2h, 6h, 24h, then we give up and (after repeated failures) auto-disable the endpoint.Make.com
- 01
Add a 'Custom webhook' trigger
In Make, create a scenario starting with Webhooks → Custom webhook. Copy the URL it gives you.
- 02
Paste it into tinyposter
Go to /dashboard/webhooks, paste the URL, pick your events, and save.
- 03
Send a test
Hit Send test next to the endpoint. Make will capture the structure so you can map fields downstream.
n8n
- 01
Add a 'Webhook' node
Method
POST. Copy the production URL. - 02
Register it in tinyposter
Paste the URL at /dashboard/webhooks and choose events.
- 03
(Optional) verify the signature
Add a Function node using the HMAC snippet above, or skip it if the URL is secret enough for you.
Manage via the API
Everything in the dashboard is also in the REST API (same tp_… token):
# Create an endpoint (secret is returned ONCE)
curl https://tinyposter.app/api/v1/webhooks \
-H "Authorization: Bearer tp_..." \
-H "Content-Type: application/json" \
-d '{"url":"https://hook.eu1.make.com/abc","events":["post.published","post.failed"]}'
# List, update, delete, test, and inspect deliveries
curl https://tinyposter.app/api/v1/webhooks -H "Authorization: Bearer tp_..."
curl -X POST https://tinyposter.app/api/v1/webhooks/<id>/test -H "Authorization: Bearer tp_..."
curl https://tinyposter.app/api/v1/webhooks/<id>/deliveries -H "Authorization: Bearer tp_..."Full schemas are in the OpenAPI spec.
Troubleshooting
▸I'm not getting any calls
Check the endpoint is enabled and subscribed to the right event. Open the row on the webhooks page to see the delivery log with HTTP codes and errors.
▸Signature check fails
Sign the raw body bytes, not a re-serialized JSON object. The signed string is {t}.{rawBody} where t is the value from the signature header.
▸My endpoint got auto-disabled
After 10 deliveries give up in a row we turn it off. Fix the receiver, then toggle it back on (that resets the counter).
▸Do I need https?
Yes. Webhook URLs must be https:// and publicly reachable. Internal/localhost addresses are rejected.
▸Will I get duplicates?
Possibly. Delivery is at-least-once, so dedupe on the event id (or the X-Tinyposter-Delivery header).