Skip to main content

Web Funnel Reports

This folder holds the traffic funnel for jodapp.com. The funnel shows how many people arrive, how many look at a job, and how many apply.

This page is the definition. It says which events make up the funnel, which filters to apply, and which numbers you may trust. Dated pages next to this one hold each actual reading.

Why this page exists

Two people can pull "the funnel" from GA4 and get very different numbers. The reason is that the same GA4 property serves two different websites, and several event names do not mean what they sound like. This page fixes one definition so every report matches.

For the allowed values of every event parameter — enums, formats, and the rules about money — see the GA4 event parameter reference.

Vocabulary

Read this first. The rest of the page uses these words exactly.

  • A GA4 property is one analytics account. Ours is properties/515821279, named jodapp.prod.web.
  • A hostname is the domain that sent the event. One property receives events from more than one hostname.
  • An event is one action the website reports to GA4, for example view_item.
  • A key event is an event we treat as success. For us that is generate_lead.
  • Careers::Job is a normal job. The worker applies on jodapp.com.
  • Gig::Job is a shift-based job. The worker applies on gig-partners.jodapp.com.
  • activeUsers is the GA4 user count used inside funnel reports.
  • itemsViewed counts job detail views. It counts events, not people.

The two hostnames

One GA4 property receives events from two separate websites.

HostnameWhat it isCodebaseWhere a worker applies
jodapp.comMain job boardjodapp-web (React Router v7)Careers jobs apply here
gig-partners.jodapp.comGig partner siteA separate repoGig jobs apply here

Every funnel query must filter on hostName. If you do not, you mix two products and the numbers mean nothing.

The filters

Apply both of these to every step of the funnel.

FilterValueWhy
countrySingaporeSingapore is the market we sell in
hostNamejodapp.comKeeps gig-partners out

In a funnel report you cannot set one filter for the whole report. You must repeat both filters inside every step. See the query recipe below.

Two things to say out loud whenever you use these filters:

  • hostName is an exact match. It drops www.jodapp.com, jobs.jodapp.com and careers.jodapp.com. Those are worth about 1,068 sessions over 365 days. Small, but state it.
  • The Singapore filter is not most of the traffic. It removes about 46% of jodapp.com sessions and about 80% of careers applications. Always report the unfiltered country split beside the Singapore number, so nobody reads a Singapore figure as a whole-site figure.

What each event really means

Check this table before you name an event in a report. Several events do not mean what the name suggests.

EventWhat it really meansSafe to call a conversion?
session_startA visit beganNo
view_item_listA list of job cards scrolled into viewNo
select_itemSomeone clicked a job card in a listNo
view_itemA job detail page loadedNo
view_item_jodappCopy of view_item, made for Google AdsNo
generate_leadA careers application was accepted by the APIYes
form_startSomeone focused any form fieldNo
form_submitSomeone submitted any formNo

The form_submit trap

form_submit is not an application. It is fired by GA4 enhanced measurement on every form on the site. On jodapp.com over 90 days it splits like this:

  • /jobs — 972 events. This is the search and filter box.
  • / — 283 events. This is the homepage search box.
  • /login — 101 events.
  • /sign-up — 73 events.
  • /talent/setup — 69 events.

If you report form_submit as applications you overstate conversions by about 36 times (1,637 form_submit against 45 generate_lead). Do not do it.

The funnel

Use this shape. It has three steps.

StepEventWhat it tells you
1. Landedsession_startTop of funnel
2. Viewed a jobview_itemReal interest in one job
3. Appliedgenerate_leadA careers application

Why the funnel skips the job list

An earlier version put view_item_list between step 1 and step 2. That version undercounts. Many people arrive on a job detail page straight from Google and never see a list. A funnel report in GA4 is ordered, so those people are dropped at the list step and never counted as job viewers.

Measure the job list as a separate discovery funnel instead:

  • view_item_list — saw a list of jobs
  • select_item — clicked a card
  • view_item — the detail page loaded

The query recipe

Both filters must be repeated inside each step. This is the working shape.

{
"name": "2. Viewed a job",
"filter_expression": {
"and_group": {
"expressions": [
{ "funnel_event_filter": { "event_name": "view_item" } },
{ "funnel_field_filter": {
"field_name": "country",
"string_filter": { "value": "Singapore" } } },
{ "funnel_field_filter": {
"field_name": "hostName",
"string_filter": { "value": "jodapp.com" } } }
]
}
}
}

Run it with mcp__analytics__run_funnel_report against property 515821279.

Two limits of the tool:

  • The funnel is always closed. Everyone must enter at step 1.
  • funnel_next_action accepts a limit of 5 or less. Larger values fail.

Splitting gig from careers

view_item on jodapp.com covers both job types. generate_lead on jodapp.com covers careers only. If you compare them directly you understate the careers conversion rate by about three times.

Split them with the standard item dimension itemCategory. It holds Careers::Job or Gig::Job.

Item dimensions only work with item metrics. Use itemsViewed, itemsViewedInList and itemsClickedInList. Adding eventCount returns a 400 error.

Always split by itemListName too

This rule exists because we already got it wrong once.

Comparing click rates for gig against careers across the whole site showed careers at 3.09% and gig at 7.69%. That looked like a careers card quality problem. It was not. Splitting by itemListName showed:

SurfaceJob typeShownClickedRate
Search resultsGig::Job138,07011,3378.21%
Search resultsCareers::Job19,9721,5397.71%
Home — gig moduleGig::Job10,2603723.63%
Home — careers moduleCareers::Job40,2753230.80%
McDonald's pageGig::Job5,7101362.38%

On the same surface the two job types perform almost the same. The site-wide gap came from the mix: two thirds of careers card views come from the homepage module, which converts badly, while nine tenths of gig card views come from search results, which converts well.

Never quote a site-wide click rate by job type. Always split by surface first. A total that mixes surfaces with very different rates will point you at the wrong fix.

Known blind spots

State these in every report. They change what the numbers mean.

1. The "clicked Apply" step is missing

jodapp-web sends an event called job_apply_start when someone clicks Apply. It was added on 2026-01-15 and it fires on both job types. GA4 has received zero of these events across the whole life of the property. The property started collecting on 9 December 2025, so that is every day the event could possibly have been recorded.

Four other events behave the same way — login, sign_up, search and share. All five exist in the app code. None reach GA4.

The cause is confirmed. Container GTM-NKWB9VWN was inspected through the Tag Manager API on 2026-07-30. It holds only 6 tags and 3 triggers.

One generic tag, GA4 Event - Ecommerce, forwards our app events. It sends eventName: {{Event}}, so it passes through whatever name is in the data layer. It would forward job_apply_start without any change.

The blocker is the trigger that fires it. Trigger GA4 - Universal Ecommerce Items only matches this list of names:

^(view_item|view_item_list|select_item|add_to_cart|begin_checkout|purchase|generate_lead)$

The five silent events are not in that list, so the trigger never fires for them.

Note what else that list shows. add_to_cart is still allowed, but the app stopped sending it on 2026-01-15 when commit 44c99eac replaced it with job_apply_start. The app was changed and the GTM trigger was not. So the event was renamed on one side of the boundary only.

The fix is small: add the event names to that trigger's pattern. Give the four non-ecommerce events (login, sign_up, search, share) their own tag with ecommerce sending turned off.

Two things this ruled out:

  • Tags built but never published. The draft workspace is identical to the live version. Nothing is waiting unpublished.
  • Wrong GA4 property. The container picks the property from the page hostname. jodapp.com correctly maps to the production property G-LNFY0YRZD8. Other hostnames go to the QA property.

One partial exception: search is already covered in a weaker form by GA4 enhanced measurement, which sends view_search_results. That is 654 events over 90 days in Singapore. It has no search_term, so it cannot tell you what people looked for.

The effect on the funnel is large. Without job_apply_start we cannot tell these two cases apart:

  • People look at a job and never click Apply.
  • People click Apply and then give up at the login or profile step.

These need opposite fixes. One is a demand problem. The other is a signup problem.

2. Gig applications are not on this site

Roughly 70% of job detail views on jodapp.com are gig jobs. A gig worker is sent to gig-partners.jodapp.com to apply. So a funnel filtered to jodapp.com can never show a gig conversion.

Report gig and careers separately. Never divide all generate_lead events by all view_item events.

3. The apply flow has two hidden gates

In jodapp-web/app/routes/jobs/job-detail-page.jsx, a careers application passes two checks after the user clicks Apply:

  • Not logged in — the user is sent to /login.
  • Career profile not finished — the user is sent to /talent/setup.

Neither gate sends an event. People lost at these two gates are invisible.

4. The ecommerce object is never cleared — but no leak was found

The app never pushes ecommerce: null between events. GA4 merges the data layer, so in theory items from an earlier view_item_list can leak into a later event.

We checked, and the data is clean:

  • itemsViewed is 22,066 and view_item is 22,066 events. Exactly one item per event.
  • itemsClickedInList is 13,707 and select_item is 13,707 events. Exactly one item per event.

So item splits are trustworthy today. Re-check this if the app starts sending multi-item events.

5. GA4 funnels are closed and ordered

Everyone must enter at step 1 and do the steps in order. Anyone already mid- journey is dropped. Over 90 days this excluded 22.7% of session_start users, 25.7% of view_item users, 31.9% of select_item users, and 37.0% of generate_lead users.

Always publish this comparison next to the funnel. Read every funnel rate as "among people who started here in this window".

6. Measure the gig handoff with listing_job_id, not session source

The two sites are joined by the GA4 cross-domain linker, so a worker moving from jodapp.com to gig-partners.jodapp.com keeps the original session and the original source. A low count of jodapp.com as a source is what a working linker looks like. Do not read it as a tracking failure.

The utm_source=jodapp.com tag is only on login links. It does not measure job handoffs.

Instead, count gig job pages on gig-partners whose URL carries listing_job_id. jodapp.com adds that parameter to every gig apply redirect. Over 90 days, 6,069 of 6,179 gig job views on gig-partners carried it, which is 98.2%.

Rules for anyone writing a funnel report

  • Always filter by hostName. Never report a number that mixes both sites.
  • Always check an event with a pagePath query before you name it.
  • Never call form_submit a conversion.
  • Always split Careers::Job from Gig::Job before quoting a conversion rate.
  • Always split by itemListName before quoting a click rate. A site-wide rate hides which surface is failing.
  • Always publish the country split beside any Singapore number. Singapore is about 20% of careers applications, not most of them.
  • Always publish how many users the closed funnel excluded.
  • Never draw a conclusion from a segment with fewer than about 30 conversions. The whole Singapore careers funnel has 27. Device and channel cuts of it cannot carry a decision.
  • Always say whether a rate is events divided by events, or people divided by people. They are different numbers.
  • Always repeat the blind spots above.