Victus Cloud / Support
RESELLER & API

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"]
  }'
Save the signing secret

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

EventFires when
service.provisioningA create request was accepted and build started.
service.readyA service finished installing and is usable.
service.failedProvisioning or a lifecycle action failed.
service.suspendedA service was suspended (by you or non-payment).
service.unsuspendedA suspended service was restored.
service.terminatedA service was deleted.
service.powerA power state change (start/stop/restart/kill).
backup.completedA backup/snapshot finished.
account.low_balanceYour 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);
});
Verify against the raw body

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.

Respond fast, process later

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.

Was this article helpful?

Still stuck?

Our team can help with your account or service.

Open a ticket →