Skip to content

Retries and polling

A delivery counts as successful when your endpoint answers with any 2xx status. Anything else, including a timeout or a connection error, schedules a retry.

Up to 6 attempts over about 15 hours:

Attempt Delay after the previous one
1 immediately
2 1 minute
3 5 minutes
4 30 minutes
5 2 hours
6 12 hours

After the sixth failure the event is marked undelivered and not sent again. It stays readable through GET /v1/events.

Respond fast. Verify the signature, write the event somewhere durable, return 200, and process afterwards. A handler that calls Meta, sends email and renders a PDF before answering will time out under load and be retried, which brings the next point.

Every retry carries the same event id in the body and in X-MissLess-Delivery. Your handler will see the same event more than once, both from retries and from the occasional duplicate. Treat the id as the unit of work:

async function handle(event) {
const fresh = await db.insertIgnore('missless_events', { id: event.id, type: event.type, received_at: new Date() });
if (!fresh) return; // already processed
switch (event.type) {
case 'post.published':
case 'post.partial':
case 'post.failed':
await updatePost(event.data.post);
break;
// ...
}
}

Keep processed ids for at least a day; 15 hours is the retry horizon.

Deliveries are not ordered. Two events for the same post can arrive out of sequence when one is retried. Use the timestamps inside data (updated on posts, last_message_at on conversations) to decide which state is newer, not the arrival order.

GET /v1/events lists the same events the webhook delivers, whether or not you have a webhook configured. Use it as your only integration if you prefer polling, or as a catch-up after an outage.

Query Notes
workspace One workspace
type One event type
since ISO-8601. Events with created_at after this instant
page, limit Pagination, default 50, max 200. Oldest first
Terminal window
curl "https://social.missless.tel/v1/events?workspace=ws7k2m9p4q1r8t3&type=post.published&since=2026-08-25T00:00:00Z" \
-H "Authorization: Bearer $MISSLESS_API_KEY"
{
"data": [
{
"id": "evt1a3c5e7g9i2k",
"type": "post.published",
"created_at": "2026-08-25T09:20:11Z",
"partner": "acme",
"workspace": "ws7k2m9p4q1r8t3",
"external_id": "user_123",
"data": { "id": "pst5g7j9l2n4q6s", "external_ref": "acme:post:1234", "status": "published" }
}
],
"page": 1,
"limit": 50,
"total": 1
}

Each item is exactly the body a webhook delivery would carry. Run the same handler over it.

A polling loop that behaves:

  1. Store the created_at of the newest event you processed as a cursor.
  2. Every minute, GET /v1/events?since=<cursor> and page through until data is empty.
  3. Process each event through the same idempotent handler, so overlap with webhooks is harmless.
  4. Move the cursor forward only after the page is processed.

Polling costs one request per minute per poller against the 300 per minute per key limit. Poll per partner, not per workspace, unless you have a reason.

If your endpoint was down for longer than the retry horizon, some events were never delivered. Fetch GET /v1/events?since=<last good timestamp> once and run them through your handler. Because it de-duplicates on id, the events that did arrive are skipped and the missing ones are filled in.