Reaction-Time Safe Ride Check
The Safe Ride Check is a short reaction-time test the rider takes before unlocking during a configurable late-night window. It is designed to encourage safe decisions - it is not a sobriety or DUI test, and Levy frames it carefully both in-app and in the rental agreement.
Privacy framing - important
This is a "Safe Ride Check," not a medical or legal impairment test. The in-app copy is:
"This is not a medical or legal test of impairment. We use it to encourage safe decisions. If you do not feel safe to ride, please choose another mode of transportation."
We never share results with law enforcement except by warrant. See Appeals and Disputes for the legal framing.
What the rider does
- Late-night unlock attempt (default window: 22:00 to 04:00 local).
- The app launches a 5-round tap-the-dot mini-game. Each round spawns a dot in a random screen position; the rider taps it as fast as they can.
- Each round records a reaction time in milliseconds, or a "miss" if the rider does not tap inside the 3-second timeout.
- After 5 rounds the app computes the median reaction time and miss count.
- Pass: median reaction time < 450ms, fewer than 2 misses.
- Fail: rider sees a 30-minute unlock cooldown plus an "Are you safe to ride?" prompt with an Uber-style "Call a friend" CTA.
Pass thresholds
| Setting | Default | What it does |
|---|---|---|
reaction_median_threshold_ms | 450 | Maximum median reaction time to pass |
reaction_max_misses | 1 | Maximum misses (0 or 1) to pass |
reaction_test_round_count | 5 | Number of tap-the-dot rounds |
reaction_test_timeout_ms | 3000 | Per-round timeout |
All are per-subaccount editable at Settings > Safe Ride Check.
Trigger window
| Setting | Default | What it does |
|---|---|---|
reaction_window_start_local | 22:00 | When the check starts firing |
reaction_window_end_local | 04:00 | When it stops firing (wraps midnight - handled in isWithinNightWindow) |
reaction_window_enabled | true | Set false to disable Safe Ride Check entirely |
The night window wrap (10pm to 4am crosses midnight) is handled in isWithinNightWindow() in src/lib/rider-score/reaction-test.ts and is unit-tested. You can also leave the window open all day if you really want, though we do not recommend it - the fail rate of a sleepy rider mid-afternoon is high enough to be unfair.
What happens on fail
A single fail:
- 30-minute unlock cooldown on this rider's account.
- The cooldown UI surfaces the time-remaining and a "Try again" button.
- No score penalty.
- No
score_audit_logrow, because no action was taken against the rider's tier.
Three fails in 24 hours opens a step-6 temporary lockout in the intervention ladder. This is the cross-link to the ladder - the reaction test is one of the inputs that can escalate a rider into a lockout, alongside score-based triggers.
What happens on pass
- The rider unlocks normally.
- A
reaction_testsrow is recorded withpassed=true, the median, the miss count, and the trigger. - No effect on score or pricing.
Cooldown logic
The evaluateAttempts() kernel is unit-tested. The rules are:
reaction_tests row:
median_reaction_ms - median of all rounds
miss_count - misses (timeout >= 3s)
passed - bool from pass criteria
trigger - 'nighttime' | 'appeal' | 'random'
cooldown_until - now + 30 min on fail
A rider whose cooldown_until is in the future is blocked from ride start by the pre-unlock gate. The block is reported as reason='reaction_cooldown'.
When else the test fires
- Nighttime trigger (default): every unlock attempt inside the configured night window, if the rider has not passed within the last N hours (default 6).
- Appeal trigger (optional): an operator can require a Safe Ride Check before approving a step-6 lockout appeal. This is off by default.
- Random trigger (optional): a random subset of all unlocks (any time of day) get the test. Default 0%. Use this to catch impairment outside the night window - keep it low (1-2%).
Privacy and what we do not do
- We do not log who the rider was talking to or what app they came from.
- We do not record the rider's screen, camera, or microphone.
- We do not share reaction-test results with insurers in identifiable form.
- We do not share results with law enforcement except by warrant.
- The 30-minute cooldown is a cooldown, not a penalty - it never reduces a rider's score.
Operator controls
- Settings > Safe Ride Check to enable, set window, set thresholds.
- Dashboard > Rider Score > Audit Log filtered by
action='reaction_test_fail_lockout'to see who triggered a step-6 via reaction test. - Rider profile > Reaction tests shows the last 30 days of attempts for any given rider (useful for appeal review).
ADA and accessibility note
Reaction tests can disadvantage riders with certain disabilities. If a rider self-discloses an accessibility constraint, the operator should configure an account-level exemption at Rider profile > Exemptions. The exemption skips the reaction test entirely for that rider. This is a v3.0 manual workflow; an in-app accessibility flag is on the v3.1 roadmap.
API surface
| Endpoint | Purpose |
|---|---|
POST /api/mobile/rider/reaction-test | Submit attempts and get pass/fail |
GET /api/mobile/rider/reaction-test | Is one required right now? Returns cooldown state. |
Next
See Intervention Ladder for how 3 fails in 24h become a step-6 lockout. See Appeals and Disputes if a rider believes a fail was unfair.