# Webhooks

Admins can add endpoints in **Settings › Webhooks**. HostEdge POSTs JSON to them when subscribed events happen.

## Events

| Event               | When                                             |
| ------------------- | ------------------------------------------------ |
| `deployment.ready`  | A deployment finished and is live                |
| `deployment.failed` | A deployment failed                              |
| `domain.verified`   | A custom domain's DNS and certificate are active |
| `project.created`   | A project was created                            |
| `project.deleted`   | A project was deleted                            |
| `form.submitted`    | A form on a project received a submission        |

**Send test** delivers a `ping` event.

## Payload

```json
{
  "id": "evt_01m4…",
  "type": "deployment.ready",
  "createdAt": "2026-10-07T12:00:00.000Z",
  "workspace": { "id": "wsp_…", "slug": "acme" },
  "data": { "deployment": { "id": "dep_…", "url": "https://…" }, "project": { "slug": "shop" } }
}
```

Headers: `X-HostEdge-Event`, `X-HostEdge-Delivery` (unique per delivery) and `X-HostEdge-Signature`.

## Verifying signatures

`X-HostEdge-Signature` is `t=<unix seconds>,v1=<hex>`, where the hex is HMAC-SHA256 of `"<t>.<raw body>"` with your endpoint's signing secret. Reject old timestamps to stop replays.

```js
import { createHmac, timingSafeEqual } from 'node:crypto'

export function verify(secret, rawBody, header, toleranceSeconds = 300) {
  const { t, v1 } = Object.fromEntries(header.split(',').map((p) => p.split('=')))
  if (Math.abs(Date.now() / 1000 - Number(t)) > toleranceSeconds) return false
  const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex')
  return v1.length === expected.length && timingSafeEqual(Buffer.from(v1), Buffer.from(expected))
}
```

## Retries

Answer with any 2xx within 10 seconds. Otherwise HostEdge retries after 30 seconds, 2 minutes, 10 minutes, 1 hour and 6 hours, then marks the delivery failed. Every attempt is in the delivery log with the status code and the start of your response, and you can redeliver any event.

If every delivery to an endpoint fails for 3 days, with no success in between, HostEdge turns the endpoint off, stops retrying, and tells the workspace’s Admins in the dashboard and by email. Fix the endpoint, switch it back on, and redeliver what you missed from the log.
