Marketing attribution rides order-side and journey-side v2 webhooks as an attribution object. Use it to stitch a paid order or renewal back to the campaign that started the journey.
Attribution is omitted entirely when nothing was captured. Keys that were never stored are omitted — they are never sent asnull. Omission does not changeapi_version.
Shape
{
"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",
"utm_medium": "cpc",
"utm_campaign": "spring_launch",
"telegra_utm_source": "google",
"gclid": "Cj0KCQexample",
"fbp": "fb.1.1700000000.1234567890"
}
},
"last": {
"captured_at": "2026-03-11T18:02:55.000Z",
"params": {
"telegra_utm_source": "facebook",
"fbclid": "IwARexample",
"fbp": "fb.1.1700000000.1234567890",
"fbc": "fb.1.1700000000.IwARexample"
}
},
"touch_count": 2,
"visitor_id": "550e8400-e29b-41d4-a716-446655440000"
},
"journey_id": "jour::example"
}| Field | Always? | Notes |
|---|---|---|
first / last | ⚠️ | First touch is written once and never overwritten. Last is overwritten on each later touch. |
first.params / last.params | ⚠️ | Open bag of UTMs, ad click IDs (gclid, fbclid, …), Meta cookies (fbp/fbc), and telegra_* keys. Not a place for URLs. |
first.landing_url / last.landing_url | ⚠️ | Query-stripped http(s) landing page. Omitted when the storefront did not send it (older commerce bundles omit it forever). |
first.referrer_url / last.referrer_url | ⚠️ | Query-stripped http(s) referrer (document.referrer). Credentials, query, and fragments are stripped. Omitted when empty. |
touch_count | ⚠️ | Number of persisted touches. |
visitor_id | ⚠️ | Storefront visitor id used to join Journey Viewed → journey create. |
journey_id | ⚠️ | Join key next to attribution on order-side events. |
Why both first and last touch
firstanswers the acquisition question — which channel originally brought the patient.lastis the latest captured parameter set. It may include non-click values such astelegra_*,fbp, orfbc. Agclidorfbclidappears inlast.paramsonly when that latest touch contained one — not because last touch always represents the click immediately before conversion. Preferlastwhen replaying a click ID to an ad platform so you do not credit a stale first-touch click.
Subscription renewals inherit the parent order's attribution (same first/last block).
Sending tracking codes in
Append query parameters to any journey storefront URL. No per-client Telegra configuration is required.
Captured parameters
| Kind | Keys | Notes |
|---|---|---|
| Prefixed client tags | any telegra_* | e.g. telegra_utm_source, telegra_utm_medium, telegra_utm_campaign, or any other telegra_* name |
| Standard UTM parameters | utm_source, utm_medium, utm_campaign, utm_content, utm_term, utm_id | Captured without a telegra_ prefix — campaign URL builders and ad-platform auto-tagging emit these names and cannot be configured to prefix them |
| Ad platform click IDs | gclid, gbraid, wbraid, dclid, fbclid, msclkid, ttclid, twclid, sccid, epik, rdt_cid, irclickid, li_fat_id | Captured without a telegra_ prefix because platforms append them |
| Meta browser / click cookies | fbp, fbc | Read from _fbp / _fbc cookies on the storefront when present; refreshed on each visit that still has the cookies |
Standard utm_* parameters no longer need a telegra_ prefix. Both forms are accepted and both are returned. If you send utm_source and telegra_utm_source, you get both back as separate keys — Telegra does not merge or deduplicate them.
Recognition is case-insensitive, because platforms disagree on casing for what they append (Snapchat sends ScCid). Keys are stored and returned exactly as you sent them, so UTM_Source comes back as UTM_Source.
Do not put PHI, patient identifiers, or other sensitive data in attribution inputs. Use only non-sensitive campaign identifiers (UTM-style tags, ad click IDs, and Metafbp/fbccookie values). Telegra stores and re-emits accepted params verbatim — it does not scrub health or identity content from these keys.
Validation limits
Invalid or excess parameters are dropped silently. The journey still loads.
| Limit | Applies to | Value |
|---|---|---|
| Max keys per touch | attribution params bag | 25 |
| Max value length | attribution params bag | 512 characters |
| Max URL length | landing_url, referrer_url | 1024 characters (separate from the params bag) |
| Key charset | params bag | alphanumeric, _, - (^[a-zA-Z0-9_-]+$). Dots are rejected |
| Repeated parameter | params bag | First value wins (?utm_source=a&utm_source=b → a) |
| Value charset | params bag | printable ASCII (space through ~) |
| URL protocol | landing_url, referrer_url | http or https only. Credentials, query, and fragments are stripped. |
Example URL
https://shop.example.com/get-started?telegra_utm_source=google&telegra_utm_medium=cpc&telegra_utm_campaign=spring_launch&gclid=Cj0KCQexample&fbclid=IwARexampleCapture (storefront)
landing_url and referrer_url are top-level fields — they are not inside the JSON attribution params bag. That bag is capped at 512 characters per value and only accepts UTM / click-ID / telegra_* keys — an unprefixed landing_url would be dropped.
| Endpoint | Where | Fields |
|---|---|---|
GET /v2/journeyTemplates/{slug}/frontend | query | landing_url, referrer_url, campaignId, plus attribution, visitorId, track |
POST /journeys | JSON body | landing_url, referrer_url, visitorId, attribution |
Both URL fields:
- Max length 1024 characters. URLs exceeding this limit are dropped or omitted from the stored touch (not truncated, and not rejected with an HTTP error).
- Must be
httporhttps. Other schemes are dropped. - Credentials, query string, and fragment are stripped before storage. Campaign params already live in the params bag.
- Omitted from the stored touch when empty or invalid. Older commerce bundles that never send them omit them forever.
landing_url is taken from window.location.href. referrer_url is taken from document.referrer and omitted when empty.
The storefront also forwards a JSON attribution query/body parameter along with track and visitorId (used to distinguish first vs repeat visits for the same browser).
Renewal orders copy the parent checkout attribution, so a month-6 subscription_renewed still carries the original first.landing_url / first.params.
Neutral naming
Journey slugs, template_display_name, and landing_url must be named neutrally (no drug or condition names in origin-facing copy). Name the journey neutrally rather than stripping template_display_name.
Who receives attribution
attributionv2 categories that allowlist attribution: ORDER, PRESCRIPTION, SHIPPING, PHARMACY, PAYMENT, LAB, JOURNEY, COMMERCE.
SUPPORT, PATIENT, and INTERNAL do not receive attribution.
| Event family | Carries attribution | Join key |
|---|---|---|
Journey (journey_contact_captured, journey_checkout_started, journey_completed, journey_abandoned) | Yes | ecommerce_order_id once an order exists |
| Order / commerce / payment | Yes | journey_id |
| Prescription / pharmacy / shipping / lab | Yes | journey_id |
| Support, patient | No | — |
The order is the preferred source, so a subscription renewal carries the attribution of the order that created it — the acquisition campaign stays attached for the life of the subscription. Journey data is used as a fallback for events fired before an order exists.
journey_id and ecommerce_order_id are provided so you can stitch an order-side event back to the journey webhook that carried the same codes. Each field is omitted independently when its own condition is not met.
Compliance guidance
Required reading before forwarding click IDs to ad platforms.
Journey webhook payloads may include patient_id and patient_external_id when a linked patient exists — those fields are omitted when unavailable, never sent as null. patient_id is present once the journey is linked; patient_external_id is present only when that patient also has an external identifier. Payloads always include patient_marketing_consent. When patient_id or patient_external_id is present, the webhook contains identified patient data, whether or not attribution is present. When attribution is also present, it arrives alongside that data.
- Do not forward the webhook payload as-is to Meta Conversions API, Google Ads, or any other advertising platform.
- Extract the click ID (or other tracking token) you need and send that alone, using only data your own privacy program permits.
- Neither Google nor Meta signs a BAA covering their advertising products, including Conversions API. Anything sent to those platforms must be de-identified under your own policies.
- Attribution also travels on clinical event families — prescription, pharmacy, shipping and lab — because those events hang off an attributed order. A payload can therefore pair a campaign identifier with a clinical status such as a prescription approval. That is appropriate over your authenticated endpoint, but it makes the "extract the token, forward nothing else" rule above more important, not less.
- Telegra's role is a conduit: it returns your own tokens to you over your authenticated webhook endpoint and transmits nothing to ad platforms. Responsibility for what leaves your systems rests with you.
