Stub-text
Mätningen och BigQuery-pipelinen för Safariresor: vad som rapporteras och hur. booking_maturity.js (/workspace/group/booking_maturity.js) on-demand mognadsanalys, två tidsmått: first_touch → inquiry (consideration) och inquiry → booking (säljcykel). BQ-projekt gen-lang-client-0542783734, dataset catch_crm, EU multi-region. Tabeller assignments, inquiries, offers, bookings (med seller_id/seller_name/seller_email/channel), booking_expenses, sync_state. Views inquiry_attribution + booking_margin. Catch CRM → BQ-pipeline via /workspace/group/scripts/catch-bq-sync.js, Cloud Run Job catch-bq-sync (europe-west1, 900s timeout), Cloud Scheduler catch-bq-sync-daily 06:00 Stockholm. COGS från GET /booking/{id} expenses-array, 96% coverage på 1498 bookings, 337,8 MSEK omsättning, 246,3 MSEK COGS, 91,5 MSEK bruttovinst, 26,96% snittmarginal. Schema-evolution-läxa: ensureTable() hanterar inte kolumntillägg, kör bq update + --full backfill manuellt. setWatermark-bug: BQ Node-klienten persisterar inte TIMESTAMP @params i parameterized MERGE, inline literaler istället. Attribution-pipeline live 2026-04-23, första FTS-data den dagen, 1131 inquiries april-maj, 102 har first_touch, 60 har bokning, p50 consideration 2h, p50 säljcykel 12d. Google Ads autotagging-quirk: tracker_gclid men inte tracker_utm_source, kolla båda. Dashboards på Cloudflare Pages: safariresor-mognadskurvor.pages.dev (huvuddashboard, histogram + kumulativa kurvor + percentiler + kanalsplit), safariresor-maturity-index.pages.dev (övergiven CPL-index). Veckorapport Safariresor maturity tracker fredag 09:00 (pausad). Gamla maturity-trackers (maturity_daily.js Meta + GAds) PAUSADE. Meta:s click-attribution stabil från age=2, ratio 1.00 beror på att form-fills sker same-session. Adsets använder 7d_click + 1d_view (28d_click ej tillgängligt post-iOS 14.5). BQ Data Transfer Service Google Ads: dataset google_ads, lag 1-2d, backfill cap 300d/run, c._LATEST_DATE = c._DATA_DATE i JOIN. SA: republiken-shared för GA4/GTM/BQ, dataextractor read-only på BQ. Använd vid frågor om mognadskurvor, consideration, säljcykel, CRM-pipeline, COGS, marginalrapport, BQ, BigQuery, scheduled queries, Cloud Run Job, attribution, POAS, dashboards, mognadsdashboard, veckorapport.
Fullständigt arkiv
Mätning + BigQuery: mognadskurvor, CRM-pipeline, dashboards
Senast uppdaterad: 2026-05-03 Refresh-källa: /workspace/project/groups/safariresor/memory.md + /workspace/project/groups/safariresor/booking_maturity.js
Mätstrategi (varför vi mäter som vi gör)
- Långsiktigt mål: Standard
Lead-event som primär konvertering över alla plattformar (Meta, Google Ads, BidTheatre, Microsoft UET). Plattformsneutralt, framtidssäkert, bättre långsiktigt stöd.
- Just nu: Custom event
skicka_forfragan är primär konvertering eftersom Meta-leadkampanjer historiskt optimerat mot det. Byte till standard Lead kräver tillräckligt med historik på Lead-eventet först (annars förlorar Meta-algoritmen sin lärdom).
- Dual-tracking är medvetet övergångstillstånd: Tag 13 (skicka_forfragan, override custom event) + Tag 15 (standard Lead) i server-containern, samt Tag 9 + Tag 66 i web-containern. Båda aktiva. Risk för dubbelräkning, men så länge vi rapporterar mot CRM som sanningskälla är det ok.
- Eventmodell (full):
form_start (GA4 Enhanced Measurement) + Form Started (Tag 48): trattmätning, dual-trackas (~identiska volymer 314 vs 311 på 7d /mina-sidor/)
skicka_forfragan (custom): primär lead idag, Meta optimerar mot detta
Lead (standard): sekundär idag, framtida primär
generate_lead (Microsoft UET travel-eventtyp): UET-specifik
form_submit (GA4 auto): fångar INTE Catch-formuläret pga relativ fetch-URL
- Optimization per plattform:
- Meta: optimerar mot
skicka_forfragan (action_type offsite_conversion.fb_pixel_custom). Standard Lead (action_type offsite_conversion.fb_pixel_lead) ger andra (oftast bredare) siffror. Kontrollera ALLTID vilken action_type kampanjen optimerar mot innan rapportering.
- Google Ads: SE conv 994089998, NO conv 828096526. Conversion Linker + server-container Google Ads tags.
- BidTheatre: Conversion ID 2133 (Safariresor | Förfrågan, type: Lead).
- Microsoft UET: Tag 211072413, eventtyp
generate_lead (travel-vertikal).
- CRM som sanningskälla: BigQuery (
gen-lang-client-0542783734.catch_crm) är källan för faktisk lead-volym. Plattformssiffror tappar attribution efter session (cross-device, ATT-opt-out), CRM behåller allt. Mognadskurvor/POAS bygger på CRM, inte Meta/Ads.
- Lärdom (2026-05-01-audit): Stort gap mellan plattformar och GA4:
generate_lead = 0 i GA4 (7d), Meta rapporterar 50. CRM 17 inquiries, Meta Lead 9, Meta skicka_forfragan 13 i samma fönster. Förvänta dig att siffrorna inte matchar.
- Övergångsplanen (när byte sker): A) Verifiera Lead-event-volym >= 80% av skicka_forfragan-volym i 30+ dagar, B) Skapa parallell Meta-leadkampanj som optimerar mot Lead, jämför CPL i 14 dagar, C) Migrera huvudkampanj när Lead presterar lika eller bättre, D) Behåll skicka_forfragan som event för backward-compat-rapportering.
booking_maturity.js (mognadskurvor on-demand)
Sökväg: /workspace/group/booking_maturity.js. Hämtar rådata för mognadsanalys.
- Användning:
node booking_maturity.js --since YYYY-MM-DD --until YYYY-MM-DD --out file.json. Default: senaste 30 dagar.
- Två tidsmått:
first_touch → inquiry = considerationstid (hur länge användare funderar innan formulärinskick)
inquiry → booking = säljcykel (hur länge säljteamet jobbar innan bokning)
- Datakällor:
gen-lang-client-0542783734.catch_crm.inquiry_attribution JOIN bookings JOIN booking_margin
- GCP-projekt:
gen-lang-client-0542783734 (location EU)
- SA:
/workspace/group/.gcloud/service-account.json (republiken-shared)
- Designbeslut: ingen cron, on-demand. Rådata sparas per inquiry, bucketar/percentiler beräknas i frontend så granularitet kan ändras utan ny extraktion.
Första data 2026-04-01 till 2026-05-01 (1131 inquiries)
- 102 har first_touch (9%, pipeline live från 2026-04-23)
- 60 har bokning (5.3%)
- Konsideration: p50=2h, p90<6h (kort bild eftersom pipeline ~1v gammal)
- Säljcykel: p50=12d, 45% av bookings sker 14-30d efter inquiry, 28% inom 3-7d
- Den 32d-cykel Jonatan kände intuitivt finns mellan inquiry → booking, INTE click → inquiry
- Konsiderationskurvan stabiliseras först ~2026-05-23 (30d bakåt med pipelinen live)
Catch CRM → BigQuery-pipeline
- Script:
/workspace/group/scripts/catch-bq-sync.js
- Dataset:
catch_crm i gen-lang-client-0542783734 (EU)
- Tabeller:
assignments (17 197 rader)
inquiries (17 193)
offers (17 487)
bookings (1 503, inkl. seller_id/seller_name/seller_email/channel)
booking_expenses (5 399 rader)
sync_state (watermark per resurs)
- Views:
inquiry_attribution (joinar attributions från assignment.properties.trackers + utm)
booking_margin (omsättning, COGS, marginal per bokning)
Cloud Run + Scheduler
- Cloud Run Job:
catch-bq-sync i europe-west1, READY, 900s timeout, 2 retries, default compute SA
- Cloud Scheduler:
catch-bq-sync-daily, cron 0 5 * UTC = 06:00 Stockholm
- Default compute SA. Vid 403 på BQ: ge
roles/bigquery.dataEditor + roles/bigquery.jobUser på projektnivå.
- API-quirk: Catch response =
{ success, list, total }. Bookings-endpointen ger intermittent HTTP 500, hanteras med page-size-halvering + skip per record.
COGS + marginalview
GET /booking/{id} returnerar expenses[] array med price (COGS i lokal valuta per supplier).
- 1 498 bookings → 5 397 expense-rader, 96% coverage.
- Marginal: 337,8 MSEK omsättning, 246,3 MSEK COGS, 91,5 MSEK bruttovinst, 26,96% snittmarginal (Q1 19,0% / median 22,7% / Q3 27,0%).
- Lead-time inquiry → booking: 7+ pax har längst lead (32d avg, 18d median paid+confirmed). Mindre sällskap: 2 pax = ~153d booking→departure, 7+ pax = ~235d.
Schema-evolution-läxa
ensureTable() skapar bara ny tabell, hanterar INTE kolumntillägg. Vid nya fält:
- Kör
bq update med full schema-JSON manuellt
- Kör
--full sync för backfill
Användes 2026-04-25 när seller_id, seller_name, seller_email, channel lades till. Backfill: alla 1503 bookings körda. Datan hämtas från /booking/{id} detail endpoint, piggyback på syncBookingExpenses-loopen.
Bug-fix setWatermark (2026-05-01-läxa)
BigQuery Node-klienten persisterar INTE TIMESTAMP @params i parameterized MERGE-statements. Värdet hamnar som silent NULL i destination. Workaround: inline TIMESTAMP-literaler direkt i SQL (TIMESTAMP '2026-05-01 12:00:00'), inte via params. Vanlig INSERT/UPDATE med TIMESTAMP-params fungerar, det är specifikt MERGE som triggar buggen.
Attribution-pipelinen (live 2026-04-23)
- Första FTS-data på inquiry-nivå: 2026-04-23. Coverage rampar upp: 23 apr 21%, 24 apr 53%.
- Stefan Schönberg (inquiry 17208) har full stack: gclid, gbraid, first_touch_timestamp, landing_page, session_referrer med
gad_campaignid=14883828884. attributed_channel=Google Ads i viewen.
- Google Ads autotagging-quirk: har
tracker_gclid men INTE tracker_utm_source. Kolla BÅDA fält i view-logiken.
- Bug i assignments:
inquiry_id är tom/0 på samtliga 17 213 rader. Kosmetiskt fel, inga operativa konsekvenser.
- POAS-per-kampanj omöjligt idag. Tidslinje: 2-3v första attribuerade bokningar, 4-8v meningsfull kampanjdata, 60-90d fullt kohort-fönster.
- Nästa steg: koppla in Meta Ads + Google Ads spend-data i BQ och bygg POAS-view.
Dashboards på Cloudflare Pages
- Mognadskurvor (huvuddashboard): https://safariresor-mognadskurvor.pages.dev
- CF Pages-projekt:
safariresor-mognadskurvor
- Visar: histogram + kumulativa kurvor + percentiler + kanalsplit för consideration + sales cycle
- Bucketar beräknas i frontend, byts utan ny extraktion
- Övergiven viz (referens): https://safariresor-maturity-index.pages.dev (gamla CPL-index, behålls men uppdateras inte)
- Workshop-prepp: https://safariresor-workshop-prepp.pages.dev (senaste deploy: https://993e2f20.safariresor-workshop-prepp.pages.dev). Källfiler:
/workspace/group/workshop-kundresan/
Mognadskurvor: gamla vs nya
Tidigare (PAUSADE):
task-1773131771323-nzxm6c Meta+GAds maturity_daily.js dagligen 05:00. Pausad.
task-1773129985355-7drjin veckokoll. Pausad.
- Maturity-rapport v18 (27 apr - 1 maj): Meta SE 7 488 SEK 7 leads CPL 1 070 (+55%), DK 2 157 DKK 1 lead CPL 2 157 (+433%), NO 4 491 NOK 6 leads CPL 748 (+16%). Google Ads NO -50% CPL från dag 2 till senaste på 27-28 apr. Meta snittratio 1.00 (ingen mognad i Meta-data), Google 0.80.
Varför ratio 1.00 i Meta? Tidigare misstanke om bug var FEL. 8 API-varianter (action_report_time, attribution_windows, narrow vs wide window) returnerar identiska siffror. Meta:s siffror ÄR click-attribuerade och stabila från age=2 framåt. Skälet: form-fills sker same-session deterministiskt, sena tillskrivningar (cross-device, ATT-opt-out) försvinner ur Meta helt och hamnar som direct/organic i CRM.
Adset-attribution: alla 6 aktiva leads-adsets på Meta använder 7d_click + 1d_view på pixel 565967720230784 event skicka_forfragan. 28d_click ej tillgängligt för optimization post-iOS 14.5.
Nu: mognadskurvor flyttade till BigQuery via booking_maturity.js. CRM = sanningskällan, eftersom Meta ändå tappar attribution efter session.
GA4-verifiering (1-3 maj)
- 30 april: Konverteringar (skicka förfrågan) undersökta i GTM, form start-event verifierat. SE-formulärsubmissions går via Catch-proxyn.
- Sanity-check-rutin efter Catch-deploy: Kör vid +5 min, +30 min, +2h, +4h. Larm-trigger om 0 riktiga leads 4h efter deploy.
- 2026-04-29: #1 19:00, 19:04, 19:07 UTC + #2 21:00 UTC körda och OK.
- Whisper är default för transkribering här. Ny regel i shared-behavior.md (subagent försökte använda Gemini 2 maj, korrigerat).
- Lärdom 2026-05-01: Catch first-party-proxy + SST hidden fields är nu standard. Verifiera GA4 minst 4h efter en sådan deploy innan grönt rapporteras.
BQ Data Transfer Service (Google Ads)
- Dataset typiskt
google_ads, EU multi-region
- Standard report type + PMax tables
cost_micros / 1e6 = SEK
- Backfill cap: 300 dagar/run, kör flera sekventiellt för längre historik
- Lag: 1-2 dagar bakom live-data
- I JOIN:
c._LATEST_DATE = c._DATA_DATE för att få senaste kampanjmetadata utan dubbletter
- SA:
dataextractor@republiken-service-accounts har bara dataset-WRITER + bigquery.readonly, räcker för läs
Veckorapport Safariresor maturity tracker
Fredagsjobb 09:00 (extern scheduler-uppgift, inte i denna repo). Sammanfattar veckans CPL-utveckling per kampanj/land och postar till Discord/mail. Pausad i nuvarande form, ersatt av on-demand mognadskurvor.
Trello-uppgifter kopplat till mätning
Säljcykel + COGS-marginal-rapporter och attributions-tasks tracking i Jonatan-board, men inga öppna kort som blockerar pipelinen just nu.
[2026-04-24] Catch CRM till BQ sync komplett med COGS + marginalview
- Script:
/workspace/group/scripts/catch-bq-sync.js. Dataset gen-lang-client-0542783734.catch_crm.
- Tabeller:
assignments, inquiries, offers, bookings, booking_expenses, sync_state. Views: inquiry_attribution, booking_margin.
- COGS hittad:
GET /booking/{id} returnerar expenses[] array med price (COGS i lokal valuta per supplier). 1 498 bookings → 5 397 expense-rader, 96% coverage.
- Marginal: 337,8 MSEK omsättning, 246,3 MSEK COGS, 91,5 MSEK bruttovinst, 26,96% snittmarginal (Q1 19,0% / median 22,7% / Q3 27,0%).
- Lead-time (inquiry → booking): 7+ pax har längst lead (32 dagar avg, 18 median paid+confirmed). Mindre sällskap: 2 pax = ~153 dagar booking → departure, 7+ pax = ~235 dagar.
- Bug-fix setWatermark: BQ Node client persisterar INTE TIMESTAMP @params i parameterized MERGE (silent NULL). Fix: inline TIMESTAMP-literaler i SQL.
[2026-04-24] Attribution-pipelinen igång (aktiverad 23 apr)
- Första FTS-data på inquiry-nivå: 2026-04-23. Coverage rampar upp: 23 apr 21%, 24 apr 53%.
- Stefan Schönberg (inquiry 17208) har full stack: gclid, gbraid, first_touch_timestamp, landing_page, session_referrer med
gad_campaignid=14883828884. attributed_channel=Google Ads i viewen.
- Viktigt: Google Ads-trafik via autotagging har
tracker_gclid men INTE tracker_utm_source. Kolla båda fält.
- Bug i assignments-tabellen:
inquiry_id är tom/0 på samtliga 17 213 rader. Kosmetiskt fel, inga operativa konsekvenser.
- POAS-per-kampanj omöjligt idag. Tidslinje: 2-3v första attribuerade bokningar, 4-8v meningsfull kampanjdata, 60-90d fullt kohort-fönster.
- Nästa steg: koppla in Meta Ads + Google Ads spend-data i BQ och bygg POAS-view.