Registering an endpoint
1
Give it a URL
HTTPS only, on port 443 or 8443, reachable from the public internet. A URL that resolves to
a private or loopback address is refused — see Why the URL is
checked.
2
Choose events
Subscribe to as many as you want. Each endpoint has its own subscription list.
3
Save the signing secret
It is shown once, at creation, and stored encrypted afterwards. There is no endpoint
that can hand it back — if you lose it, delete the webhook and register a new one.
Up to 5 endpoints per server.
Events
Events are deliberately coarse. Every payload carries the entity’s current state, so a consumer
that cares about the difference between “voided” and “removed” can read it rather than needing
one event per moderation verb.
The request
Report payloads deliberately omit the reporter’s free-text description and any moderator
notes. A webhook goes to a third-party server, and the person who filed the report did not
agree to have their words forwarded there.
Verifying a delivery
The signature is the only reason your endpoint can believe a request came from us rather than from anyone who learned the URL. Verify it on every request.- Sign the raw body bytes, not a re-serialised copy. Re-encoding the JSON changes the bytes and the signature will not match.
- Use a constant-time comparison.
==on a string leaks timing information. - Reject old timestamps. The timestamp is inside the signed material, so it cannot be swapped — but you still need to refuse anything more than a few minutes old, or a captured request can be replayed at you later.
Retries
Answer with any2xx and the delivery is done. Anything else is a failure, and we retry on a
fixed schedule: 30 seconds, 5 minutes, 30 minutes, 6 hours, then give up and mark the
delivery abandoned. Abandoned deliveries are kept, not deleted — “why didn’t my endpoint get
this?” deserves an answer.
Two exceptions:
- A
4xxother than408or429is not retried. Your endpoint understood the request and said no; sending identical bytes again cannot change that answer. - A redirect is a failure, not a hop to follow. Redirects are never followed.
Why the URL is checked
A webhook URL is chosen by you and then fetched by our servers, which is exactly the shape of an SSRF vulnerability. Without a check, an endpoint could be pointed at a cloud metadata service or at something else on our private network. So the destination is validated when you register it, and re-checked before every send — DNS can be re-pointed after registration — and redirects are never followed, since a302 is
the easy way to turn a validated public URL into an internal request.
This is why http://, non-standard ports, and hosts resolving to private ranges are all
refused at registration rather than failing quietly later.