Px/

Webhooks

Receive signed HTTP callbacks when policies change, resources are registered, or access anomalies are detected.

Edit this page on GitHub

Permix can push signed event notifications to any HTTPS endpoint you register. Webhooks are useful for keeping external systems — audit logs, dashboards, SIEM tools — in sync with policy and access changes.

Supported event types#

EventTriggered when
resource.registeredA resource is created or updated via POST /api/v1/resources
policy.rbac.changedA Casbin RBAC rule is created, updated, or deleted
policy.abac.createdAn ABAC policy is created
policy.abac.updatedAn ABAC policy is updated or toggled
policy.abac.deletedAn ABAC policy is soft-deleted
access.deniedA check request returns decision: deny
service_api_key.createdA new service API key is issued
service_api_key.revokedA service API key is deleted

Registering a webhook endpoint#

bash
curl -X POST https://api.permix.dev/api/v1/webhooks \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "url":    "https://your-service.example.com/webhooks/authz",
    "events": ["policy.abac.created", "policy.abac.updated", "access.denied"],
    "secret": "your-signing-secret"
  }'

Response 201:

json
{
  "id":        "wh_abc123",
  "url":       "https://your-service.example.com/webhooks/authz",
  "events":    ["policy.abac.created", "policy.abac.updated", "access.denied"],
  "tenant_id": "tenant_prod",
  "created_at": "2026-05-10T08:00:00Z"
}

Event payload format#

Each delivery is a POST request with Content-Type: application/json:

json
{
  "id":         "evt_xyz789",
  "type":       "policy.abac.created",
  "tenant_id":  "tenant_prod",
  "timestamp":  "2026-05-10T09:15:00Z",
  "data": {
    "policy_id": "3f7a1b2c-...",
    "resource":  "invoice:read",
    "effect":    "allow",
    "created_by": "user_123"
  }
}

Verifying signatures#

Every delivery includes an X-Authz-Signature header containing an HMAC-SHA256 of the raw request body, signed with your secret:

ts
import crypto from "node:crypto";

export function verifyWebhook(
  rawBody: string,
  signature: string,
  secret: string
): boolean {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

Always verify the signature before processing the event. Reject requests with an invalid or missing signature with 400 Bad Request.

Delivery behavior#

  • At-least-once — events may be delivered more than once. Use id for idempotent processing.
  • Retry policy — failed deliveries (non-2xx or timeout) are retried with exponential backoff: 10s, 30s, 2m, 10m, 1h. After five failures the delivery is marked dead and dropped.
  • Timeout — each delivery attempt times out after 10 seconds.
  • Ordering — events within the same tenant are delivered in the order they were generated, but delivery retries may cause out-of-order arrival. Use timestamp to reorder if needed.

Managing webhooks#

bash
# List registered webhooks
GET /api/v1/webhooks

# Update events or URL
PUT /api/v1/webhooks/{id}

# Delete a webhook
DELETE /api/v1/webhooks/{id}

Testing your endpoint#

Use the test delivery endpoint to send a sample ping event to your registered URL:

bash
curl -X POST https://api.permix.dev/api/v1/webhooks/{id}/test \
  -H "Authorization: Bearer $TOKEN"

Your endpoint should respond with 200 OK. The test event has "type": "webhook.test" and a synthetic payload.

Webhook delivery logs

Delivery attempts — including HTTP status, response body, and retry count — are visible in the webhook detail response at GET /api/v1/webhooks/{id}/deliveries. Useful for debugging endpoint issues without checking your own service logs.