Rebrandly — clicks source & the campaign_utm_ct issue

Rebrandly is the click-tracking source for creator campaigns. This page documents a parsing bug in rebrandly_campaign_utm_ct that silently dropped campaign attribution for most clicks created in 2024.
Last update: 2026-06-12

TL;DR. The campaign of a click is extracted from the link's destination URL by rebrandly_campaign_utm_ct, which only understands old-style query params (ct= / utm_campaign=). When creators moved to AppsFlyer OneLink and bare moises.app/<slashtag> deep links in 2024, the campaign moved into the path/slashtag — the parser returns NULL, and those clicks fall out of every campaign-keyed report.

Source pipeline

LayerObjectFile / owner
Ingestion (Airflow)moises_raw.rebrandly_rawDAG rebrandly-to-bq · dags/to_deprecate/rebrandly_to_bq.py (S3 click-stream → GCS → BQ)
Prep (dbt 🧱)rebrandly_prepdags/domains/marketing/models/rebrandly/rebrandly_prep.sql
Prep (dbt 🧱)rebrandly_campaign_utm_ct…/rebrandly_campaign_utm_ct.sql — parses query-param campaigns; NULL for OneLink is expected (recovered downstream by the slug→sheet join)
Prep (dbt 🧱)rebrandly_device_platform…/rebrandly_device_platform.sql
Prep (Dataform ⚙️)rebrandly_campaigns_creatorsderived/marketing/rebrandly/rebrandly_campaigns_creators.sqlx — not yet migrated (next port)
Master (dbt 🧱)rebrandly…/rebrandly.sql — dbt is the sole writer of derived_marketing_master.rebrandly (cutover complete 2026-06-12)

As of 2026-06-12 the rebrandly clicks chain is fully migrated to dbt (#647/#648), with the production cutover complete (#652, dataform #357) — dbt is the sole writer. The earlier "buggy model" framing was wrong: the campaign_utm_ct NULL is expected for OneLink links and is recovered downstream. See the funnel drop trace.

Lineage (red = model with the issue)

graph LR raw["moises_raw.rebrandly_raw
Airflow · S3 click-stream"] subgraph RB["dbt 🧱: rebrandly/ (migrated, sole writer)"] prep["rebrandly_prep"] utm["rebrandly_campaign_utm_ct
parses campaign from destination_raw; NULL for OneLink (expected)"] dev["rebrandly_device_platform"] master["rebrandly (master)"] end creators["rebrandly_campaigns_creators
⚙️ Dataform — not yet migrated"] sheet["connected_sheets.mkt_creators_campaigns
Connected Sheet"] funnel["campaigns_funnel_clicks → campaigns_funnel
⚙️ Dataform — slug→sheet fallback recovers OneLink"] raw --> prep prep --> utm prep --> dev prep --> master utm --> master dev --> master sheet --> creators master --> funnel sheet --> funnel classDef dbt fill:#ddf4e4,stroke:#2da44e,color:#000 classDef partial fill:#fff2cc,stroke:#c90,color:#000 class prep,utm,dev,master dbt class creators,funnel partial

How campaign_utm_ct is parsed

rebrandly_campaign_utm_ct.sqlx reads only destination_raw (the URL the link redirects to) and matches a fixed CASE of 4 query-param shapes. There is no ELSE, so anything else → NULL:

  1. …ct=<campaign>…
  2. …utm_campaign=<campaign>…
  3. Android old format (utm_source%3D…, pre-2023-02-06)
  4. Android new format (utm_campaign%3d…, regex)

Link patterns over time (milestones)

Era 1 — 2021 → 2023: campaign in a query param ✅ (~0.6% NULL)

PatternExample destination_rawParsed
ct=moises.ai/made-for/bassists/?ct=mar23_doni_yt&utm_source=youtube…mar23_doni_yt
utm_campaign=moises.ai/beat-friday-2023?utm_campaign=nov23_martymusic_ignov23_martymusic_ig
Android referrer=play.google.com/…?referrer=utm_source%3Djun_eloy_YTIG%26…jun_eloy_YTIG

Era 2 — 2024: AppsFlyer OneLink → campaign leaves the URL ❌ (89% NULL)

Example destination_rawParsed
moises.onelink.me/AuSS/eddiewarboyNULL
moisesai.onelink.me/lbUs/bf24kleyttonfarneyNULL
moisesai.onelink.me/lbUs/bf24kaelinellisNULL

The campaign moved into the OneLink path / slashtag. No ct=/utm_campaign= → parser returns NULL. This is the 2024 clicks crater.

Era 3 — 2025: messy split

Example destination_rawParsed
moises.ai/features/ai-studio-creators/?utm_campaign=aug25_ericassarsson_ytaug25_ericassarsson_yt
moises.onelink.me/AuSS/pw6rzgpp (opaque token, not the campaign name)NULL

Many links returned to utm_campaign= (parse OK → 2025 clicks recover), but some OneLinks now use opaque random tokens that even a slashtag fallback can't resolve.

Parse success by link-creation year

Link yearDistinct linksParsed OKNULL% NULLDominant new pattern
202135,86935,6422270.6%utm_campaign / ct
202329,13028,9371930.7%utm_campaign / ct
202421,2032,25318,95089.4%onelink.me (14,033)
202520,02419,2028224.1%back to utm_campaign (19,196)

Root cause & why it was never caught

rebrandly_campaign_utm_ct is a hard-coded CASE with no ELSE. When Marketing changed link tooling (query-param links → AppsFlyer OneLink → back to web links), nobody updated the SQL. A NULL doesn't error — it silently drops the row, so the funnel quietly under-counted for a full year. People changed the links; we never changed the query.

Fix direction (for the dbt migration)

See the recovery numbers in 2026-06-08 clicks (rebrandly) attribution issues.

Reproduce with qmb

session pi-2026-06-08-rebrandly-link-patterns