List webhooks
GET /webhooksResponse 200
[
{
"id": "wh_abc123",
"url": "https://myapp.com/webhooks/canarygate",
"events": ["flag.enabled", "flag.disabled"],
"active": true,
"createdAt": "2025-01-01T00:00:00Z"
}
]Create webhook
POST /webhooksBody
{
"url": "https://myapp.com/webhooks/canarygate",
"events": ["flag.enabled", "flag.disabled", "flag.updated"]
}Response 201
{
"id": "wh_abc123",
"url": "https://myapp.com/webhooks/canarygate",
"events": ["flag.enabled", "flag.disabled", "flag.updated"],
"secret": "whsec_xxxxxxxxxxxxxxxx",
"active": true
}The secret field is returned only on creation. Store it in an environment
variable — it cannot be retrieved afterwards.
Update webhook
PATCH /webhooks/:idBody (optional fields)
{
"active": false,
"events": ["flag.enabled"]
}Delete webhook
DELETE /webhooks/:idResponse 204 — no body.
Validating the signature
Each webhook request includes the X-Canarygate-Signature header:
import { createHmac, timingSafeEqual } from 'crypto'
export function verifyWebhookSignature(
rawBody: string,
signature: string,
secret: string
): boolean {
const expected = `sha256=${createHmac('sha256', secret)
.update(rawBody)
.digest('hex')}`
return timingSafeEqual(Buffer.from(signature), Buffer.from(expected))
}Use timingSafeEqual to prevent timing attacks.
Last updated on