GET /projects/{id}/files keeps working, and stays the way to catch
up after your server was down for longer than deliveries are retried.
Create a webhook
https:// and reach a public address: private networks, loopback and link-local
addresses are refused, when you create the webhook and again at every delivery, and so are host
names without a dot and names under .local or .internal. An account has at
most 10 webhooks, one per URL. A webhook receives the events that happen after it was created,
for every project of the account.
Events
Every delivery is a JSON body of the same shape:id is the event’s, the same on every attempt: keep the ids you have handled and drop a repeat.
timestamp is when it happened. Events can arrive out of order; compare timestamp if the order
matters to you.
Payloads only grow. Fields may be added to data, and new event types added, without notice;
none is removed or changes meaning. Ignore what you do not know. A change that would break that
comes as a new event type (file.done.v2, say), sent beside the old one for as long as it takes to
move.
file.done’s hash is the file’s processing key: the same for every run of that name in the
project, retries and replacements included. Quote it to support; to tell runs apart, use
completedAt, and to tell events apart, id.
On file.done, download each output whose available is true with
GET /projects/{id}/outputs/download?path=<path>. On file.failed,
POST /projects/{id}/files/{name}/retry and start try it again. On project.error,
POST /projects/{id}/start does.
A project that reads a feed sends the same events. A
file’s name is the one made from the feed item (4f1c2b9a7d3e.8a2b4c6d.mp4), and the files of an
output feed are among its outputs, under feeds/<language>/. By the time file.done is sent,
the output feed links the file, or will on its next rewrite, within 30 seconds. Such a project is
never stopped for being idle, so it sends project.stopped with reason: "idle" only after its
service has read failed for 2 hours.
Verify the signature
Each delivery carries three headers, as the Standard Webhooks specification has them:
To verify one:
- Read the body as raw bytes, before any JSON parsing: re-serialised JSON does not match.
- Refuse a
webhook-timestampmore than 5 minutes from your clock, so an old delivery cannot be replayed. - Compute the signature and compare it, in constant time, with each space-separated entry of
webhook-signature.
pip install standardwebhooks,
npm install standardwebhooks, and others for most languages.
Rotate the secret
secret, shown only there, and previousSecretExpiresAt. Until then
every delivery is signed with both secrets, so:
- Rotate, and give your server the new secret beside the old one.
- Your server accepts a delivery when either secret’s signature matches (the receivers above
already compare every entry of
webhook-signature). - Once the new secret is in place everywhere — within 24 hours — drop the old one.
Retries
A delivery is made when your server answers with a2xx status within 10 seconds of the
attempt starting: connecting, TLS and the status line all count, and the body you send back is not
waited for. Anything else — another status, a redirect (redirects are not followed), a timeout, a
refused connection — is tried again:
A delivery still failing 24 hours after its event is given up, and shows
status: "failed" in
GET /webhooks/{id}/deliveries?status=failed. Each delivery there has lastStatus (your server’s
last HTTP status, or null when it could not be reached) and lastError. Once your server is fixed,
POST /webhooks/{id}/deliveries/{eventId}/retry sends one again, with another 24 hours of retries;
deliveries are kept 7 days. After a longer outage, catch up by listing your projects’ files.
Deliveries to one webhook go one at a time, and a few per webhook in each round: when your server
does not answer, the rest of its deliveries wait for the next round rather than each waiting out
its own timeout. Other accounts’ webhooks are not slowed by yours, nor yours by theirs.
So that retries do no harm:
- Answer fast, then do the work: queue the event and return
204. - Drop repeats by
id: an attempt your server handled but answered too slowly is sent again. - Do not depend on order.