Features
Webhooks
secr can send HTTP notifications when secrets change. Use webhooks to trigger deployments, sync secret stores, or send alerts. Secret values are never included in payloads.
Events
Subscribe to one or more of the following events when creating a webhook:
| Event | Trigger |
|---|---|
| secret.created | A new secret is created |
| secret.updated | An existing secret is updated |
| secret.deleted | A secret is deleted |
| secret.promoted | Secrets are promoted between environments |
| secret.bulk_set | Secrets are bulk imported |
Payload Format
All webhook payloads share this shape. Secret values are never included.
{
"event": "secret.created",
"timestamp": "2024-01-15T12:00:00.000Z",
"org": { "id": "uuid" },
"project": { "id": "uuid", "slug": "my-project" },
"environment": { "id": "uuid", "slug": "production" },
"key": "DATABASE_URL",
"actor": { "id": "uuid", "email": "user@example.com" },
"metadata": {}
}Headers
Every webhook delivery includes the following headers:
| Header | Description |
|---|---|
| X-Secr-Signature | sha256={hmac} — HMAC-SHA256 of the raw body using your signing secret |
| X-Secr-Event | Event name (e.g. secret.created) |
| Content-Type | application/json |
HMAC Verification
Always verify the X-Secr-Signature header before processing a webhook payload. Use a timing-safe comparison to prevent timing attacks.
Node.js
import { createHmac, timingSafeEqual } from "node:crypto";
function verifyWebhook(body, signatureHeader, secret) {
const expected = createHmac("sha256", secret)
.update(body)
.digest("hex");
const received = signatureHeader.replace("sha256=", "");
return timingSafeEqual(
Buffer.from(expected, "hex"),
Buffer.from(received, "hex"),
);
}Python
import hashlib
import hmac
def verify_webhook(body: bytes, signature_header: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(), body, hashlib.sha256
).hexdigest()
received = signature_header.replace("sha256=", "")
return hmac.compare_digest(expected, received)API Endpoints
All endpoints require admin or owner role.
| Method | Path | Description |
|---|---|---|
| GET | /v1/webhooks/:orgId | List webhooks |
| POST | /v1/webhooks/:orgId | Create webhook (returns signing secret) |
| GET | /v1/webhooks/:orgId/:webhookId | Get webhook + recent deliveries |
| PATCH | /v1/webhooks/:orgId/:webhookId | Update webhook |
| DELETE | /v1/webhooks/:orgId/:webhookId | Delete webhook |
| POST | /v1/webhooks/:orgId/:webhookId/test | Send a test ping |
Scoping
Webhooks are org-wide by default. Optionally scope to a specific project and/or environment when creating:
{
"url": "https://example.com/hook",
"events": ["secret.updated"],
"projectId": "uuid",
"environmentId": "uuid"
}A scoped webhook only fires for events matching its project and environment filters.
Delivery Logs
Each webhook delivery is logged with status code, success flag, and response time. View the last 20 deliveries via the GET /v1/webhooks/:orgId/:webhookId endpoint or the dashboard.
Timeouts & Retries
| Behavior | Detail |
|---|---|
| Timeout | 5-second timeout per delivery |
| Retries | Fire-and-forget -- no automatic retries |
| Failures | Failed deliveries are logged for debugging |
Start using webhooks
Create your first webhook from the dashboard or API.