Webhook event catalog
Every event Clment can send to your webhook endpoint, with example payloads.
Updated 16 Jun 2026
Webhooks let your services react to things happening in Clment without polling. Configure a webhook under Settings → API & Integrations → Webhooks, choose which events you care about (or subscribe to all), and Clment POSTs a signed JSON body to your endpoint when each fires.
This page is the catalog of every event and its payload. To set up an endpoint and verify signatures, see Setting up webhooks.
Envelope
Every delivery has the same envelope. The event-specific data lives in payload:
{
"id": "evt_8f3a…",
"type": "review.completed",
"occurredAt": "2026-06-16T14:32:07.120Z",
"organizationId": "org_a1b2…",
"payload": { }
}
id— unique per event. This is your deduplication key. Retries re-deliver the sameid; apply each event at most once.type— one of the event types below.occurredAt— ISO 8601 timestamp of when the event happened.payload— the event-specific body documented per event below.
Request headers carry the same metadata plus the signature:
X-Clment-Event: review.completed
X-Clment-Event-Id: evt_8f3a…
X-Clment-Delivery-Timestamp: 2026-06-16T14:32:08.000Z
X-Clment-Signature: sha256=2c9f…
Events
contract.created
A document was uploaded. Fires after the contract row is persisted but before background extraction completes — pageCount may be null on first delivery.
{
"contractId": "a1b2…",
"contractNumber": 142,
"title": "Acme Master Services Agreement",
"fileName": "acme-msa.pdf",
"fileFormat": "pdf",
"pageCount": 18,
"createdAt": "2026-06-16T14:30:00.000Z",
"createdBy": "user_77…"
}
contract.classified
The smart classifier finished and assigned a category. Fires once per contract — re-classifications don’t re-fire. Map contractCategory.code to a label via GET /taxonomy.
{
"contractId": "a1b2…",
"contractNumber": 142,
"contractCategory": { "code": "C03", "label": "Services Agreement" },
"contractType": "Master Services Agreement",
"confidence": 0.94,
"reasoningSummary": "Recurring services, SOW framework, no goods transfer."
}
redline.generated
A redline DOCX was generated and is downloadable. The new version row exists by the time this fires — call GET /contracts/{contractId} to find it.
{
"contractId": "a1b2…",
"contractTitle": "Acme MSA",
"playbookName": "Standard SaaS — Buy-side",
"appliedCount": 11,
"totalChanges": 14,
"versionId": "ver_5c…",
"versionNumber": 3,
"versionLabel": "Redline v3",
"filename": "acme-msa-redline.docx"
}
ai_review.generated
An AI review run finished. Carries the review id; call GET /reviews/{reviewId} for the full findings. Note: a review is auto-assigned to its creator on generation — that does not fire review.assigned (see below).
{
"reviewId": "rev_9d…",
"reviewNumber": 37,
"contractId": "a1b2…",
"playbookId": "pb_2e…",
"playbookName": "Standard SaaS — Buy-side",
"mode": "thorough",
"scope": "full",
"overallRisk": "medium",
"findingCount": 14,
"reviewedByUserId": "user_77…",
"reviewedByUserName": "Simon Aspden",
"reviewedAt": "2026-06-16T14:31:50.000Z"
}
review.assigned
A review was assigned to a user for the first time (it previously had no assignee). Use this to notify the new reviewer.
{
"reviewId": "rev_9d…",
"reviewNumber": 37,
"contractId": "a1b2…",
"assignedToUserId": "user_88…",
"assignedToUserName": "Dana Lee",
"assignedByUserId": "user_77…",
"assignedByUserName": "Simon Aspden",
"previousAssigneeUserId": null,
"previousAssigneeUserName": null,
"assignedAt": "2026-06-16T15:00:00.000Z"
}
review.reassigned
A review that already had an assignee was moved to a different user. Distinct from review.assigned so you can notify both the new assignee and the former one. previousAssigneeUserId is always populated here.
{
"reviewId": "rev_9d…",
"reviewNumber": 37,
"contractId": "a1b2…",
"assignedToUserId": "user_99…",
"assignedToUserName": "Priya Nair",
"assignedByUserId": "user_77…",
"assignedByUserName": "Simon Aspden",
"previousAssigneeUserId": "user_88…",
"previousAssigneeUserName": "Dana Lee",
"assignedAt": "2026-06-16T16:10:00.000Z"
}
review.completed
A review’s status flipped to completed (first sign-off). Fires once — subsequent sign-offs on a multi-signer review don’t re-fire.
{
"reviewId": "rev_9d…",
"reviewNumber": 37,
"contractId": "a1b2…",
"completedByUserId": "user_88…",
"completedByUserName": "Dana Lee",
"completedAt": "2026-06-16T17:45:00.000Z",
"overallRisk": "medium",
"findingCount": 14
}
user.mentioned
A user was @-mentioned in a finding comment or a review-level note. Fires once per freshly-added mention.
{
"mentionedUserId": "user_88…",
"mentionedUserEmail": "[email protected]",
"mentionerUserId": "user_77…",
"mentionerName": "Simon Aspden",
"context": "finding",
"reviewId": "rev_9d…",
"reviewNumber": 37,
"contractId": "a1b2…",
"findingId": "fnd_3a…",
"findingTitle": "Liability cap below policy floor"
}
context is "finding" (a per-finding note) or "review" (the review-level reviewer note). findingId and findingTitle are null for review-level mentions.
playbook.updated
A playbook was created or saved. changeKind distinguishes the two; version increments on every save, so watch version > 1 if you only care about edits.
{
"playbookId": "pb_2e…",
"name": "Standard SaaS — Buy-side",
"status": "published",
"version": 4,
"updatedAt": "2026-06-16T18:00:00.000Z",
"changeKind": "updated"
}
Adding new events
We add events over time. New event types are additive — subscribing to “all events” means you’ll start receiving new ones automatically. If we ever change an existing payload’s shape, we ship a new versioned type (e.g. review.completed.v2) and run both in parallel through a deprecation window, so your integration never breaks without warning.