Fires once per completed affiliate custom intake form instance when that instance becomes valid (all required questions answered). The event_type is custom_intake_form_completed. Categories: JOURNEY and PATIENT.
Preview instances (isPreview: true) never emit this event. Deduplicate on event_id. The platform also enforces at most one live completion event per intake form instance, so retries and concurrent answer requests must not produce a second webhook for the same instance.
The event fires once per form instance when the patient finishes the form — whether that happens before checkout, after checkout, or before logging in. If the patient completes the form before creating an account, no webhook is sent at that moment; after the patient is linked to the journey, the platform emits one custom_intake_form_completed for that instance (if none was recorded yet), including patient_id and patient_marketing_consent for your affiliate. Retries resend the same payload; they do not create a second completion for the same instance.
v1 and v2 payloads are identical for this event. It is built as a journey-native, flat, sanitized payload at emit time — there is no separate raw-envelope form.
Field guarantees
| Field | Guarantee |
|---|---|
event_type, event_id, api_version, timestamp, timestamp_epoch, affiliate_id, intake_form_id, intake_form_instance_id, completed_at, responses, patient_marketing_consent | ✅ Always present |
journey_id | ⚠️ Conditional — omitted when the instance has no originatingJourney. Never sent as null. |
patient_id | ⚠️ Conditional — omitted only when no patient is linked to the instance at emit time. Pre-login completion is not emitted until the patient is linked, so the webhook you receive normally includes patient_id. Never sent as null. |
patient_external_id | ⚠️ Conditional — omitted when the linked patient has no externalIdentifier. Independent of patient_id: a payload can include patient_id without patient_external_id. Never sent as null. |
No journey template fields (template_id, money fields, first_purchase, plan_type) and no is_returning_patient. The responses array is the form snapshot at completion: question labels and answer values only — not a substitute for the full Responses export in Command.
patient_marketing_consent is captured when the event is created and does not change on retry. It reflects the answer to your form's marketing-consent question for your affiliate: true when the patient answered Yes, false when they answered No or the question was not flagged. Consent is per affiliate, so a "Yes" on another brand's form does not show as true for your webhooks. See Affiliate Custom Intake Forms.
Payload (v1 and v2)
{
"event_type": "custom_intake_form_completed",
"event_id": "evt::example",
"api_version": "2026-08-17",
"timestamp": "2026-05-26T12:05:00.000Z",
"timestamp_epoch": 1779797100,
"affiliate_id": "aff::example",
"patient_id": "pat::example",
"patient_external_id": "ext-patient-123",
"patient_marketing_consent": true,
"journey_id": "jour::example",
"intake_form_id": "aif::example",
"intake_form_instance_id": "aifi::example",
"completed_at": "2026-05-26T12:05:00.000Z",
"responses": [
{
"location_id": "aifl::example",
"label": "Join our marketing emails?",
"value": "yes"
}
]
}| Field | Type | Always? | Notes |
|---|---|---|---|
event_id | string | ✅ | Event document _id. Stable across retries — use as the dedup key. |
timestamp / timestamp_epoch | string / integer | ✅ | Set at event creation from the instance completionDate when present (otherwise first emit time). Stored in eventData and not regenerated on each v2 delivery retry for this event (unlike some other v2 webhooks). Use completed_at for completion semantics. |
intake_form_id | string | ✅ | Published AffiliateIntakeForm id the patient answered. |
intake_form_instance_id | string | ✅ | One completion webhook per instance id. |
completed_at | string (ISO 8601) | ✅ | When the instance became valid (completionDate). Matches timestamp / timestamp_epoch because all three are captured once at event creation. |
responses | array | ✅ | One entry per stored answer on that instance. May be empty if the form had no answerable questions. |
responses[].location_id | string | ✅ | AffiliateIntakeFormLocation id. |
responses[].label | string | ✅ | From the question’s data.question, data.label, or data.title. |
responses[].value | string, number, boolean, or array | ✅ | Scalar or string array for multi-select answers. File-upload answers are reduced to a file name string when available. Nested objects are stripped at sanitization. |
patient_marketing_consent | boolean | ✅ | Captured when the event is created: true for Yes on your flagged question, false for No or when the question is not flagged. |
Subscription
Subscribe under Journey (or any webhook configuration that includes the JOURNEY category). The event also carries the PATIENT category tag for dictionary grouping but behaves like other journey-native prebuilt payloads.
