Journey Contact Captured

Fires once per journey when that journey is first linked to a patient that has persisted contact. The eventType is journey_contact_captured. Category: JOURNEY.

This is the lead event. Contact exists; there is no purchase yet. Deduplicate on event_id. A second attach is a no-op.

It does not fire on OTP begin (that path only looks up email/phone in memory). Emit sites:

  1. Patient attach after existing-user OTP or post-registration.
  2. Journey create when patient is already set on the body (attach never runs).
  3. Safety net: immediately before journey_completed.
📘

v1 and v2 payloads are identical for journey events. These are journey-native events built on the flat, sanitized payload shape — there is no separate raw-envelope form.

Field guarantees

FieldGuarantee
event_type, event_id, api_version, timestamp, timestamp_epoch, journey_id, affiliate_id, template_id, patient_id, patient_marketing_consent, is_returning_patient✅ Always present
template_display_name, journey_type, patient_external_id, started_at, attribution, intake_form_instance_ids⚠️ Conditional — omitted when the underlying data is absent. patient_external_id is omitted when the patient has no external identifier.

No money fields (total_value_cents, products, plan_type) and no first_purchase — there is no order yet. No raw email or phone. Hashed identifiers (email_sha256 / phone_sha256) are not shipped.

Payload (v1 and v2)

{
  "event_type": "journey_contact_captured",
  "event_id": "evt::example",
  "api_version": "2026-08-17",
  "timestamp": "2026-05-26T12:00:00.000Z",
  "timestamp_epoch": 1779796800,
  "journey_id": "jour::example",
  "affiliate_id": "aff::example",
  "template_id": "jtmp::example",
  "template_display_name": "Wellness Program",
  "journey_type": "ONE_TIME",
  "patient_id": "pat::example",
  "patient_external_id": "ext-patient-123",
  "patient_marketing_consent": false,
  "is_returning_patient": false,
  "started_at": "2026-05-26T12:00:00.000Z",
  "intake_form_instance_ids": ["aifi::example"],
  "attribution": {
    "first": {
      "captured_at": "2026-01-04T10:22:31.000Z",
      "landing_url": "https://shop.example.com/get-started",
      "referrer_url": "https://google.com/",
      "params": {
        "utm_source": "google",
        "gclid": "Cj0KCQexample"
      }
    },
    "last": {
      "captured_at": "2026-01-04T10:22:31.000Z",
      "params": {
        "utm_source": "google",
        "gclid": "Cj0KCQexample"
      }
    },
    "touch_count": 1,
    "visitor_id": "550e8400-e29b-41d4-a716-446655440000"
  }
}
FieldTypeAlways?Notes
event_idstring✅Event document _id. Stable across retries — use as the dedup key.
timestamp_epochinteger✅Unix seconds for timestamp. Do not replace ISO timestamp.
patient_idstring✅Always present — this event fires only after the journey is linked to a patient. Identity is this id, not email/phone.
first_purchase, plan_type, money fields—⛔Never sent. No order exists at first contact.
attributionobject⚠️See Attribution Passthrough. landing_url / referrer_url omitted when absent.
template_display_namestring⚠️Name journeys neutrally (no drug or condition names).
intake_form_instance_idsstring[]⚠️AffiliateIntakeFormInstance ids (aifi::…) for custom intake forms on this journey (pre- and post-checkout). Omitted when no instances exist yet.