Skip to main content
TEEI in Practice
Engineering note · Research & Engineering · 17 October 2026 When the Website Is Up but the Signup Flow Is Broken

TEEI in Practice

Engineering noteResearch & Engineering

When the Website Is Up but the Signup Flow Is Broken

For six days in August 2026, every new volunteer was turned away at the LinkedIn step while the site stayed up. How we tell a quiet door from a shut one.

On this page
  1. Who the door is for
  2. Did the person get through?
  3. Who sees what, step by step
  4. Six days, drawn
  5. Why nothing paged
  6. Quiet, uninterested or shut
  7. What we built afterwards
  8. Name the programme in every query
  9. What a no looks like
  10. What PostHog sees, and what it does not
  11. Teach what you use at work
  12. About the figures
  13. Next steps
  14. Frequently asked questions

Volunteers from many countries give their time on TEEI Language, TEEI Mentorship and Skills Academy. The Educational Equality Institute (TEEI) is a nonprofit, and our programmes are free. They are for Ukrainians affected by the war, both those displaced abroad and those still living in Ukraine, and for women from Latin America and the Caribbean, including women who have migrated or been displaced.

From the evening of 12 August to the evening of 18 August 2026, every new volunteer who signed in with LinkedIn was turned away when LinkedIn sent them back to us. The website was up. Learners kept signing up, between 2 and 10 a day on TEEI Language. Between 13 and 17 August we deployed TEEI Language to production 14 times, and the check after each deploy reported OK. In those six days, not one new volunteer account was created, and no alarm went off.

The cause was a change on our side to how we check the sign-in that LinkedIn hands back. It rejected every real one. This note is not about that check. It is about how we now tell that kind of silence apart from an ordinary quiet week, using PostHog, our own database and Sentry, each for what it is good at. It follows the volunteer’s path through the programmes, shows the daily numbers, the alarm we built afterwards and the code behind it, and ends with what the same tools showed on 14 October, when no errors were logged and people still stopped.

Screens labelled with a date in August are screenshots from that day. Screens labelled “As it looks today” come from our development server. The people in them are sample profiles. The figures were read on 17 October 2026; their limits are listed at the end, with a public data file.

Built through volunteer contributions. TEEI’s software platforms are built through volunteer work, with NOK 0 spent on paid software development. Infrastructure and third-party services are separate from that figure.

Three screens of the volunteer signup in TEEI Language from August 2026. First, the volunteer page on 4 August with a button to start volunteering. Second, the LinkedIn step on 5 August with a button to continue to LinkedIn and a box to accept the terms. Third, the role chooser that a failed return from LinkedIn ended on, with no error message. Under each screen is the instrument that saw that step.
Figure 1. The volunteer door in August. PostHog saw the visit and the press on LinkedIn; only Sentry saw the failed return, and no alert fired. Source: TEEI, screenshots of TEEI Language taken on 4 and 5 August 2026. On 12 August the LinkedIn step’s heading changed from “Verify your identity” to “Connect your LinkedIn account”, the heading volunteers saw during the outage; the layout, the button and the box stayed the same.

Who the door is for

Figure 2. Heidi B., a volunteer language partner in TEEI Language, on preparing each session and meeting her learners regularly. 1 min 13 s. Source: TEEI, a film recorded with Heidi B.’s consent; this square cut rendered on 22 September 2026.

A TEEI Language volunteer gives learners regular conversation practice, often about work: meetings, presentations, interviews. A TEEI Mentorship mentor talks through a career with someone who is building one. A Skills Academy instructor teaches a workshop, a clinic or a class in their own field. Some volunteers give their time through their employer, and their hours are counted for the company in Evenhall, TEEI’s free volunteering workspace for companies.

Each of those starts with an account, and for a volunteer the account comes after LinkedIn. Six days without new volunteer accounts meant six days in which nobody new could be approved, open their calendar to learners or add hours to a company’s record.

Three screens with sample people. A volunteer's home in TEEI Language with two requests from learners waiting for an answer, a live session in the room on two phones, and Evenhall's session record with a list of verified sessions. Under each screen is the instrument that watches it.
Figure 3. What the door leads to: requests from learners, the session itself, and the hour in a company’s record. Source: TEEI Language demo platform and a live Amazon Chime SDK meeting with demo personas, 14 October 2026, and Evenhall’s public demo workspace with sample data. The demo workspace’s totals are cropped out, because they are sample figures.

Did the person get through?

Uptime answers “does the page respond?”. Error monitoring answers “did something throw?”. Neither answers the question a volunteer cares about, which is whether they got in. In August the page responded and the errors were logged. The door was still shut.

We had met this kind of failure once before. On 17 July 2026 a volunteer told us that the LinkedIn field in their application would not submit. The browser had rejected a link without “https://” and stopped the form before any of our code ran, so nothing threw, nothing was logged and nothing was counted. The volunteer waited 13 days. That case is why we built a silence alarm at the end of July: a failure that leaves no trace can only be seen as an absence.

An absence can mean three things. The door can be quiet, because few people came. People can come and decide not to go on. Or people can try and be stopped. The rest of this note is about telling those apart, with three instruments that each see part of the path.

Who sees what, step by step

Table 1. Which instrument sees each step of the volunteer door today, and what each of them saw in August 2026.
StepPostHogOur databaseSentryIn August
The volunteer pageThe visit, and where it came fromNothingNothingSeen by PostHog
The LinkedIn stepThe step opened, the button pressedAn attempt, since 20 AugustNothingSeen by PostHog alone
Back from LinkedInAn event when the sign-in is acceptedThe return, since 20 AugustAn error when the sign-in failsEvery failure logged; no rule watching
Application and accountThe form reached, the account createdThe accountThe volunteer alarm, since 18 AugustNo new accounts
First booking and sessionBookings and finished sessionsThe booking and the sessionErrors in booking and the callNo new volunteer to book
Hours for a companyNothingThe session, carried into EvenhallNothingNo new hours to count

PostHog sees the most steps, and it sees how people move: which screen they were on, how long they stayed, what they pressed. Our database sees fewer steps, but it sees every one of them for every person, and it holds the outcome that matters. Sentry sees what goes wrong loudly. In August the only step that told the truth on its own was the account, and nothing was counting accounts by door.

Six days, drawn

Five panels of daily bars for TEEI Language, 1 to 25 August 2026, with 13 to 18 August shaded. People who opened the LinkedIn step and people who pressed the LinkedIn button continue through the shaded days. People who reached the application form fall to 0 until 18 August, new volunteer accounts fall to 0 for all six days, and new learner accounts continue at 2 to 10 a day.
Figure 4. TEEI Language, per UTC day. Interest held while the outcome was zero. Source: TEEI, from PostHog (people per day on each step, production events of TEEI Language only) and from a nightly copy of our database (accounts per day), 1 to 25 August 2026, read on 17 October 2026.

Read it from the top. People kept opening the LinkedIn step, 4 to 8 different people a day, and kept pressing the button: 15 people pressed it 101 times in the six days. The third row falls to zero, because the application form only appears after a return from LinkedIn that we accept. New volunteer accounts fall to zero with it. New learner accounts, in the bottom row, carry on.

Two details in the timings matter if you read error logs. The change reached production on 12 August. The last return we accepted came at 06:24 UTC, and nobody pressed the button again until 22:55 that evening, so the first error was logged at 22:56. At the other end, the errors stopped at 13:33 UTC on 18 August, hours before the fix was live, because nobody tried again until the evening. Returns were accepted again from 18:46 UTC, and the first new volunteer account arrived the next morning. An error log starts and stops when people arrive, not when the code changes.

Why nothing paged

Our silence alarm had been watching since 4 August. It asked whether new accounts were still arriving in the programme, and they were. Learners sign up with Telegram, WhatsApp or an email address, without LinkedIn, and on every day of the outage some did.

Two phone screens of TEEI Language as it looks today. On the left, the learner signup in Ukrainian, which asks for a way to be reached and continues without LinkedIn. On the right, the volunteer signup, which asks the volunteer to connect their LinkedIn account before anything else.
Figure 5. Two doors into one programme. A learner account needs no LinkedIn; a volunteer account comes only after it. Source: TEEI Language on our development server, 17 October 2026.

The failure itself was logged. Sentry recorded an error each time a return was rejected, 121 times in all: 3 on the evening of 12 August, then 6, 10, 13, 10, 45 and 34 a day. No alert rule was watching that error until the afternoon of 18 August, when the fix had already been written.

The rule we took from it is short. A group of people who can go dark on their own has to be counted on their own. On 18 August we split the volunteer door out of the programme’s count and gave it an alarm of its own.

Quiet, uninterested or shut

The volunteer door is small. In the twelve days before the outage, TEEI Language had between 0 and 3 new volunteers a day, so a day with none is normal. A day with none tells you nothing until you know whether anyone tried.

Three cards, one for each reading of a day without a new volunteer in TEEI Language. A quiet week, 20 to 27 July 2026: 0 volunteer accounts in 171.4 hours, 0 to 2 people a day pressed the LinkedIn button, and someone got through on 23 and on 26 July. Pressed, then stopped, five days in July and August: 0 volunteer accounts each day while 6 to 13 learners signed up, 0 to 3 people pressed, and someone got through on 23 July. A shut door, 13 to 18 August 2026: 0 volunteer accounts each day while 2 to 10 learners signed up, 15 people pressed 101 times, and none got through until the fix.
Figure 6. Three readings of a zero. What separates them is not the count of what came out, but whether anyone tried and came back. Source: TEEI, from PostHog and a nightly copy of our database, TEEI Language, July and August 2026, read on 17 October 2026. Returns turned away come from Sentry.

Look at the middle card. On 5 August three people pressed the LinkedIn button and none of them reached the application form. Sentry logged no rejected return that day, and a person can stop at LinkedIn’s own page and close the tab. Pressing is not the same as coming back.

So the alarm counts attempts. This is the reasoning in our code as it stands:

Verbatim excerpt.

ts
/**
 * The conversion signals, and why they are a different shape from everything above.
 *
 * The silence floor answers "did anything arrive", which cannot separate a shut door from a
 * quiet weekend: measured over 90 days, no threshold on completions alone can. Volunteer
 * interest genuinely reaches zero on real days (2026-07-18, 07-23, 07-24 and 07-28 each had
 * zero volunteers while 6 to 14 learners signed up), so learner traffic does not discriminate
 * either. What does is an ATTEMPT: somebody committed to the door and produced nothing.
 *
 * These need no baseline guard, and that is the property worth keeping. No attempts means no
 * alarm, so a quiet weekend, a fresh staging database and a local checkout are all silent for
 * free. The silence floor needs a 30-day baseline to buy the same thing.
 */
export const FUNNEL_CONVERSION_WINDOW_MS = 24 * 60 * 60 * 1000;

Measured again for this note, the most learners on those four days was 13, not 14.

Every press of the LinkedIn button now writes a row in our database, and so does every return from LinkedIn, before anything on our side can turn the person away. The alarm watches two steps within 24 hours: three returns and no new volunteer account, or three presses and no return at all. The second would also fire on a day like 5 August if none of the three came back; we cannot tell whether they did, because the return counter came later. Three is a judgement, and the code says so:

Verbatim excerpt, with one internal reference cut, marked […].

ts
/**
 * THIS NUMBER IS NOT MEASURED, unlike every other constant in this file, and it cannot be:
 * the counter it reads […] has never existed before, so there is no history to
 * replay it against.
 *
 * 3 is chosen so a single abandoned attempt cannot page, while a wholly shut door does within
 * a day. During the 2026-08-12 outage the volunteer signup page was opened 12 to 47 times a
 * day for six days, so a total failure clears 3 on day one with room to spare.
 *
 * DEBT, stated rather than dressed up: re-measure this against real attempt data after two to
 * four weeks and replace this comment with the count it earns. Until then it is a guess, and
 * a reader deserves to know which of these constants is evidence and which is judgement.
 */
export const FUNNEL_CONVERSION_MIN_ATTEMPTS = 3;

The 12 to 47 holds up: it counts how often the LinkedIn step was opened on each of the six days, by 4 to 8 people a day. Sentry logged 3 rejected returns on the first evening alone, so the rule as it stands today would have fired within the first day of the outage.

The silence floor stays, as a backstop for a door that fails before anyone can attempt it. Its window comes from a backtest over the 90 days before it was added, 20 May to 12 August 2026, with the outage left out. The first volunteer account through this door in that window came on 6 July, so the backtest covers about five weeks of volunteers.

Grouped bars of false alarms per silence window, for TEEI Language alone and for both programmes. 24 hours: 11 and 10. 48 hours: 5 and 4. 72 hours: 1 and 1. 192 hours: 0 and 0.
Figure 7. False alarms in the backtest, per silence window. At 72 hours the programme scope no longer changes the answer. Source: TEEI, from a nightly copy of our database and PostHog, 20 May to 12 August 2026, outage excluded, measured on 17 October 2026.

At 72 hours the floor fired once, in the quiet week of 20 to 27 July, when 171.4 hours passed without a new volunteer. At 192 hours it would never have fired, but it would also have stayed silent through this outage, which was over before eight days had passed. We chose 72 hours.

Here the note’s own lesson caught us. The window was first measured across both programmes, while the alarm counts TEEI Language alone. Run again for this note with the alarm’s own scope, the false alarms at 24 and 48 hours rise by one each. At 72 hours the choice holds either way. The rule we rejected, “no volunteers while at least five learners sign up”, would have caught the outage on day one as well, but it fired on four quiet days across both programmes, five for TEEI Language alone, against the floor’s one.

What we built afterwards

Two things changed in the week after. On 20 August the attempt counter arrived: a row when someone presses the LinkedIn button, and another when LinkedIn sends them back. On 24 August a server-side record of the whole signup followed, with one row for each visit to the signup page and one for each step, from landing to approval by our team.

The split between the tools is deliberate. The alarm stands on what happened, in our own database, where every account exists whether or not the visitor accepted analytics. PostHog explains what happened before the stop: which screen, how long people stayed, whether they pressed something that did nothing.

The LinkedIn step of TEEI Language on a desktop screen, twice: on 5 August 2026, and as it looks today. The layout is the same; under the first, PostHog alone, and under the second, PostHog and an attempt row in our database.
Figure 8. The same step, then and now. In August only PostHog saw it. Today our database sees every press and every return. Source: TEEI Language, a screenshot of 5 August 2026 and our development server on 17 October 2026, both at 1280 by 720 pixels and cropped to the same area.

Name the programme in every query

TEEI Language and TEEI Mentorship run from one codebase and send their events to one PostHog project. Skills Academy runs inside TEEI Mentorship, with the same accounts and the same database. Every event carries the programme it belongs to, and Skills Academy’s pages add a marker for their own surface.

Three phone screens as they look today: the LinkedIn step in TEEI Language, the same step in TEEI Mentorship with the mentor's wording, and the registration to teach in Skills Academy. Under each, the programme PostHog records.
Figure 9. Three doors in two programmes. Each event says which door it came through. Source: TEEI Language, TEEI Mentorship and Skills Academy on our development server, 17 October 2026.

In a September audit of booking availability, a query without a programme filter counted 189 attempts. 163 were TEEI Language and 26 were TEEI Mentorship. Every success in the sample was in TEEI Language, so the blend did not show until we split it. Since then, our queries name the programme, and the ones behind this note run a second time without that clause, so the parts can be checked against the whole.

This is the query behind the PostHog rows in this note:

Adapted for publication.

sql
select toDate(timestamp) as day,
       properties.product as programme,
       multiIf(event = 'signup_viewed' and properties.step = 'linkedin_gate', 'opened',
               event = 'signup_linkedin_started', 'pressed',
               event = 'signup_viewed' and properties.step = 'apply', 'through',
               'other') as kind,
       count() as events,
       uniq(person_id) as people
from events
where event in ('signup_viewed', 'signup_linkedin_started')
  and properties.role = 'volunteer'
  and properties.environment = 'production'
  and properties.product in ('language', 'mentorship')
  and timestamp >= toDateTime('2026-08-01') and timestamp < toDateTime('2026-09-01')
group by day, programme, kind
order by day, programme, kind
limit 10000

Run it again without the line on properties.product, and check that on every day and for every kind the two programmes’ events add up to the total. People do not add up the same way, because one person can appear in both programmes. For this note we ran it a month at a time, from 1 July to 15 September 2026, and the events added up on all 77 days.

Two things about HogQL caught us while writing it. Through the API, a query without its own LIMIT returns at most 100 rows. PostHog’s query documentation says so (“By default, API queries return up to 100 rows.”), but a cut-off result looks like a complete one, and a month of days, programmes and steps is longer than that. Our first sum check failed for that reason. And step is a reserved word, so it cannot be used as a column alias.

What a no looks like

Not every stop is a fault. On 14 October we went through every signup that had started from our volunteer role board since the first one, on 21 September. That review counted 33 on the server, and 29 people once two of our own tests and two duplicates were taken out. 13 came through.

Of the 16 who stopped, the review found a screen in PostHog for 9. Eight of those nine stopped at LinkedIn. Five left the LinkedIn step without ticking the box, four of them within 1 to 4 seconds. Two went to LinkedIn and never came back, both of them mentors. One came back and went no further. The ninth left the Skills Academy screen “Who you will teach” after 3 seconds. There were no errors and no failed submissions.

This is what a no looks like to the tools: no error, no return, a short visit. They cannot tell us why these people left, and we did not ask. The alarm stays silent, as it should. What we can work on is the screen.

What PostHog sees, and what it does not

From 15 July to 15 September 2026, 660 learners signed up through TEEI Language’s signup page. Our server sent PostHog an account event for 659 of them, 99.8 percent. PostHog’s browser library left a trace for 615, 93.2 percent; for the other 45 we found no trace in the browser, and the figures do not say why. For volunteers the figures were 83 and 78 of 84. In those weeks, the browser alone would have missed about one person in fifteen.

On our website, TEEI Language, TEEI Mentorship and Skills Academy, PostHog runs only after a visitor accepts analytics. Everyone who declines is missing from PostHog by design, and is in our database like everyone else. That is the strongest reason the alarm lives in our database.

Adapted for publication.

js
// The PostHog snippet runs on every page, opted out and without storage.
posthog.init(POSTHOG_KEY, {
  api_host: POSTHOG_HOST,
  opt_out_capturing_by_default: true,   // send nothing until the visitor accepts
  opt_out_persistence_by_default: true, // and store nothing
  person_profiles: 'identified_only',
  autocapture: true,
  mask_all_text: true,                  // clicks carry no text from the page
  capture_pageview: false,              // the router sends each page view itself
  respect_dnt: true,
  disable_session_recording: true,      // switched on per page, after consent
  session_recording: {
    maskAllInputs: true,
    maskTextSelector: '*',
    blockSelector: 'img,picture,video',
  },
});

// "Accept": opt in, then load the library.
posthog.opt_in_capturing();
window.dispatchEvent(new Event('lc:posthog-ready'));

// "Decline", or a later withdrawal: stop recording, opt out,
// and remove PostHog's cookie and stored data.
posthog.stopSessionRecording();
posthog.opt_out_capturing();

Two settings live in the PostHog project rather than in the code. Since 12 September 2026 it discards IP addresses and adds no location. Recordings mask all text and everything typed, block images and video, and are never made in a video room, on the page that leads into one or on a sign-in link. The account event our server sends is covered by our interest in keeping signup working, and anyone can object to it; our privacy policy says how.

Teach what you use at work

Nothing in this note depends on one product. Count any group that can go dark on its own separately. Put the alarm on an attempt that produced nothing, not on a quiet day. Name the programme in every query, and check that the parts add up. If you work with a tool like PostHog, you already know most of this, and you can teach it.

Skills Academy is where TEEI’s volunteers teach. An instructor offers a Workshop, a Clinic, an Expert Q&A, a Webinar or a Class in their own field, TEEI staff approve each instructor, and TEEI brings the learners from its programmes. A clinic on reading a signup funnel, or a workshop on telling a quiet week from a broken step, is the kind of practical session we mean. Companies that build a tool can also offer a Workshop or a Class about their own product.

About the figures

  • PostHog counts are production events, split by programme, and are for TEEI Language unless a label says otherwise. “People” are distinct people on one day; they do not add up across days. On every day from 1 July to 15 September 2026, the events of TEEI Language plus TEEI Mentorship equal the count without a programme filter; people are not summed, since one person can appear in both. Read on 17 October 2026 at 17:38 UTC.
  • Account counts come from a nightly copy of our database taken on 17 October 2026. Test and monitoring accounts are left out. Accounts deleted since they were created are not in the copy, so some early days can be a little low.
  • The backtest covers 20 May to 12 August 2026, with the outage left out. Volunteer accounts that came through the door and have since been deleted are added back from PostHog’s record of their creation: two across both programmes, one of them in TEEI Language. Counted across both programmes, as when the alarm was built, the backtest gives the figures written down then: 10, 4, 1 and 0. The database copy alone, for TEEI Language, gives 10, 5, 2 and 0.
  • The errors are Sentry’s count of rejected returns, read on 17 October 2026. A person who tried again is counted again.
  • The 14 October review covers signups from the role board between 21 September and 14 October 2026 at 12:11 UTC. Read again for this note, the 33 rows and the stops at each screen matched. The two tests were identified on 14 October from request logs we did not read again, so the 29 people and the 9 screens rest on that review.
  • Coverage compares the accounts in our database with PostHog’s events from 15 July to 15 September 2026, matched within ten seconds.
  • The daily counts for 1 July to 15 September 2026, the backtest and the coverage figures, with their definitions, are in a public data file. It holds counts only, with no names, email addresses, account references or PostHog identifiers.
  • To check the method against your own project, run the query above with and without the programme clause.

Next steps

Frequently asked questions

What broke in August 2026?

A change on our side to how TEEI checked the sign-in that LinkedIn hands back rejected every one of them, from the evening of 12 August to the evening of 18 August 2026. New volunteers could open the LinkedIn step and press the button, but nobody got an account. Learners, who sign up without LinkedIn, were not affected.

Why did no alarm go off?

The silence alarm counted every new account in the programme, and learners kept signing up, 2 to 10 a day. The error was logged every time, but no alert rule watched that error until the afternoon of 18 August.

What would raise the alarm today?

Within 24 hours, either three returns from LinkedIn and no new volunteer account, or three presses of the LinkedIn button and no return, counted in our own database. Behind it sits a floor that fires after 72 hours without a new volunteer account.

Does PostHog decide when the alarm fires?

No. PostHog shows where people stop and how. The alarm reads our database, which sees every account whether or not a visitor accepted analytics.

Can I check the figures?

The daily counts, the backtest and the coverage figures are in a public data file, linked under About the figures.

More in Research & Engineering