> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vmarea.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Subscribe to VMArea events and receive real-time HTTP POST notifications at your own HTTPS endpoint.

Webhooks let you subscribe to VMArea events and receive real-time HTTP POST notifications at your own HTTPS endpoint. Requires the `webhooks:write` scope to manage subscriptions, and `webhooks:read` to list them.

## Subscribing

Create a webhook subscription with `POST /webhooks`:

```bash theme={null}
curl -X POST https://api.vmarea.com/api/public/v1/webhooks \
  -H "x-api-key: $VMAREA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-server.example.com/hooks/vmarea",
    "events": ["vm.created", "vm.started", "billing.payment_received"],
    "description": "Production listener"
  }'
```

<Warning>
  The response includes a `secret` field — copy it immediately, it is shown only once. You use this secret to verify incoming requests.
</Warning>

Endpoint URLs must use HTTPS. Each account can have up to 10 active webhook subscriptions.

## Event types

| Event                      | Description                                                                       |
| -------------------------- | --------------------------------------------------------------------------------- |
| `vm.created`               | A VM has finished provisioning.                                                   |
| `vm.started`               | A VM has started.                                                                 |
| `vm.stopped`               | A VM has stopped.                                                                 |
| `vm.restarted`             | A VM has restarted.                                                               |
| `vm.suspended`             | A VM has been suspended (e.g., due to non-payment).                               |
| `vm.terminated`            | A VM has been permanently deleted.                                                |
| `vm.renewed`               | A VM billing cycle has renewed.                                                   |
| `billing.payment_received` | A wallet top-up was successfully processed.                                       |
| `billing.low_balance`      | Wallet balance has dropped below the threshold needed to cover upcoming renewals. |
| `ticket.reply`             | A support ticket has received a new reply.                                        |

## Signature verification

Every delivery includes an HMAC-SHA256 signature so you can verify the request came from VMArea.

**Headers sent with each delivery:**

| Header                 | Value                                                                                |
| ---------------------- | ------------------------------------------------------------------------------------ |
| `X-VMArea-Event`       | The event type string (e.g., `vm.started`).                                          |
| `X-VMArea-Signature`   | `sha256=<hex-digest>` of the raw JSON request body, signed with your webhook secret. |
| `X-VMArea-Delivery-Id` | A unique ID for this delivery attempt.                                               |

**Verification example:**

```js theme={null}
import crypto from "node:crypto";

function verifySignature(rawBody, signatureHeader, secret) {
  const expected = "sha256=" +
    crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signatureHeader)
  );
}

// Express example
app.post("/hooks/vmarea", express.raw({ type: "application/json" }), (req, res) => {
  const sig = req.headers["x-vmarea-signature"];
  if (!verifySignature(req.body, sig, process.env.VMAREA_WEBHOOK_SECRET)) {
    return res.status(401).send("Invalid signature");
  }
  const event = req.headers["x-vmarea-event"];
  const payload = JSON.parse(req.body);
  // handle event ...
  res.status(200).send("ok");
});
```

<Note>
  Always use `crypto.timingSafeEqual` (or equivalent) to prevent timing attacks.
</Note>

## Retry semantics

If your endpoint does not return a `2xx` response within 15 seconds, VMArea retries the delivery up to **3 attempts total** with exponential backoff (initial delay 10 seconds, doubling per attempt).

After **50 consecutive delivery failures** across all events, the webhook subscription is automatically disabled. You can re-enable it from the dashboard or via `PATCH /webhooks/:id` with `{ "isActive": true }`.

Delivery history (status code, success flag, attempt count) is available via `GET /webhooks/:id/deliveries`.

## Best practices

* **Respond quickly.** Return `2xx` before doing any heavy processing — offload work to a queue so you don't hit the 15-second timeout.
* **Make handlers idempotent.** The same event may be delivered more than once (e.g., after a retry). Use `X-VMArea-Delivery-Id` to deduplicate.
* **Always verify the signature.** Do not trust the payload without checking the HMAC.
* **Use a dedicated endpoint** per environment (production, staging) so you can inspect and replay events independently.
