Webhooks
Set a webhook URL and secret in the integration's dashboard settings and enable delivery. Cloud API uses one endpoint for all event types. Delivery history is available in the dashboard. The webhook secret is separate from the API key.
Envelope
Events arrive as HTTP POST with Content-Type: application/json and X-Webhook-Signature:
{
"event": "order.completed",
"timestamp": "2026-09-28T12:00:00.000Z",
"data": {
"order_id": 401,
"status": "completed",
"amount_usd": "5.0000",
"currency": "USD",
"vm_id": 101,
"rental_id": null,
"error_code": null,
"error_message": null,
"order_type": "vm_purchase",
"api_user_id": "customer-123"
}
}
event is the event type; timestamp is the envelope creation time in UTC. data is event-specific. Resource events also contain the numeric data.event_id and data.created_at from the source event; order events do not contain those two fields. Some event fields are optional and are omitted when absent at the source.
Order events
| Event | Meaning |
|---|---|
order.completed | Paid action completed; dedicated purchase is reserved, not yet issued |
order.failed | Order failed; inspect error and transaction history |
order.refunded | Order amount returned to the integration balance |
order.review | Uncertain or partial result requiring clarification |
All four use the order fields shown above: order_id, order_type, api_user_id, status, amount_usd, currency, nullable vm_id, rental_id, error_code and error_message. vm_id is returned only for a completed order. ipv4_renewal additionally includes ip_id and paid_until. A completed VPS purchase can include guest_initialization; it is null when no result is available.
Order types: vm_purchase, vm_renewal, vm_resize, traffic_purchase, ipv4_purchase, ipv4_renewal, dedicated_purchase, dedicated_renewal. A refund returns the original positive order amount; use event/status to distinguish it from a purchase.
VPS events
Common fields: event_id, created_at, vm_id; api_user_id, order_id and operation_id when present. The table lists additional fields. Creation includes ips as objects containing only address. Deletion includes held_ips matching the VPS deletion result. Failure events include public error_code and error_message.
| Event | Meaning | Additional data fields |
|---|---|---|
vm.rescue.enabled | Rescue enabled | status, rescue |
vm.rescue.enable_failed | Rescue enable failed | requires_review, error_code, error_message |
vm.rescue.disabled | Normal OS boot restored | status, rescue |
vm.rescue.disable_failed | Rescue exit failed | requires_review, error_code, error_message |
vm.reinstalled | OS reinstalled | os, hostname, wipe_extra_disks, status, guest_initialization |
vm.reinstall_failed | OS reinstall failed | requires_review, error_code, error_message |
vm.password.changed | Password changed | login, password_changed, status |
vm.password.change_failed | Password change failed | requires_review, error_code, error_message |
vm.created | VPS created | status, hostname, smtp_blocked, paid_until, ips, guest_initialization |
vm.creation_failed | VPS creation failed | status, requires_review, error_code, error_message |
vm.resized | Plan resources changed | status, cpu_cores, cpu_percent, ram_mb, disk_gb, traffic_limit_bytes, resources_applied, traffic_applied |
vm.resize_failed | Plan change failed or partially applied | requires_review, status, cpu_cores, cpu_percent, ram_mb, disk_gb, traffic_limit_bytes, resources_applied, traffic_applied, error_code, error_message |
vm.renewed | VPS renewed | paid_until, resume_pending, auto_start |
vm.traffic.topped_up | Extra traffic added | period_id, quota_revision, additional_bytes, total_additional_bytes, remaining_bytes |
vm.traffic.updated | Traffic quota changed | period_id, quota_revision, previous_limit_bytes, traffic_limit_bytes, remaining_bytes |
vm.ip.attached | IPv4 attached | status, ip_id, detached_ip_id, keep_ip, addresses, guest_configuration_required |
vm.ip.attach_failed | IPv4 attachment failed | requires_review, error_code, error_message |
vm.ip.replaced | IP replacement reported | status, ip_id, detached_ip_id, keep_ip, addresses, guest_configuration_required |
vm.ip.replace_failed | IP replacement failed | requires_review, error_code, error_message |
vm.ip.detached | IPv4 detached | status, ip_id, detached_ip_id, keep_ip, addresses, guest_configuration_required |
vm.ip.detach_failed | IPv4 detach failed | requires_review, error_code, error_message |
vm.power.changed | Power command completed | action, status, uptime |
vm.power.failed | Power command failed | action, requires_review, error_code, error_message |
vm.deleted | VPS deleted | status, keep_ips, reason, paid_until, held_ips |
vm.delete_failed | VPS deletion failed | requires_review, error_code, error_message |
vm.expiring | Paid term ending soon | paid_until, hours_before, delete_after |
vm.deleting_soon | One hour before VPS deletion | paid_until, hours_before, delete_after, reason |
vm.suspended | VPS suspended | status, reason, paid_until, delete_after |
vm.resumed | VPS resumed | status, reason, paid_until, auto_started |
vm.traffic.low | Traffic quota nearly exhausted | period_id, quota_revision, starts_at, ends_at, included_bytes, additional_bytes, outgoing_bytes, overage_bytes, remaining_bytes |
vm.traffic.exhausted | Traffic quota exhausted | period_id, quota_revision, starts_at, ends_at, included_bytes, additional_bytes, outgoing_bytes, overage_bytes, remaining_bytes |
vm.shaping.changed | Bandwidth limit changed | previous_reason, reason, limit_mbps, bandwidth_limited_until |
Initialization and Rescue results
vm.created, vm.reinstalled and completed VPS purchase orders report guest_initialization separately from resource creation:
{
"guest_initialization": {
"status": "failed",
"error_code": "cloud_init_failed"
}
}
Possible statuses: succeeded with error_code=null, failed with cloud_init_failed, or unknown with guest_initialization_timeout after 15 minutes. Custom initialization may fail or remain unconfirmed while the running VPS is retained and its purchase completes. This is not a creation failure or an automatic refund. The resource event omits the field when unavailable.
Successful Rescue events include this nested state:
{
"event": "vm.rescue.enabled",
"timestamp": "2026-09-28T12:00:00.000Z",
"data": {
"event_id": 701,
"created_at": "2026-09-28T12:00:00Z",
"vm_id": 101,
"api_user_id": "customer-123",
"operation_id": 501,
"status": "running",
"rescue": {
"active": true,
"access": "ssh",
"requires_review": false,
"operation_id": 501,
"operation_status": "succeeded"
}
}
}
On exit, rescue.active=false; the VPS can remain stopped if its paid term expired or it was suspended. active means saved Rescue context, not guaranteed SSH availability. access can be ssh, console or null. Failure events carry requires_review, error_code and error_message; read VPS details and the operation for the current state.
Passwords, cloud-init contents, stdout and stderr are not sent in these webhooks. See Cloud-init and Rescue for request rules.
IPv4 events
Common fields: event_id, created_at, ip_id, address, family, vm_id, api_user_id, paid_until when present. A saved address has vm_id=null. Additional fields:
| Event | Meaning | Additional data fields |
|---|---|---|
ip.expiring | Paid IPv4 term ending soon | hours_before, delete_after |
ip.releasing_soon | One hour before IPv4 release | hours_before, delete_after, reason |
ip.suspended | IPv4 suspended | reason, delete_after |
ip.resumed | IPv4 resumed | reason |
ip.released | IPv4 released | reason |
Dedicated rental events
Common fields when present: event_id, created_at, rental_id, plan_id, order_id, api_user_id, status, issued_at, paid_until, overdue_at, finished_at. Credentials are not sent in the webhook: retrieve rental details after dedicated.issued. Additional fields:
| Event | Meaning | Additional data fields |
|---|---|---|
dedicated.preparing | Rental reserved and being prepared | — |
dedicated.issued | Dedicated server issued | — |
dedicated.expiring | Rental payment ending soon | hours_before |
dedicated.overdue | Rental payment expired | release_after |
dedicated.releasing_soon | One hour before rental release | hours_before, release_after, reason |
dedicated.renewed | Rental renewed | — |
dedicated.released | Rental released or cancelled; inspect reason | reason |
dedicated.completed | Rental cleanup completed | — |
Field meanings and final warnings
- Dates (
created_at,paid_until,starts_at,ends_at,delete_after,release_after,bandwidth_limited_until) are UTC timestamps; optional dates can be null. Byte counters are integers.limit_mbpsis a speed limit in Mbps, or null. statusdescribes the affected resource, not the delivery.reasondescribes the transition, for examplepayment_expired. Do not treat reason strings as the entire event identity.hours_beforeis the warning interval.delete_afteris the VPS/IPv4 deletion or release deadline;release_afteris the dedicated rental deadline.resources_applied/traffic_appliedindicate which parts of a resize were applied.requires_review=truemeans the result needs clarification before another command.period_idandquota_revisionidentify the traffic period and its quota revision.additional_bytesis the added/current quota according to the event;total_additional_bytesis the total after a top-up.remaining_bytesis the remaining quota.guest_configuration_required=truemeans the customer's OS network configuration needs attention.addressescontains resulting addresses;keep_ip/keep_ipsexpresses retention. Read current VPS/IP details for the resulting state.resume_pending,auto_startandauto_starteddescribe restoration after payment. Renewal success does not necessarily mean the VPS is already running.
vm.deleting_soon, ip.releasing_soon and dedicated.releasing_soon warn one hour before removal after the 24-hour unpaid grace period. They are forwarded even when auto-renewal is off or cannot charge the balance. When enabled, Cloud API makes the final renewal attempt. The warning still describes its original deadline; inspect renewal events or the current resource to learn the outcome.
Example final VPS warning:
{
"event": "vm.deleting_soon",
"timestamp": "2026-09-29T11:00:00.000Z",
"data": {
"event_id": 701,
"created_at": "2026-09-29T11:00:00+00:00",
"vm_id": 101,
"api_user_id": "customer-123",
"paid_until": "2026-09-28T12:00:00+00:00",
"hours_before": 1,
"delete_after": "2026-09-29T12:00:00+00:00",
"reason": "payment_expired"
}
}
Signature verification
X-Webhook-Signature is the lowercase hexadecimal HMAC-SHA256 of the exact raw request bytes, signed with the integration's webhook secret. The secret is not sent. Verify the original bytes before parsing JSON; re-serializing JSON changes the signature input.
const crypto = require('node:crypto');
function verifySignature(rawBody, secret, signature) {
if (typeof signature !== 'string' || !/^[a-f0-9]{64}$/.test(signature)) {
return false;
}
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest();
return crypto.timingSafeEqual(expected, Buffer.from(signature, 'hex'));
}
rawBody must be the original Buffer from your HTTP server, before its JSON middleware.
Acknowledgement, repeats and delivery
- Verify the signature and save the event.
- Apply it once: for resource events deduplicate by integration and
data.event_id; for order results use integration,data.order_idandevent. - Return HTTP 200 or 201 after accepting the event. No response body is required. Other codes, including 202 and 204, do not acknowledge delivery.
The HTTP timeout is 10 seconds. Once accepted by the delivery service, an unsuccessful delivery has up to 10 attempts, with delays after the preceding failed attempt:
| Attempt | Delay |
|---|---|
| 1 | Immediately |
| 2 | 60 seconds |
| 3 | 120 seconds |
| 4 | 300 seconds |
| 5 | 600 seconds |
| 6 | 900 seconds |
| 7 | 1800 seconds |
| 8 | 3600 seconds |
| 9 | 7200 seconds |
| 10 | 7200 seconds |
Delivery order is not guaranteed. A late warning must not overwrite a newer paid term in your billing. Recheck current state when events conflict. Order results and resource events can describe the same action; avoid granting the service or crediting a refund twice.
A webhook is not a guarantee that every result reaches your server. If absent, query orders, VPS, rentals or IPs. Delivery history is in the dashboard, not a separate public history endpoint. Some supported events, such as vm.ip.replaced, do not imply there is a matching public replacement route; follow the published route catalog.