Once you've added an endpoint under Account → API & Webhooks,
Treads.nz will POST a signed JSON batch to your URL every time one of your
subscribed events happens. Deliveries are batched - if several events fire close
together they may arrive in one request rather than several.
| Event | Fires when… |
|---|---|
photo.uploaded |
A member uploads a new photo |
status.updated |
A status post is created or edited |
status.deleted |
A status post is deleted |
comment.created |
A new comment is posted |
trader.listed |
A new Trader listing is published |
Every delivery is a single JSON object containing one or more events:
{
"batch_id": "9f1c2e7a4b3d6081a2c5e7f9b1d3a5c7",
"sent_at": "2026-09-16T04:12:03+00:00",
"event_count": 2,
"events": [
{
"id": "3fa81c0b9d2e7a45",
"event": "photo.uploaded",
"data": {
"photo_id": 48213,
"album_id": 902,
"uploaded_at": "2026-09-16T04:11:58+00:00"
}
},
{
"id": "b71e02c4f9a3d856",
"event": "status.updated",
"data": {
"status_id": 771234,
"updated_at": "2026-09-16T04:12:01+00:00"
}
}
]
}
Each event's id stays the same if a delivery is retried, so you can safely ignore an
event you've already handled.
The exact fields inside each event's data object depend on the event type and are
still being finalised alongside the REST API - treat the shapes above as illustrative for now.
Every request includes an X-Webhook-Signature header - the hex-encoded
HMAC-SHA256 digest of the raw request body, signed with your webhook's own
signing secret (shown on the API & Webhooks page),
prefixed sha256=. There's also an X-Webhook-Timestamp header (unix time
the request was sent) if you want to reject stale/replayed deliveries.
Always verify the signature before trusting or processing the body:
<?php
// Your endpoint - receives the raw POST body from Treads.nz.
$secret = 'your_webhook_signing_secret'; // from /account/api
$raw_body = file_get_contents('php://input');
$received_signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$expected_signature = 'sha256=' . hash_hmac('sha256', $raw_body, $secret);
if (!hash_equals($expected_signature, $received_signature)) {
http_response_code(401);
exit('Invalid signature');
}
$batch = json_decode($raw_body, true);
foreach ($batch['events'] as $event) {
switch ($event['event']) {
case 'photo.uploaded':
// handle it - e.g. queue a re-sync job
break;
case 'status.updated':
// ...
break;
}
}
http_response_code(200); // acknowledge receipt
If your endpoint doesn't respond with a 2xx status (or times out), the delivery is
retried with exponential backoff - roughly 1, 2, 4, 8… minutes apart, capped at an hour
between attempts, up to 8 attempts total before we give up on that event. Respond quickly and
with a 2xx status as soon as you've accepted the batch; do any slow processing
afterwards on your end rather than inside the request.
Not sure how to get a signing secret in the first place? See Getting started.