Webhook triggers reference
Choose events, read their JSON payloads, and verify every request.
This page describes the outbound webhook contract: CJM sends a JSON POST to the URL configured in Set up an outbound webhook.
Common request format
Every request has this body shape:
{
"event": {
"id": "unique-event-id",
"trigger": "lead.captured",
"projectId": "project-id",
"profileId": "profile-id-or-null",
"occurredAt": "2026-09-07T10:00:00.000Z",
"queuedAt": "2026-09-07T10:00:01.000Z"
},
"data": {},
"metadata": {}
}data changes with the trigger. metadata can contain contextual values and should be treated as optional. Do not assume that a profile is always present: profileId can be null.
Security headers
| Header | Use |
|---|---|
Content-Type | Always application/json. |
X-CJM-Event-Id | The same ID as event.id; use it to deduplicate retries. |
X-CJM-Trigger | The internal event trigger selected in CJM. Use event.trigger in the body when routing by the public trigger name. |
X-CJM-Timestamp | Time the request was signed. |
X-CJM-Signature | sha256=<hex digest> when a secret is configured. |
To verify the signature, keep the exact raw JSON body. Build timestamp.eventId.body by joining X-CJM-Timestamp, X-CJM-Event-Id, and the raw body with periods, then calculate HMAC-SHA256 with your configured secret. Compare the result with the value after sha256=. Reject a request if it does not match.
Available triggers and data
| Trigger | Sent when | Fields in data |
|---|---|---|
lead.captured | A lead is captured. | eventId, eventName, trigger, timestamp, receivedAt |
customer.converted | A lead becomes a customer. | previousType, currentType, convertedAt |
profile.score.changed | A profile score changes. | oldScore, newScore, fitScore, intentScore, activityScore, scoreIncreased |
profile.stage.changed | A profile changes CRM pipeline stage. | previousStageId, nextStageId, nextStageName, crmStageId, monetaryValue, integrationSlug, eventId; currency and dealId may be present |
form.submission.created | A tracked form submission is created. | submissionId, formId, formConfigId, formVersionId, score fields, submittedAt, eventId |
integration.meta.event_sent | A Meta integration event is dispatched. | providerPayload, normalizedUserData, result |
integration.google_ads.event_sent | A Google Ads integration event is dispatched. | providerPayload, normalizedUserData, hashedUserData, result |
system.test | You select Send test for connectivity. | message, requestedBy, requestedAt |
For Meta and Google Ads, result contains success, status, externalId, and errorSummary. The payload can include personal data supplied to the destination, so secure the receiver, limit access to its logs, and apply your privacy policy.
Receiver checklist
- Read the raw request body before changing or parsing it for signature verification.
- Verify
X-CJM-Signatureand reject invalid signatures. - Deduplicate by
X-CJM-Event-Idbefore performing an irreversible action. - Route using
event.trigger, then validate the expecteddatafields for that trigger. - Return a 2xx response only after accepting the event.
See Set up an outbound webhook for retry behaviour and troubleshooting.