Developer Reference · Webhooks · v1

Real-time events.

HMAC-SHA256 signed HTTP POST notifications for every key event in your organization — documents, credentials, seals, members, and batches.

HMAC-SHA256 signed 15s timeout · 5 retries Auto-disable after 15 failures Enterprise plan

Quick start

  1. Create an endpoint (UI or API) with an https:// URL and at least one event subscription.
  2. Store the signing secret shown at creation time — it is never shown again.
  3. On each delivery, verify the x-credo-signature header before processing the payload.

Request headers

Every delivery includes:

Header Value
Content-Type application/json
X-Credo-Signature sha256=<hex_digest>
X-Credo-Event The event type, e.g. document.sealed
User-Agent EngineeringID-Webhooks/1.0

Verifying signatures

The signature is an HMAC-SHA256 hex digest of the raw request body, keyed with your endpoint's signing secret.

pseudocode
signature = HMAC-SHA256(secret, raw_body)
expected  = "sha256=" + hex(signature)
Important. Always use a constant-time comparison function to prevent timing attacks.

Elixir / Phoenix

elixir
def valid_signature?(raw_body, secret, header) do
  expected =
    "sha256=" <>
      (:crypto.mac(:hmac, :sha256, secret, raw_body)
       |> Base.encode16(case: :lower))

  Plug.Crypto.secure_compare(expected, header)
end

Python

python
import hmac, hashlib

def valid_signature(raw_body: bytes, secret: bytes, header: str) -> bool:
    digest = hmac.new(secret, raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(f"sha256={digest}", header)

Node.js / TypeScript

typescript
import { createHmac, timingSafeEqual } from "crypto";

function validSignature(rawBody: Buffer, secret: Buffer, header: string): boolean {
  const digest = "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex");
  return timingSafeEqual(Buffer.from(digest), Buffer.from(header));
}

Delivery behavior

Timeout
15s
Per attempt
Retries
5
Exponential backoff
Success
2xx
Any 2xx response
Auto-disable
15
Consecutive failures
Return a 2xx as quickly as possible. If your handler does heavy work, acknowledge receipt immediately and process asynchronously.

Events

Every event payload has the same envelope: an event type identifier, the organization_id that owns it, an ISO 8601 timestamp, and an event-specific data object.

Document events

Document lifecycle changes — uploads, seals, deletions.

EVENT document.created #

A document is uploaded or created in your organization.

json
{
  "event": "document.created",
  "organization_id": "01HXYZ-org-uuid",
  "timestamp": "2026-04-03T12:00:00Z",
  "data": {
    "id": "01HXYZ-doc-uuid",
    "filename": "structural-report.pdf",
    "content_type": "application/pdf",
    "size": 2048576,
    "created_by": {
      "id": "01HXYZ-user-uuid",
      "email": "[email protected]"
    }
  }
}
EVENT document.sealed #

A document is sealed with a professional credential, creating an immutable, verifiable version.

json
{
  "event": "document.sealed",
  "organization_id": "01HXYZ-org-uuid",
  "timestamp": "2026-04-03T12:00:00Z",
  "data": {
    "id": "01HXYZ-doc-uuid",
    "filename": "structural-report.pdf",
    "version": 2,
    "seal": {
      "id": "01HXYZ-seal-uuid",
      "verification_code": "CREDO-ABC123",
      "sealed_by": {
        "id": "01HXYZ-user-uuid",
        "email": "[email protected]"
      },
      "credential": {
        "license_number": "PE-12345",
        "state_code": "CA",
        "profession": "engineer"
      }
    }
  }
}
EVENT document.deleted #

A document is deleted from your organization.

json
{
  "event": "document.deleted",
  "organization_id": "01HXYZ-org-uuid",
  "timestamp": "2026-04-03T12:00:00Z",
  "data": {
    "id": "01HXYZ-doc-uuid",
    "filename": "structural-report.pdf",
    "deleted_by": {
      "id": "01HXYZ-user-uuid",
      "email": "[email protected]"
    }
  }
}

Credential events

Professional license additions and verifications.

EVENT credential.created #

A professional credential is added to the organization.

json
{
  "event": "credential.created",
  "organization_id": "01HXYZ-org-uuid",
  "timestamp": "2026-04-03T12:00:00Z",
  "data": {
    "id": "01HXYZ-cred-uuid",
    "license_number": "PE-12345",
    "state_code": "CA",
    "profession": "engineer",
    "status": "pending",
    "created_by": {
      "id": "01HXYZ-user-uuid",
      "email": "[email protected]"
    }
  }
}
EVENT credential.verified #

A credential passes board verification, confirming the license is valid and active.

json
{
  "event": "credential.verified",
  "organization_id": "01HXYZ-org-uuid",
  "timestamp": "2026-04-03T12:00:00Z",
  "data": {
    "id": "01HXYZ-cred-uuid",
    "license_number": "PE-12345",
    "state_code": "CA",
    "profession": "engineer",
    "status": "verified",
    "verification_method": "board_lookup",
    "verified_at": "2026-04-03T12:00:00Z"
  }
}

Seal events

Cryptographic seals being created or revoked.

EVENT seal.created #

A seal record is written. A seal is the cryptographic proof linking a credential to a specific document version.

json
{
  "event": "seal.created",
  "organization_id": "01HXYZ-org-uuid",
  "timestamp": "2026-04-03T12:00:00Z",
  "data": {
    "id": "01HXYZ-seal-uuid",
    "verification_code": "CREDO-ABC123",
    "document_id": "01HXYZ-doc-uuid",
    "credential_id": "01HXYZ-cred-uuid",
    "document_hash": "sha256:a1b2c3d4...",
    "sealed_by": {
      "id": "01HXYZ-user-uuid",
      "email": "[email protected]"
    }
  }
}
EVENT seal.revoked #

A seal is revoked, invalidating the sealed document version.

json
{
  "event": "seal.revoked",
  "organization_id": "01HXYZ-org-uuid",
  "timestamp": "2026-04-03T12:00:00Z",
  "data": {
    "id": "01HXYZ-seal-uuid",
    "verification_code": "CREDO-ABC123",
    "document_id": "01HXYZ-doc-uuid",
    "reason": "Credential expired",
    "revoked_by": {
      "id": "01HXYZ-user-uuid",
      "email": "[email protected]"
    }
  }
}

Member events

Organization membership changes.

EVENT member.added #

A user joins the organization.

json
{
  "event": "member.added",
  "organization_id": "01HXYZ-org-uuid",
  "timestamp": "2026-04-03T12:00:00Z",
  "data": {
    "user": {
      "id": "01HXYZ-user-uuid",
      "email": "[email protected]"
    },
    "role": "member",
    "added_by": {
      "id": "01HXYZ-admin-uuid",
      "email": "[email protected]"
    }
  }
}
EVENT member.removed #

A user is removed from the organization.

json
{
  "event": "member.removed",
  "organization_id": "01HXYZ-org-uuid",
  "timestamp": "2026-04-03T12:00:00Z",
  "data": {
    "user": {
      "id": "01HXYZ-user-uuid",
      "email": "[email protected]"
    },
    "removed_by": {
      "id": "01HXYZ-admin-uuid",
      "email": "[email protected]"
    }
  }
}

Batch events

Batch sealing operations.

EVENT batch.completed #

A batch seal job finishes processing all documents. Includes success / failure counts.

json
{
  "event": "batch.completed",
  "organization_id": "01HXYZ-org-uuid",
  "timestamp": "2026-04-03T12:00:00Z",
  "data": {
    "batch_id": "01HXYZ-batch-uuid",
    "total": 25,
    "succeeded": 23,
    "failed": 2,
    "initiated_by": {
      "id": "01HXYZ-user-uuid",
      "email": "[email protected]"
    }
  }
}

Need help integrating?

Webhook integrations are available on Enterprise accounts. Tell us about your use case and we'll help you wire it up.

Contact us

Developer Support

Get in touch

Tell us what you're building. We answer integration, schema, auth, rate-limit, SDK, and pricing questions — usually within a business day.

No spam, unsubscribe anytime. We respect your privacy.