Webhooks (events and X-Victus-Signature HMAC verification)
Register a webhook endpoint with PUT /webhooks, receive service and billing events, and verify every delivery with the X-Victus-Signature HMAC header.
Because provisioning and lifecycle actions are asynchronous, webhooks are the reliable way to react to state changes without polling. Victus POSTs a JSON event to your URL whenever something happens to your services or balance.
Register your endpoint
# See current webhook config
curl https://control.victuscloud.com/api/reseller/v1/webhooks \
-H "Authorization: Bearer $VICTUS_KEY"
# Set (or update) the delivery URL and subscribed events
curl -X PUT https://control.victuscloud.com/api/reseller/v1/webhooks \
-H "Authorization: Bearer $VICTUS_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourstore.com/hooks/victus",
"events": ["service.ready", "service.suspended", "account.low_balance"]
}'When you set a webhook, the response includes a signing secret (shown once). Store it securely — you need it to verify every incoming delivery via the X-Victus-Signature header.
Events
| Event | Fires when |
|---|---|
| service.provisioning | A create request was accepted and build started. |
| service.ready | A service finished installing and is usable. |
| service.failed | Provisioning or a lifecycle action failed. |
| service.suspended | A service was suspended (by you or non-payment). |
| service.unsuspended | A suspended service was restored. |
| service.terminated | A service was deleted. |
| service.power | A power state change (start/stop/restart/kill). |
| backup.completed | A backup/snapshot finished. |
| account.low_balance | Your prepaid credit balance dropped below the threshold. |
Verify the signature
Every delivery includes an X-Victus-Signature header: the HMAC-SHA256 of the raw request body keyed with your signing secret, hex-encoded. Compute the same HMAC and compare using a constant-time check. Reject any request that does not match — never trust the payload otherwise.
const crypto = require('crypto');
function verifyVictus(rawBody, signatureHeader, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody) // the RAW request body, before JSON.parse
.digest('hex');
const a = Buffer.from(expected);
const b = Buffer.from(signatureHeader || '');
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
// Express example (use the raw body, not the parsed one)
app.post('/hooks/victus', express.raw({ type: '*/*' }), (req, res) => {
const sig = req.header('X-Victus-Signature');
if (!verifyVictus(req.body, sig, process.env.VICTUS_WEBHOOK_SECRET)) {
return res.status(400).send('bad signature');
}
const event = JSON.parse(req.body.toString('utf8'));
// handle event.type ...
res.sendStatus(200);
});Compute the HMAC over the exact bytes received, before any JSON parsing or re-serialization. Re-encoding the body changes whitespace/key order and breaks the signature check.
Return a 2xx quickly (ideally after signature verification) and do heavy work in a background job. Victus retries deliveries that do not receive a 2xx, so make your handler idempotent using the event id.