Create a webhook
POST /v1/webhooks with scope vms.manage. Subscribing to order_refunded also needs billing.read.
The answer
201 holds the webhook and its signingSecret, 64 hex characters. It is shown once, in this answer, and never again. Store it now. The URL is stored encrypted and is not shown again either, only its host, so it may carry a credential of yours.
An account has at most 10 notification channels, the dashboard’s Telegram and Discord channels included. One more is 400 NOTIFICATION_CHANNEL_LIMIT_REACHED.
GET /v1/webhooks(scopevms.read) lists the account’s webhooks, including those set up in the dashboard under Settings → Notifications, with theirstatus,consecutiveFailures,lastSuccessAt,lastFailureAtandlastError.DELETE /v1/webhooks/{id}(scopevms.manage) deletes one. It gets no further events, and deliveries already queued are dropped.
Events
Every delivery is aPOST with a JSON body:
id names the event: it is the same on every retry of it. occurredAt is when the change happened, not when it was sent. vmId is the server’s id, as in GET /v1/servers/{id}.
reason of vm_suspended:
expired: the term ended unpaid.deletionAtsays when the server is deleted with its data unless its term is renewed first.abuse: the server was found abusing.admin: suspended by our team.system: a paid server could not be started again; our team takes over.
expired, deletionAt is null.
reason of vm_terminated: expired (the term ended unpaid), deleted (by you, in the dashboard or with DELETE /v1/servers/{id}) or admin (by our team).
Headers
Verify the signature
Check every request before you trust it:- Compute the HMAC-SHA256 of the timestamp header, a dot, and the raw request body, exactly as received. Use the signing secret string as the key. Compare
v1=<hex digest>withX-Exy-Signaturein constant time. - Reject the request when the timestamp is more than 300 seconds from your clock: that is a replay of an old request.
https on port 443. The sample servers listen on plain HTTP, so put them behind your TLS proxy.
Answer fast, then work
Store or queue the event first, answer with any2xx within 10 seconds, then do the work. A 2xx counts as delivered and the event is not sent again, so answer only once the event is safe. A slow handler times out and the event is sent again.
Retries
When 5 deliveries in a row have failed for good, after all their retries, the webhook is switched off: its
status becomes failed and the account owner gets an email. A successful delivery resets the count. Resume a failed webhook in the dashboard, or delete it and create it again. A webhook paused in the dashboard is paused; neither state gets events.
Because of retries:
- A delivery can arrive twice. Deduplicate on the event
id(also inX-Exy-Delivery). - A retried delivery can arrive after a newer event. Order events by
occurredAt, not by arrival.