Growth report

Claude writes it from the live data for last 12 months. Takes a minute or two. Not saved automatically.

Saved reports

Committed to reporting/monthly.

Organic growth report: 2 September to 14 October 2026 reporting/monthly/2026-09-to-10.md · 20 Oct

Organic growth report: 2 September to 14 October 2026

First report with working conversion tracking. Data: GSC 2026-09-02 to 10-13 (42 days) vs prior 42 days; GA4 2026-09-03 to 10-14; PostHog 90 days. All figures from scripts/data/, regenerable with the pull scripts.

Headline

Demo bookings are now measurable and there were 17 in six weeks. Almost all came from the homepage, none from content. The content schedule did not start: nothing in the September plan shipped, and the non-brand search problem identified in August is unchanged. The measurement system works. The publishing programme has not begun.

1. Search

Current 42d Prior 42d Change
Impressions (all) 3,590 4,105 -12.5%
Clicks (all) 283 210 +34.8%
Non-brand impressions 1,046 1,057 -1.0%
Non-brand clicks 1 0 +1

Click growth is entirely brand. Non-brand search produced one click in six weeks from 1,046 impressions. This is the same finding as the August baseline and nothing has been done about it.

Position movement, non-brand

Gains:

  • recall communication dashboard tools: 12.2 to 3.2
  • recall planning and execution dashboard: 14.6 to 9.8
  • medical practice analytics: 15.7 to 13.1
  • improve patient access gp practice: new entry at 5.2
  • how to reduce missed calls at a gp practice: 7.1 to 6.2

Losses:

  • automated recall: 11.1 to 15.6. Trajectory since May: 5.6, 9.8, 11.1, 15.6. Our biggest non-brand query has fallen off page one entirely.
  • patient recall software: 9.1 to 11.3
  • real-time recall management: 16.5 to 20.1
  • practice management analytics: 6.6 to 7.8

Pages ranking 3 to 8 with zero clicks remain the single biggest unconverted opportunity on the site.

2. Traffic and conversion

GA4, 2,469 sessions, 17 key events (demo_booked).

Landing page Sessions Demos booked
/ 1,930 15
/flex 72 1
/platform 46 1
/askhattie 63 0
/blog/hippo-labs-the-nhs-app... 26 0
/blog/7-ways-to-streamline-patient-recall... 21 0

Homepage converts at roughly 0.8% of sessions to a booked demo. Blog pages produced no demos, on tiny traffic: the two best-performing posts drew 26 and 21 sessions in six weeks.

New pages /flex and /askhattie are live and already drawing traffic; /flex produced a demo from 72 sessions.

form_start appeared 9 times, a new event not present in August.

3. AI search (AEO)

Against the 2026-08-30 baseline:

  • ChatGPT: 7 views, unchanged. No movement, consistent with no AEO work having shipped.
  • Bing: 106 to 168 views.
  • Perplexity, Gemini, Claude: still zero.
  • Direct entries to deep blog URLs: 73 to 145, doubled. Shares or citations rather than search.

4. Content shipped

One blog post published on 7 September (Best Practice Birmingham stand post). Nothing else. The September schedule committed to: homepage title/meta (1 Sep), recall pillar refresh (4 Sep), flu campaign guide (11 Sep), QOF refresh (18 Sep), why-recalled pillar (25 Sep), RSV explainer (2 Oct), slot configuration guide (9 Oct). None shipped.

Pipeline is where it was on 31 August: 4 briefs, 1 draft, nothing published.

The homepage title tag is still "Hippo Labs | Proactive Care Automation for the NHS". The rewrite has been drafted since 29 August.

5. What worked, what did not, what next

Worked. Conversion tracking. Six weeks ago we could not count a single demo booking; we can now attribute 17 of them to landing pages. The Flex launch is drawing traffic and converting. Several recall-cluster positions improved without intervention, which suggests the cluster has momentum available if anything is actually published into it.

Did not work. The content programme did not start. Every piece of evidence gathered in August still points at the same two fixes, and neither has been made: rewrite the homepage title and meta (minutes of work, affects the query we rank highest for), and refresh the four pages that carry the non-brand impressions. Meanwhile "automated recall" has decayed from position 5.6 in May to 15.6 now. Decay is not neutral: the cost of doing nothing is compounding.

Next month. QOF year-end demand starts building now and peaks January to March. The QOF page already owns 30 buying-intent queries and is slipping. If one thing ships before November, it should be the QOF refresh, followed by the homepage title and the recall pillar.

Demand: what practices ask

Mined from Intercom and help centre searches by /mine-questions. Themes with no public answer are the content backlog.

2026-08-29-mining-summary research/questions/2026-08-29-mining-summary.md · 20 Oct

Corpus: 371 conversations, 332 with user content, roughly 150 genuine human questions after stripping automated digests, receipts and calendar noise. The voice is almost entirely existing-customer practice staff, so this maps Path A operational demand. Path B demand still needs the sales objection list.

Top five themes with no public answer, ranked

  1. Why has this patient been recalled? (24). Eligibility, exclusions, coding.
  2. Campaign setup per condition or indicator (18). Validates indicator pages.
  3. Slot configuration for self-booking (13). EMIS slot best practice.
  4. SNOMED and decline codes (9). Reference piece, AEO-shaped, via Theo.
  5. Patient messaging and opt-outs (7). Doubles as patient-facing reassurance.

Corroboration

Help centre views agree: the Targets Glossary is the most-read article by four times (1,083 views), and "Which indicators should I use?" appears verbatim in conversations. Indicator-level content is demanded from two independent directions.

Non-content findings routed to Tom

  • Security vendors filed four phishing false-positives against bookings.hippolabs.co.uk links in 90 days. Deliverability and trust risk: investigate booking link domain reputation before scaling SMS volume.
  • Three cancellation or unsubscribe notices and two campaign pauses for staffing sit in the corpus. CS intel, not content.
  • The second most-viewed help article is a DRAFT (Unapproved Basic Health Data, 401 views) and the same feature generates repeated error reports. Publish the article and flag the feature friction to product.
snomed-and-decline-codes research/questions/snomed-and-decline-codes.md · 20 Oct

Requests to add specific SNOMED codes to campaigns, decline codes for smears, mental health, diabetic foot checks, childhood imms; exception reporting for cervical screening.

Representative (paraphrased):

  • I need this decline code added: diabetic foot examination declined.
  • Is there no exception reporting option for cervical smears?
  • The RSV invitation SNOMED code we use is not in the list.

Content angle: SNOMED codes for recall and QOF exception reporting, a reference piece practice managers would bookmark. Correct-jargon territory where generic content farms cannot compete, and exactly the shape AI search retrieves. Clinical-adjacent: route through Theo.

slot-configuration research/questions/slot-configuration.md · 20 Oct

Patients booking into the wrong slot types, asthma plus COPD patients needing longer slots, slot types not recognised, interpreter patients needing double slots, controlling which clinicians and how many weeks show, flu slots not appearing.

Representative (paraphrased):

  • Patients with both asthma and COPD are booking single-condition slots.
  • Can patients who need an interpreter be offered double appointments?
  • How do I control how far ahead Hippo looks for appointments?

Content angle: setting up EMIS appointment slots for self-booking, a best practice guide. High operational pain, zero public coverage, and it directly reduces support load. Strong help-centre-to-blog conversion candidate.

recall-eligibility-queries research/questions/recall-eligibility-queries.md · 20 Oct

The single biggest theme. Practice staff querying why a specific patient was invited: already vaccinated, not on the disease register, pregnant and invited for a smear, recently reviewed, declined and still invited, under-age for the message, recalled twice in consecutive days. Every query is a small trust wobble in the recall logic, and several escalated to patient complaints.

Representative (paraphrased):

  • A patient had their pneumococcal vaccine years ago but has been invited again.
  • A patient was recalled for a COPD review but is not on the COPD register.
  • Can patients who declined all childhood vaccinations be excluded from recall?

Content angle: how automated recall decides who to invite. Eligibility logic, exclusions, decline codes, and the coding hygiene a practice needs for recall to be accurate. Nobody publishes this. It is also the trust content Path B needs. Most mis-recalls trace to record coding, which makes the honest angle "recall is only as good as your coding" rather than defensiveness.

patient-messaging-and-optouts research/questions/patient-messaging-and-optouts.md · 20 Oct

WhatsApp opt-out handling, patients ringing about texts they did not expect, message template management (one practice compared it unfavourably with Accurx), SMS cost questions.

Representative (paraphrased):

  • If a patient says no WhatsApp messages, how is that respected?
  • Is there a cost to the practice for SMS or is it included?
  • Can templates be managed centrally rather than per campaign?

Content angle: what patients receive and how opt-outs work. Doubles as the reassurance page practices can point patients to. The Accurx template comparison is product feedback, not content (competitor policy applies).

multi-site-and-multimorbidity research/questions/multi-site-and-multimorbidity.md · 20 Oct

Branch site selection on campaigns, multimorbid configuration requests (patients with several conditions handled in one visit), medication monitoring folded into the same recall.

Content angle: low frequency, high value. This is Path B language (scale, consistency, one visit instead of three) and the strongest efficiency story in the product. Needs register numbers before it goes public; a live case study slug already covers multimorbid appointment reduction, consent check applies.

campaign-setup-by-condition research/questions/campaign-setup-by-condition.md · 20 Oct

"How do I create a campaign?" plus a stream of condition-specific requests: RSV (including the April 2026 eligibility change), MenB, weight management, heart failure, obesity, diabetic education (DM014), thyroid monitoring, ECG for SMI, prostate awareness, GP premium / Ardens indicators.

Representative (paraphrased):

  • How do I set up an RSV campaign under the new April 2026 rules?
  • Can you build a weight management campaign with accept and decline codes?
  • I want a campaign for an Ardens-based GP premium indicator.

Content angle: first-party validation of the indicator-axis programmatic pages. One page per indicator or condition: who is eligible, what changed this year, how recall runs for it. The help centre covers generic setup; nothing public covers per-indicator guidance. Corroborated by the help centre: the Targets Glossary is the most-viewed article (1,083 views) and "Which indicators should I use?" appears verbatim in conversations.

campaign-monitoring research/questions/campaign-monitoring.md · 20 Oct

Who booked via the link, how a campaign is progressing against the eligible cohort, whether messages actually sent, when the report arrives, pausing and resuming subsets.

Representative (paraphrased):

  • Can I see all patients who booked themselves in via the link?
  • My campaign is live but does not seem to have sent anything. How do I check?
  • Can campaign progress show how much of the eligible cohort booked?

Content angle: help centre depth first (reduces support load), then a public piece on measuring recall performance. Feeds the product feedback loop too.

access-and-logins research/questions/access-and-logins.md · 20 Oct

Adding users, expired welcome links, password resets, authenticator app resistance, EMIS logins for Hippo, who has access to the practice.

Content angle: none public (support territory). Signal for the help centre and product: expired links and MFA friction are the most repeated setup pains. Self-serve user management matters if Flex self-serve ships.

Measurement notes

Tracking diagnoses and fixes handed to Atlas.

signup_submitted attribution: diagnosis and fix (2026-10-26) reporting/queries/signup-attribution-fix.md · 20 Oct

signup_submitted attribution: diagnosis and fix (2026-10-26)

Handover note for Atlas, who is building server-side tracking.

What is live now

Event Fires from Attribution in GA4
demo_booked Listener inside the Calendly embed on the Framer homepage canvas Working: Organic Search / google and bing
signup_submitted App or server side (not present in marketing site source) BROKEN: channel Unassigned, source (not set)

First signup_submitted fired 2026-10-23. It is NOT registered as a key event, so it counts as a conversion nowhere in GA4 reporting.

Why the attribution is empty

GA4 joins an event to a session using client_id plus session_id. A server-side Measurement Protocol hit that omits them (or sends a client_id GA4 has never seen) is accepted, then filed against a brand new session with no traffic source. That is the exact signature of Unassigned / (not set).

The journey also crosses hippolabs.co.uk to app.hippolabs.co.uk, so the identity has to survive that hop as well.

Fix, route A: client-side from the app (simplest)

  1. GA4 > Admin > Data streams > web stream > Configure tag settings > Configure your domains: add hippolabs.co.uk AND app.hippolabs.co.uk. GA4 then passes client_id across in the _gl linker parameter.

  2. Confirm the app carries the gtag snippet for G-QYQKWN1S33.

  3. On the signup success path (completion, not button click):

    window.gtag?.('event', 'signup_submitted', { send_to: 'G-QYQKWN1S33' });
    window.posthog?.capture('signup_submitted');
    

No cookie reading, no API secret. GA4 stitches the session itself.

Fix, route B: server side (Measurement Protocol)

Capture identity in the browser before the signup POST:

function gaIds() {
  var c = document.cookie.match(/_ga=GA\d+\.\d+\.(\d+\.\d+)/);
  var s = document.cookie.match(/_ga_QYQKWN1S33=GS\d+\.\d+\.(\d+)/);
  return { client_id: c ? c[1] : null, session_id: s ? s[1] : null };
}

Send both to the server with the signup, then:

POST https://www.google-analytics.com/mp/collect
     ?measurement_id=G-QYQKWN1S33&api_secret=<GA4 Admin > Data streams >
                                              Measurement Protocol API secrets>

{
  "client_id": "<from _ga cookie>",
  "events": [{
    "name": "signup_submitted",
    "params": {
      "session_id": "<from _ga_QYQKWN1S33 cookie>",
      "engagement_time_msec": "100"
    }
  }]
}

session_id in params is the step most often missed and is what ties the event to the originating session. Without it the hit lands detached, as now.

Required either way

  • Register the key event: GA4 > Admin > Key events > New > signup_submitted.
  • Pick ONE route. If server-side also fires demo_booked from a Calendly webhook while the Framer embed listener is live, every booking double counts.
  • CRM attribution is separate from GA4: carry utm_* plus the landing path across to app.hippolabs.co.uk and persist them on the user record (see calendly-attribution-fix.md for the same pattern on bookings).

Consent

Server-side tagging does not remove PECR consent requirements. ICO guidance applies Regulation 6 regardless of whether the tag manager runs client-side or server-side. The DUAA 2025 statistical-purposes exception (in force 2026-02-05) does not cover advertising or attribution, which is what this is for. Position needs documenting by Sachin or external advice before paid spend starts.

Verify

Re-run the event-by-channel check. signup_submitted should show a real channel and source, matching how demo_booked already reports. GA4 DebugView gives immediate feedback.

Also outstanding on the site

  • Homepage title and meta rewrite still not applied (drafted 2026-08-29).
  • Dead demo_booked snippet still in Framer Custom Code (End of body, Home). It has never fired and cannot. Safe to delete; leaving it invites someone to edit the wrong thing.
  • /askhattie has no booking widget and no page-level conversion tracking.
demo_booked event: diagnosis and fix (2026-08-29) reporting/queries/ga4-demo-booked-event.md · 20 Oct

demo_booked event: diagnosis and fix (2026-08-29)

Symptom: demo_booked never appeared in GA4 despite listener code on the site.

Root cause: the Calendly inline widget is placed via a Framer Embed component, which renders its HTML inside an iframe (srcdoc). Calendly posts calendly.event_scheduled to its parent window, which is that hidden Framer iframe, not the top page. The site-wide listener at bodyEnd sits on the top window and never receives the message. Code was correct, frame was wrong.

Fix: append a listener INSIDE the Embed component's HTML (same frame as the widget). The srcdoc iframe is same-origin (no sandbox attribute), so it calls window.top.gtag directly:

<script>
window.addEventListener('message', function (e) {
  if (e.origin === 'https://calendly.com' && e.data.event === 'calendly.event_scheduled') {
    if (window.top && typeof window.top.gtag === 'function') {
      window.top.gtag('event', 'demo_booked', {
        send_to: 'G-QYQKWN1S33',
        event_category: 'conversion',
        event_label: 'Calendly Demo Booking'
      });
    }
  }
});
</script>

GA4: Admin > Key events > New key event > demo_booked (pre-registers the name).

Verify: publish, real test booking, GA4 Realtime shows demo_booked, cancel the test booking. Until this fires, no conversion number on the site is real.

Site facts recorded while diagnosing: measurement ID G-QYQKWN1S33 (gtag in head); Calendly link calendly.com/d/cvww-sy4-6xg (Luke and Tim round robin); existing bodyEnd listener (snippet nYzzYro6g) is redundant once the embed-level listener ships.

Calendly booking attribution: the UTM pass-through fix reporting/queries/calendly-attribution-fix.md · 20 Oct

Calendly booking attribution: the UTM pass-through fix

Problem: the Calendly embed URL is static, so bookings arrive in Calendly (and any CRM webhook) with no source data. GA4 sees demo_booked (once the event fix ships) but nothing joins a CRM deal back to the page or campaign that produced it. HubSpot is retired, so this join IS the attribution loop.

Fix: build the widget URL dynamically inside the SAME Framer embed HTML box as the widget (same srcdoc frame, same-origin, so window.top works). Replace the static div with:

<div id="calendly-slot" style="min-width:320px;height:630px;"></div>
<script>
(function () {
  var base = 'https://calendly.com/d/cvww-sy4-6xg/luke-and-tim'
    + '?hide_event_type_details=1&hide_gdpr_banner=1'
    + '&embed_domain=hippolabs.co.uk&embed_type=Inline';
  try {
    var top = window.top.location;
    var params = new URLSearchParams(top.search);
    var utm = {};
    ['utm_source','utm_medium','utm_campaign','utm_term','utm_content']
      .forEach(function (k) { if (params.get(k)) utm[k] = params.get(k); });
    // no UTMs (organic): synthesise source + landing page so the CRM
    // still learns which page produced the booking
    if (!utm.utm_source) {
      utm.utm_source = document.referrer ? 'organic' : 'direct';
      utm.utm_campaign = 'site';
    }
    utm.utm_content = utm.utm_content || top.pathname;
    Object.keys(utm).forEach(function (k) {
      base += '&' + k + '=' + encodeURIComponent(utm[k]);
    });
  } catch (e) { /* attribution is best-effort, booking always works */ }
  var d = document.getElementById('calendly-slot');
  d.className = 'calendly-inline-widget';
  d.setAttribute('data-url', base);
  var s = document.createElement('script');
  s.src = 'https://assets.calendly.com/assets/external/widget.js';
  s.async = true;
  document.body.appendChild(s);
})();
</script>

Keep the demo_booked listener (ga4-demo-booked-event.md) in the same box.

Calendly stores utm_* on the invitee record (visible in the booking export and in webhook payloads under tracking). The CRM side then needs one thing: persist those fields on the lead/deal. utm_content carries the landing page path, which is the content-attribution join key for /report.

Caveat: utm_content reflects the page hosting the widget (booking page), not necessarily the blog post that referred the visitor. Good enough to split organic/direct/campaign and page. Full first-touch (blog post that started the session) comes from GA4/PostHog session data joined on time, or later by writing a first-touch cookie on landing. Start simple.

NHS news watch

Topical sources checked on the Monday /calendar run.

NHS news watch research/nhs-news-watch.md · 20 Oct

NHS news watch

Topical source for the calendar. The seasonal spine covers predictable moments; this list covers announced-not-seasonal changes, the highest-intent reactive traffic of the year (framework changes, new specs, programme changes like RSV April 2026).

Trusted sources, checked when running /calendar:

  • NHS England news and primary care bulletins
  • DHSC announcements affecting general practice
  • NICE guidance updates relevant to QOF indicators
  • CQC framework and inspection methodology changes
  • UKHSA vaccination programme changes
  • Trade press for what practices are actually talking about: Pulse, GPonline, HSJ (PCN/ICB angle), Practice Index forums

Rule: a news hook still goes through the normal pipeline. Speed never skips /review. Facts cite the primary NHS source, not the trade press summary.