Webhooks
Signed HTTP notifications when a render finishes, credits run low or something happens on your Telegram bot. How to receive them, verify them and stay correct when one goes missing.
Instead of polling a render until it finishes, register an HTTPS endpoint and Vidmoat will POST a signed event to it.
Create a destination#
Add the endpoint
Open Webhooks in the console, add your public HTTPS URL and choose the events. No events selected means all of them.
Save the signing secret
It starts with
whsec_and is shown when you create the endpoint or roll the secret. Store it with your other server secrets.Send a test
"Send test" delivers one
pingevent and shows the status code and body your receiver returned. The delivery log keeps every attempt.
Events#
| Event | When | data |
|---|---|---|
render.completed | A render finished. | jobId, projectId, url, posterUrl, format, durationSec, width, height |
render.failed | A render failed. | jobId, projectId, error |
credits.low | Your Telegram bot's work took the balance under 200 credits (at most once a day). | appId, remaining, source |
credits.exhausted | The bot refused work because the balance was short (at most every 6 hours). | appId, remaining, source |
app.suspended | We suspended the app; its keys and bot stop. | appId, reason |
telegram.end_user.limit_reached | Somebody hit your per-person daily cap (once per person per day). | appId, endUserId, telegramUserId, limit |
telegram.end_user.first_seen | Somebody used the bot for the first time. | appId, endUserId, telegramUserId, username, chatType |
telegram.end_user.blocked | You blocked somebody, in the console or with the API. | appId, endUserId, telegramUserId, username, via |
Every delivery has the same envelope:
New event types and new data fields can appear. Ignore what you do not recognise.
Verify every request#
Each request carries three headers:
| Header | Value |
|---|---|
Vidmoat-Event-Id | The event id, the same on every retry. |
Vidmoat-Event-Type | For example render.completed. |
Vidmoat-Signature | t=<unix seconds>,v1=<hex HMAC-SHA256> |
The MAC is HMAC-SHA256 with your signing secret over the timestamp, a full stop, and the exact raw request body. Compute it before parsing the JSON, compare in constant time, and reject a timestamp more than five minutes from your clock to stop replays.
Answer quickly, and deduplicate#
Return any 2xx as soon as the event is safely stored, then do the work. Delivery is at least once: store id and skip an event you have already handled.
Retries#
- Each attempt waits up to 10 seconds for your answer.
- A timeout, a network error, a
5xx, a408, a429or a redirect (redirects are not followed) is retried, up to 5 attempts in all, waiting 10, 20, 40 and then 80 seconds between them. - Any other
4xxis final: it means your receiver refused the event. - After 20 failed deliveries in a row the endpoint is disabled, and the console shows why. Fix the receiver, send a test, and enable it again.
Keep a recovery path#
A webhook can be lost to an outage on your side. Reconcile with a bounded poll of GET /v1/renders/{id}, or list recent jobs with GET /v1/renders?projectId=…. Do not queue another render because a notification has not arrived yet.