Webhooks API Reference
Base URL:
https://auth.calimatic.comWebhooks allow your application to receive real-time HTTP notifications when events occur in the Calimatic Auth platform.
Overview
When you register a webhook, the Calimatic Auth platform will send POST requests to your specified URL whenever subscribed events occur. Each delivery includes:
- A JSON payload describing the event
- An HMAC-SHA256 signature (if a secret is configured) for verification
- Automatic retries on failure (up to 3 attempts)
- Automatic disabling after 10 consecutive failures
Authentication
All webhook management endpoints require authentication via one of:
| Method | Headers |
|---|---|
| Session | Cookie-based session |
| API Key | x-api-key: your-api-key |
| App Client | x-client-id: cca_... + x-client-secret: ccas_... |
Endpoints Overview
| Method | Endpoint | Description |
|---|---|---|
POST | /api/v1/admin/webhooks | Register a new webhook |
GET | /api/v1/admin/webhooks | List all webhooks |
GET | /api/v1/admin/webhooks/{id} | Get a single webhook |
PATCH | /api/v1/admin/webhooks/{id} | Update a webhook |
DELETE | /api/v1/admin/webhooks/{id} | Delete a webhook |
GET | /api/v1/admin/webhooks/{id}/deliveries | View delivery log |
POST /api/v1/admin/webhooks
Register a new webhook to receive event notifications.
Request Body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
url | string | Yes | -- | HTTPS URL to deliver events to. Must use HTTPS in production. |
events | string[] | Yes | -- | Array of event types to subscribe to. Min 1. |
secret | string | No | -- | HMAC secret for payload signing. Min 16 characters, max 255. |
description | string | No | -- | Human-readable description. Max 500 characters. |
isActive | boolean | No | true | Whether the webhook is active. |
Example Request
curl -X POST https://auth.calimatic.com/api/v1/admin/webhooks \
-H "Content-Type: application/json" \
-H "x-client-id: cca_aBcDeFgHiJkL" \
-H "x-client-secret: ccas_xYzAbCdEfGhI" \
-d '{
"url": "https://myapp.com/webhooks/calimatic",
"events": ["user.created", "user.updated", "user.deleted"],
"secret": "whsec_my_webhook_secret_32chars",
"description": "Sync user changes to MyApp"
}'
const response = await fetch("https://auth.calimatic.com/api/v1/admin/webhooks", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-client-id": process.env.CALIMATIC_CLIENT_ID!,
"x-client-secret": process.env.CALIMATIC_CLIENT_SECRET!,
},
body: JSON.stringify({
url: "https://myapp.com/webhooks/calimatic",
events: ["user.created", "user.updated", "user.deleted"],
secret: "whsec_my_webhook_secret_32chars",
description: "Sync user changes to MyApp",
}),
});
Response (201 Created)
{
"success": true,
"data": {
"id": "wh-a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"url": "https://myapp.com/webhooks/calimatic",
"events": ["user.created", "user.updated", "user.deleted"],
"secret": "whsec_my_webhook_secret_32chars",
"description": "Sync user changes to MyApp",
"isActive": true,
"failureCount": 0,
"lastDeliveredAt": null,
"lastFailedAt": null,
"organizationId": null,
"appClientId": null,
"createdAt": "2025-01-15T10:00:00.000Z",
"updatedAt": "2025-01-15T10:00:00.000Z"
}
}
GET /api/v1/admin/webhooks
List all webhook registrations. Results are scoped to the requestor's organization when authenticated via API key or app client.
Example Request
curl https://auth.calimatic.com/api/v1/admin/webhooks \
-H "x-client-id: cca_aBcDeFgHiJkL" \
-H "x-client-secret: ccas_xYzAbCdEfGhI"
Response (200 OK)
{
"success": true,
"data": {
"webhooks": [
{
"id": "wh-a1b2c3d4-...",
"url": "https://myapp.com/webhooks/calimatic",
"events": ["user.created", "user.updated"],
"description": "Sync user changes",
"isActive": true,
"failureCount": 0,
"lastDeliveredAt": "2025-01-15T12:00:00.000Z",
"lastFailedAt": null,
"createdAt": "2025-01-10T10:00:00.000Z",
"updatedAt": "2025-01-15T12:00:00.000Z"
}
]
}
}
GET /api/v1/admin/webhooks/{id}
Get a single webhook by ID.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | string (UUID) | The webhook ID |
Example Request
curl https://auth.calimatic.com/api/v1/admin/webhooks/wh-a1b2c3d4-e5f6-7890 \
-H "x-client-id: cca_aBcDeFgHiJkL" \
-H "x-client-secret: ccas_xYzAbCdEfGhI"
Response (200 OK)
Returns the full webhook object (same schema as the create response).
Error Responses
| Status | Error | Description |
|---|---|---|
| 404 | Webhook not found | No webhook with this ID exists |
PATCH /api/v1/admin/webhooks/{id}
Update a webhook registration. All fields are optional -- only include fields you want to change.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | string (UUID) | The webhook ID |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
url | string | No | New delivery URL |
events | string[] | No | New event subscription list |
secret | string | No | New HMAC secret |
description | string | No | New description |
isActive | boolean | No | Enable or disable the webhook |
When re-enabling a disabled webhook (isActive: true), the failure count is automatically reset to 0.
Example Request
curl -X PATCH https://auth.calimatic.com/api/v1/admin/webhooks/wh-a1b2c3d4-e5f6-7890 \
-H "Content-Type: application/json" \
-H "x-client-id: cca_aBcDeFgHiJkL" \
-H "x-client-secret: ccas_xYzAbCdEfGhI" \
-d '{
"events": ["user.created", "user.updated", "user.deleted", "user.suspended"],
"isActive": true
}'
Response (200 OK)
Returns the updated webhook object.
DELETE /api/v1/admin/webhooks/{id}
Permanently delete a webhook registration. All delivery history for this webhook is retained.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | string (UUID) | The webhook ID |
Example Request
curl -X DELETE https://auth.calimatic.com/api/v1/admin/webhooks/wh-a1b2c3d4-e5f6-7890 \
-H "x-client-id: cca_aBcDeFgHiJkL" \
-H "x-client-secret: ccas_xYzAbCdEfGhI"
Response (200 OK)
{
"success": true,
"data": {
"message": "Webhook deleted"
}
}
GET /api/v1/admin/webhooks/{id}/deliveries
View the delivery log for a webhook. Shows all delivery attempts including successes, failures, and retries.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | string (UUID) | The webhook ID |
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
page | number | 1 | Page number |
limit | number | 20 | Results per page (max 100) |
Example Request
curl "https://auth.calimatic.com/api/v1/admin/webhooks/wh-a1b2c3d4/deliveries?page=1&limit=20" \
-H "x-client-id: cca_aBcDeFgHiJkL" \
-H "x-client-secret: ccas_xYzAbCdEfGhI"
Response (200 OK)
{
"success": true,
"data": {
"deliveries": [
{
"id": "del-uuid-...",
"webhookId": "wh-a1b2c3d4-...",
"eventType": "user.created",
"payload": {
"event": "user.created",
"timestamp": "2025-01-15T10:30:00.000Z",
"data": { "userId": "uuid-...", "email": "jane@school.edu" }
},
"statusCode": 200,
"responseBody": "OK",
"success": true,
"attempt": 1,
"durationMs": 145,
"errorMessage": null,
"createdAt": "2025-01-15T10:30:01.000Z"
},
{
"id": "del-uuid-2-...",
"webhookId": "wh-a1b2c3d4-...",
"eventType": "user.updated",
"payload": { "..." : "..." },
"statusCode": 500,
"responseBody": "Internal Server Error",
"success": false,
"attempt": 3,
"durationMs": 10023,
"errorMessage": "HTTP 500: Internal Server Error",
"createdAt": "2025-01-15T11:00:05.000Z"
}
],
"page": 1,
"limit": 20
}
}
Event Types
| Event Type | Triggered When | Payload Fields |
|---|---|---|
user.created | New user is provisioned | userId, email, firstName, lastName, organizationId, externalId, isNewUser, createdAt |
user.updated | User profile is updated | userId, changes |
user.deleted | User is permanently deleted | userId, email |
user.suspended | User account is suspended | userId, email, reason |
user.reactivated | Suspended user is reactivated | userId, email |
user.deactivated | User account is deactivated | userId, email |
user.password_reset | Password reset is triggered or temporary password is set | userId, triggeredBy, type, timestamp |
user.invited | User receives an invitation | userId, email, organizationId |
organization.created | New organization is created | organizationId, name, slug |
organization.updated | Organization details change | organizationId, changes |
license.assigned | App license assigned to user | userId, application, organizationId |
license.revoked | App license revoked from user | userId, application, organizationId |
Webhook Payload Format
Every webhook delivery sends a POST request with the following structure:
{
"event": "user.created",
"timestamp": "2025-01-15T10:30:00.000Z",
"data": {
"userId": "f1g2h3i4-j5k6-7890-lmno-pq1234567890",
"email": "jane@school.edu",
"firstName": "Jane",
"lastName": "Smith",
"organizationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"externalId": "usr_12345",
"isNewUser": true,
"createdAt": "2025-01-15T10:30:00.000Z"
}
}
Delivery Headers
| Header | Value | Description |
|---|---|---|
Content-Type | application/json | Always JSON |
User-Agent | CalimaticAuth-Webhook/1.0 | Identifies the sender |
X-Webhook-Event | e.g., user.created | The event type |
X-Webhook-Id | UUID | The webhook registration ID |
X-Webhook-Signature | sha256=hex... | HMAC-SHA256 signature (if secret is configured) |
HMAC Signature Verification
If you configured a secret when registering the webhook, each delivery includes an X-Webhook-Signature header. Verify it to ensure the payload was sent by Calimatic Auth and was not tampered with.
How Signatures Are Generated
signature = "sha256=" + HMAC-SHA256(secret, request_body)
The HMAC is computed over the raw JSON request body string using the webhook's secret as the key.
Verification Examples
Node.js / TypeScript:
import { createHmac, timingSafeEqual } from "crypto";
function verifyWebhookSignature(
rawBody: string,
signatureHeader: string,
secret: string,
): boolean {
const expected =
"sha256=" +
createHmac("sha256", secret).update(rawBody).digest("hex");
// Use timing-safe comparison to prevent timing attacks
try {
return timingSafeEqual(
Buffer.from(signatureHeader),
Buffer.from(expected),
);
} catch {
return false;
}
}
// Express middleware
app.post("/webhooks/calimatic", express.raw({ type: "application/json" }), (req, res) => {
const signature = req.headers["x-webhook-signature"] as string;
const rawBody = req.body.toString();
if (!verifyWebhookSignature(rawBody, signature, process.env.WEBHOOK_SECRET!)) {
return res.status(401).send("Invalid signature");
}
const event = JSON.parse(rawBody);
// Process the event...
res.status(200).send("OK");
});
Python:
import hmac
import hashlib
def verify_webhook_signature(raw_body: bytes, signature: str, secret: str) -> bool:
expected = "sha256=" + hmac.new(
secret.encode(), raw_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)
# Flask example
@app.route("/webhooks/calimatic", methods=["POST"])
def webhook_handler():
signature = request.headers.get("X-Webhook-Signature", "")
if not verify_webhook_signature(request.data, signature, WEBHOOK_SECRET):
return "Invalid signature", 401
event = request.get_json()
# Process the event...
return "OK", 200
Retry Policy
When a webhook delivery fails (non-2xx status code or network error), the platform retries with increasing delays:
| Attempt | Delay Before Retry |
|---|---|
| 1st attempt | Immediate |
| 2nd attempt | 1 second |
| 3rd attempt | 5 seconds |
| Final attempt | 30 seconds |
A delivery is considered successful if your endpoint returns any 2xx status code.
Timeout
Each delivery attempt has a 10-second timeout. If your endpoint does not respond within 10 seconds, the attempt is marked as failed.
Best Practices for Webhook Receivers
- Return
200 OKas quickly as possible. Process the event asynchronously if needed. - Store the raw payload and process it in a background job to avoid timeouts.
- Implement idempotency -- you may receive the same event more than once due to retries.
Auto-Disable Behavior
If a webhook accumulates 10 consecutive failures (across all events, not just one), it is automatically disabled:
isActiveis set tofalse- No further deliveries are attempted
- The webhook appears in the list with
isActive: falseand a non-zerofailureCount
Re-Enabling a Disabled Webhook
Fix the underlying issue (endpoint down, authentication problem, etc.), then re-enable:
curl -X PATCH https://auth.calimatic.com/api/v1/admin/webhooks/{id} \
-H "Content-Type: application/json" \
-H "x-client-id: cca_aBcDeFgHiJkL" \
-H "x-client-secret: ccas_xYzAbCdEfGhI" \
-d '{ "isActive": true }'
This resets the failureCount to 0 and resumes deliveries.
Monitoring Webhook Health
Check a webhook's delivery history to diagnose issues:
curl "https://auth.calimatic.com/api/v1/admin/webhooks/{id}/deliveries?limit=10" \
-H "x-client-id: cca_aBcDeFgHiJkL" \
-H "x-client-secret: ccas_xYzAbCdEfGhI"
Look at statusCode, errorMessage, and durationMs fields in the delivery log to identify problems.
Common Error Responses
| Status | Error | Description |
|---|---|---|
| 400 | Invalid URL | Webhook URL must use HTTPS in production |
| 401 | Unauthorized | Missing or invalid authentication |
| 404 | Webhook not found | No webhook with this ID exists |
| 422 | Validation error | Invalid request body (missing URL, invalid events, etc.) |
| 500 | Server error | Internal error |