Webhooks
Receive real-time notifications when events occur in Keypost.
Setup
- Go to your team settings in the dashboard
- Click Webhooks
- Add your endpoint URL
- Select which events to receive
- Copy the signing secret
Payload format
{
"id": "evt_abc123",
"type": "policy.denied",
"created_at": "2024-01-15T10:30:00Z",
"data": {
"keypost_id": "kp_xyz789",
"keypost_slug": "my-server",
"tool_name": "delete_user",
"policy_name": "No destructive ops",
"reason": "Tool blocked by access policy"
}
}Event types
policy.denied
Fired when a request is blocked by a policy.
{
"type": "policy.denied",
"data": {
"keypost_id": "kp_xyz789",
"keypost_slug": "my-server",
"tool_name": "delete_user",
"policy_name": "No destructive ops",
"policy_type": "access",
"reason": "Tool blocked by access policy",
"request_params": { ... }
}
}approval.requested
Fired when a new approval request is created.
{
"type": "approval.requested",
"data": {
"approval_id": "apr_abc123",
"keypost_slug": "my-server",
"tool_name": "transfer_funds",
"policy_name": "Large transfers",
"approvers": ["finance@company.com"],
"expires_at": "2024-01-16T10:30:00Z"
}
}approval.decided
Fired when an approval is approved or denied.
{
"type": "approval.decided",
"data": {
"approval_id": "apr_abc123",
"decision": "approved",
"decided_by": "admin@company.com",
"note": "Verified with customer"
}
}circuit_breaker.opened
Fired when a circuit breaker opens due to upstream failures.
{
"type": "circuit_breaker.opened",
"data": {
"keypost_slug": "my-server",
"failure_count": 5,
"last_error": "Connection refused"
}
}circuit_breaker.closed
Fired when a circuit breaker recovers.
{
"type": "circuit_breaker.closed",
"data": {
"keypost_slug": "my-server",
"recovery_time_seconds": 120
}
}Verifying signatures
Each webhook includes a signature header for verification:
X-Keypost-Signature: sha256=abc123...Verify by computing HMAC-SHA256 of the raw body using your signing secret:
const crypto = require('crypto');
function verifySignature(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return `sha256=${expected}` === signature;
}Retry behavior
If your endpoint returns a non-2xx status, Keypost retries with exponential backoff:
- 1st retry: 1 minute
- 2nd retry: 5 minutes
- 3rd retry: 30 minutes
- 4th retry: 2 hours
- 5th retry: 24 hours
After 5 failed attempts, the event is dropped and logged.