Skip to content
Updated Jul 18, 2026

Changelog ​

Platform releases, newest first. Each entry covers what shipped on the frontend and what changed in the backend API surface. Release notes with screenshots live on GitHub.

v0.2 (2026-07-12) ​

The employer portal is now a complete self-serve loop: sign up, pay, manage the team, file and track claims, and download documents, without leaving employer.dev.hiolly.com. The release also rolls up the week of member-journey work since v0.1: in-app GP booking, general health chat, Movement Level 2 and off-the-record chats. Everything below is wired to the live Go backend. Release notes: employer-app v0.2. The v0.2 milestone is the exhaustive list of closed story tickets (58; v0.1 holds the 70 completed before the first release).

Native in-app login ​

Email and password on an Olly page, no Keycloak redirect. The identity service exchanges credentials server-side (POST /identity/onboarding/login) and returns a token envelope; POST /identity/onboarding/refresh keeps sessions alive across reloads. Wrong password and unknown email return identical 401s so responses do not reveal which accounts exist. Disabled accounts get 403.

Signed document downloads ​

Invoice PDFs and policy schedules download from short-lived signed URLs: GET /documents/{locator}/download-url (JWT) returns a URL that is valid for 10 minutes and HMAC-signed, and the public GET /documents/{locator}/content?exp&sig endpoint verifies the signature and serves the file as an attachment. A plain browser click works with no auth header. Tampered or expired links get 401.

Document access now has a real tenancy model. A caller sees documents for their own party or organisation, and the employer of a scheme can fetch its members' policy schedules (resolved through group-scheme). A foreign document locator returns 404, indistinguishable from a document that does not exist. If the scheme resolver is down the check fails closed with 503 rather than serving the document. This closed a hole where any authenticated user could fetch any tenant's PDF by locator. See Document Service.

Claims tab and the accumulator chain ​

The portal gained a Claims tab: a scheme-wide claims table with search and status, member, and category filters, plus a Make-a-claim panel. Behind it, claim approval now applies utilisation to the member's accumulators, and the apply is idempotent per claim line (a ledger insert with a uniqueness key guards the balance update), so an approved claim moves the utilisation bars once and only once even on retries. Claims list endpoints are scoped to the caller and a foreign claim locator returns 404.

Member profiles and roster enrichment ​

The Team roster's View button opens a live member profile: identity, enrolment demographics (age, sex at birth, postcode), and the member's policy. Medical and claims data stay private to the member. GET /api/v1/schemes/{locator}/members now enriches each row with firstName, lastName, and email from policy-admin, so the roster shows names instead of raw party locators. Funnel bulk enrolment and single-member add require age, sexAtBirth, and postcode per member and reject invalid rows with 422.

Utilisation with human labels ​

GET /eligibility/schemes/{locator}/utilisation returns per-service usage against limits with the product catalogue's display labels ("GP video consultations", not gp_video), the same labels the quote page and the policy schedule use. Uncapped services report unlimited and render as "Included". The endpoint is gated on scheme ownership; a scheme the caller does not own returns 404.

Billing: saved card, real invoices ​

The Billing tab shows the Stripe payment method on file (GET /billing/payment-method), invoice history with real amounts, and per-invoice PDF downloads through the signed URL flow. A monthly premium draft invoice is raised when a policy is issued. Invoice endpoints are tenancy-scoped and a foreign invoice returns 404.

Quote funnel on the identity APIs ​

The funnel now runs on the identity service's onboarding endpoints: send-otp and verify-otp (which mints an onboarding session for pre-account scheme creation), redeem-magic-link, verify-pin, and set-password (which requires proof of email ownership via X-Onboarding-Session or a Bearer token). Companies House search in the funnel is live instead of a hardcoded list. See Identity.

Member journey: GP booking end to end ​

Triage hands off into booking without leaving the chat: a bookable disposition offers a shortlist of clinicians from live slot search, each with a profile (photo, rating, reviews) on GET /providers/{locator}. Booking creates a real episode and appointment, with a confirmation card and add-to-calendar; confirmed video appointments carry a Google Meet join link via a calendar invite. The care service gained a cancellation-reason taxonomy and a double-booking guard that returns 409 when two members race for the same slot. Red-flag cases get an emergency escalation sheet with a 999 countdown.

Member journey: health chat, Movement Level 2, incognito ​

"I have a health question" opens General Health Chat on the triage service's /health/v1/chat/completions: no pathway interrogation, a grounded conversational answer with an opening disclaimer, and markdown rendering across every chat surface. The Movement pillar gained a chat that logs activities from conversation and a live history chart, backed by a MET engine (Compendium-2011 registry, MET-minute and calorie metrics, weighted 7-day score against a 28-day baseline). An incognito toggle keeps chats out of history and trace export, with hard-purge on exit. Sign-in magic links now open the app directly via Universal/App Links.

Admin: Maat 360 hubs ​

Party-360 and Claim-360 hubs on a generic Entity360 shell: paste any locator into universal search and land on a hub with independently loading panels and honest miss states. The member timeline query dropped from 19 seconds to under half a second, and the console now sits behind a Keycloak gate (oauth2-proxy, maat-admin role).

Security fixes ​

  • document-service: GET /documents/{locator} no longer serves any document to any authenticated caller; reads are tenancy-checked (own party or organisation, or employer of the scheme) and foreign locators return 404.
  • claims and eligibility: list and read endpoints are scoped to the caller's tenant; cross-tenant reads that previously succeeded now return 404.
  • identity: login responses are uniform for wrong password and unknown email, and set-password requires proof of ownership of the target email.
  • Cluster-only /internal/* routes are blocked at the APISIX edge and the sensitive ones carry a fail-closed X-Internal-Service guard.

Known follow-ups ​

Recurring billing collection (drafts are raised but nothing finalises them yet, #1438), manual claims approval and accumulator reversal on reject (#1434), and app-level login rate limiting (#1437).

Olly Health Insurance Platform