Webhooks API Reference

Base URL: https://auth.calimatic.com

Webhooks 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:

MethodHeaders
SessionCookie-based session
API Keyx-api-key: your-api-key
App Clientx-client-id: cca_... + x-client-secret: ccas_...

Endpoints Overview

MethodEndpointDescription
POST/api/v1/admin/webhooksRegister a new webhook
GET/api/v1/admin/webhooksList 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}/deliveriesView delivery log

POST /api/v1/admin/webhooks

Register a new webhook to receive event notifications.

Request Body

FieldTypeRequiredDefaultDescription
urlstringYes--HTTPS URL to deliver events to. Must use HTTPS in production.
eventsstring[]Yes--Array of event types to subscribe to. Min 1.
secretstringNo--HMAC secret for payload signing. Min 16 characters, max 255.
descriptionstringNo--Human-readable description. Max 500 characters.
isActivebooleanNotrueWhether 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

ParameterTypeDescription
idstring (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

StatusErrorDescription
404Webhook not foundNo 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

ParameterTypeDescription
idstring (UUID)The webhook ID

Request Body

FieldTypeRequiredDescription
urlstringNoNew delivery URL
eventsstring[]NoNew event subscription list
secretstringNoNew HMAC secret
descriptionstringNoNew description
isActivebooleanNoEnable 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

ParameterTypeDescription
idstring (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

ParameterTypeDescription
idstring (UUID)The webhook ID

Query Parameters

ParameterTypeDefaultDescription
pagenumber1Page number
limitnumber20Results 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 TypeTriggered WhenPayload Fields
user.createdNew user is provisioneduserId, email, firstName, lastName, organizationId, externalId, isNewUser, createdAt
user.updatedUser profile is updateduserId, changes
user.deletedUser is permanently deleteduserId, email
user.suspendedUser account is suspendeduserId, email, reason
user.reactivatedSuspended user is reactivateduserId, email
user.deactivatedUser account is deactivateduserId, email
user.password_resetPassword reset is triggered or temporary password is setuserId, triggeredBy, type, timestamp
user.invitedUser receives an invitationuserId, email, organizationId
organization.createdNew organization is createdorganizationId, name, slug
organization.updatedOrganization details changeorganizationId, changes
license.assignedApp license assigned to useruserId, application, organizationId
license.revokedApp license revoked from useruserId, 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

HeaderValueDescription
Content-Typeapplication/jsonAlways JSON
User-AgentCalimaticAuth-Webhook/1.0Identifies the sender
X-Webhook-Evente.g., user.createdThe event type
X-Webhook-IdUUIDThe webhook registration ID
X-Webhook-Signaturesha256=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:

AttemptDelay Before Retry
1st attemptImmediate
2nd attempt1 second
3rd attempt5 seconds
Final attempt30 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 OK as 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:

  • isActive is set to false
  • No further deliveries are attempted
  • The webhook appears in the list with isActive: false and a non-zero failureCount

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

StatusErrorDescription
400Invalid URLWebhook URL must use HTTPS in production
401UnauthorizedMissing or invalid authentication
404Webhook not foundNo webhook with this ID exists
422Validation errorInvalid request body (missing URL, invalid events, etc.)
500Server errorInternal error