Posting
Webhooks
Register an https endpoint and yeetpost posts post.sent and post.failed events to it as posts go out, scheduled ones included. That is the whole point: by then nobody is waiting on an HTTP response.
#Register an endpoint
curl -X POST "https://api.yeetpost.com/api/v2/webhooks" \
-H "x-api-key: $YEETPOST_API_KEY" \
-H "content-type: application/json" \
-d '{
"url": "https://example.com/hooks/yeetpost",
"events": [
"post.sent",
"post.failed"
]
}'{
"webhook": {
"id": "2c9a4f3b-77d1-4b0e-9f2a-8c3e1a5b7d90",
"url": "https://example.com/hooks/yeetpost",
"events": [
"post.sent",
"post.failed"
],
"secret": "yp_whsec_3f1a...",
"createdAt": "2026-08-26T09:24:11.412Z"
}
}events if you only want one of the two.The URL has to be https and has to point at a public host: private, loopback, link-local and metadata addresses are rejected with 400 invalid_request. Deliveries do not follow redirects, so register the final URL.
#What a delivery looks like
delivery headers
The event name, the same value as event in the body.
Id of this delivery attempt's delivery row. Stable across retries of the same event, so it works as a deduplication key.
t=<unix seconds>,v1=<hex hmac-sha256 of "<t>.<raw body>">, keyed by your webhook secret.
{
"event": "post.sent",
"timestamp": "2026-08-26T09:24:12.004Z",
"post": {
"id": "6f1b0c8a-1f3d-4d3e-9a2b-2f5a1c9e7d10",
"connectionSlug": "linkedin",
"text": "shipped the new api docs today",
"status": "sent",
"url": "https://www.linkedin.com/feed/update/urn:li:share:1234567890",
"scheduledFor": null,
"createdAt": "2026-08-26T09:24:11.412Z",
"error": null
}
}Answer with any 2xx within 5 seconds. Anything else, or a timeout, is retried up to 5 attempts with exponential backoff starting at 10 seconds, after which the delivery is marked failed.
x-yeetpost-delivery is stable across retries of the same event, so it works as a deduplication key.
#Verifying the signature
Each delivery carries x-yeetpost-signature: t=<unix seconds>,v1=<hex hmac>. v1 is HMAC-SHA256("<t>.<raw request body>") keyed by your webhook secret.
- Sign the raw body bytes, before any JSON parsing. Re-serialising changes them.
- Compare with a constant-time comparison, not
===. - Reject anything whose
tis more than about 5 minutes from your clock, so a captured delivery cannot be replayed at you later.
import crypto from "crypto";
import express from "express";
const app = express();
app.post(
"/hooks/yeetpost",
express.raw({ type: "application/json" }),
(req, res) => {
const header = String(req.headers["x-yeetpost-signature"]);
const timestamp = header.match(/t=(\d+)/)?.[1] ?? "";
const signature = header.match(/v1=([0-9a-f]{64})/)?.[1] ?? "";
const rawBody = req.body.toString("utf8");
const expected = crypto
.createHmac("sha256", process.env.YEETPOST_WEBHOOK_SECRET)
.update(`${timestamp}.${rawBody}`)
.digest("hex");
const isSignatureValid =
signature.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
const isFresh = Math.abs(Date.now() / 1000 - Number(timestamp)) < 300;
if (!isSignatureValid || !isFresh) {
res.status(400).send("bad signature");
return;
}
const event = JSON.parse(rawBody);
console.log(event.event, event.post.id);
res.sendStatus(200);
},
);
app.listen(3000);#The delivery log
When a receiver is not getting events, read the log instead of guessing: what was sent, how it went, and the last error when it did not land.
curl "https://api.yeetpost.com/api/v2/webhooks/2c9a4f3b-77d1-4b0e-9f2a-8c3e1a5b7d90/deliveries" \
-H "x-api-key: $YEETPOST_API_KEY"{
"deliveries": [
{
"id": "8d3f2b7c-4e1a-4c6d-9b0f-1a2b3c4d5e6f",
"event": "post.sent",
"status": "delivered",
"attempts": 1,
"createdAt": "2026-08-26T09:24:11.412Z",
"deliveredAt": "2026-08-26T09:24:12.004Z",
"lastError": null
},
{
"id": "1c4e5f6a-7b8c-4d9e-8f0a-1b2c3d4e5f60",
"event": "post.failed",
"status": "failed",
"attempts": 5,
"createdAt": "2026-08-25T18:02:44.118Z",
"deliveredAt": null,
"lastError": "response status 500"
}
],
"limit": 25
}pending is waiting for its next attempt, processing is being sent right now, failed ran out of attempts.
Deliveries are kept for 30 days, and limit is 1 to 100, default 25.
#Listing and deleting
Your webhooks, newest first. Secrets are never included.
curl "https://api.yeetpost.com/api/v2/webhooks" \
-H "x-api-key: $YEETPOST_API_KEY"Deleting stops delivery. Deliveries still queued for that webhook are marked failed.
curl -X DELETE "https://api.yeetpost.com/api/v2/webhooks/2c9a4f3b-77d1-4b0e-9f2a-8c3e1a5b7d90" \
-H "x-api-key: $YEETPOST_API_KEY"#Full schemas
Both event bodies and every webhook endpoint are on the API reference.