An Android app for working teenagers in Washington State. Log the shifts you already worked,
by hand or from a photo of the posted schedule, and see which ones broke the law,
which statute they broke, and what the unpaid time is worth in dollars.
▶ Watch the full demo on YouTube (1:46)
What you are owed · the rule behind each problem · logging a shift · scanning a posted schedule
Washington limits how much a 16 or 17 year old can work during a school week.
| The rule | Where it is written |
|---|---|
| At most 20 hours in a school week | WAC 296-125-101(2)(a) |
| At most 4 hours on a school day before another school day | WAC 296-125-101(2)(a) |
| Nothing before 7am, nothing after 10pm before a school day | WAC 296-125-101(2)(a) |
| A paid 10 minute rest break, never more than 3 hours without one | WAC 296-125-121(2) |
| A 30 minute meal break, never more than 5 hours without one | WAC 296-125-121(1) |
Almost no teenager knows these rules exist, and almost none would push back if they did,
because the person breaking the rule is the person who writes the schedule.
Off the Clock closes that gap with one number: what your employer owes you.
| 1 Log | 2 Check | 3 Total |
|---|---|---|
| Type a shift in, or photograph the schedule your manager posted. Text recognition runs on the phone, the photo never leaves it, and nothing is saved until you confirm it. A schedule never shows the breaks you were actually given, so those stay yours to fill in. That gap is usually where the money is. | Every shift runs through a rules engine built from chapter 296-125 WAC as amended by WSR 26-11-048, effective July 1 2026, plus WAC 296-126-092 and RCW 49.46.130. Each problem quotes the statute it broke and links the published text. | Missed rest breaks, docked time, pay under the wage floor and unpaid overtime are summed in integer cents and shown as a single figure, with every problem listed underneath it. |
Finding out is free. Proving it costs $4.99, and a parent pays.
Logging shifts and finding violations is free and always will be. Charging a teenager to discover
they are being underpaid would be a toll on the person with the least money in the arrangement.
What costs money is the evidence record: a dated PDF listing every shift, every rule broken,
and the full statute behind each one, in the form you hand to someone else.
| The parent rail · Web Purchase Link | The in-app rail · RevenueCat SDK |
|---|---|
| The app mints its own customer ID and hands the same string to the SDK and to a RevenueCat Web Purchase Link. The teenager sends the link over whatever they already use to talk to their parent. The parent pays in a browser, on their own phone, through RevenueCat Web Billing on Stripe, and the record opens on the teenager's phone. Nobody creates an account and no card touches the teenager's device. | An ordinary one-time purchase of the first package in the current offering, for anyone who would rather just buy it. When the teenager taps A parent already paid, the app invalidates the SDK's cached customer before asking again, so a web payment unlocks on the first tap instead of the second. |
Both rails grant the same entitlement, evidence_export, and the app checks nothing else.
src/entitlement.ts | The entitlement ID, the customer ID, and the Web Purchase Link, with no native imports so it runs under test |
src/purchases.tsx | SDK setup, offerings, the purchase, and the refresh after a parent pays |
app/export.tsx | The locked record, both ways to unlock it, and saving it as a PDF |
Purchases in this build run on the RevenueCat Test Store and Stripe test mode. No real money moves. See the sandbox terms.
| App | Expo SDK 57 · React Native 0.86 on the New Architecture · TypeScript · expo-router |
| Interface | React Native Paper (Material 3), light and dark themes |
| Monetization | RevenueCat react-native-purchases · Test Store · Web Billing on Stripe |
| Schedule scan | ML Kit text recognition on the device via expo-mlkit-ocr |
| Evidence record | expo-print for the PDF · expo-sharing to send it |
| Storage | AsyncStorage on the device. No account, no server, no upload. |
The money and time math is what makes this app worth anything, so it is the part that is tested.
Fifty five tests run under plain node --test with no framework.
evaluate.test.ts | Daily and weekly hour caps, night cutoffs, break math for each age band, the wage floor, the overtime premium |
schedule.test.ts | Regrouping OCR blocks into schedule rows, real schedule time formats, and the readings that have to be refused |
record.test.ts | The printed record, including the escaping that keeps an employer name out of the markup |
palette.test.ts | Every text color in both themes clears WCAG AA against the surface it sits on |
entitlement.test.ts | The entitlement check, the customer ID, and the parent link |
Run it yourself
Requires Node and the Expo tooling. Android only.
npm install
npm test
npx expo run:android
The app builds and runs with no keys at all. Logging and violation detection work, and the record screen says purchases are switched off. To turn purchases on, copy .env.example to .env and fill in both values:
- Create a RevenueCat project.
- Under Apps and providers, add a Test Store. It needs no Play Store account and no developer account. Copy its key into
EXPO_PUBLIC_REVENUECAT_KEY. - In the Product Catalog, create an entitlement with the identifier
evidence_export. The app reads that exact string fromsrc/entitlement.tsand unlocks on nothing else. - Create a product for the record, attach it to that entitlement, and put it in an offering marked current. The app buys the first package in the current offering.
- Add a Web Purchase Link for the same offering. Copy the token out of the sandbox URL into
EXPO_PUBLIC_REVENUECAT_WEB_TOKEN, without any customer ID, because the app appends its own. Web Billing runs on Stripe, so connect a Stripe account in sandbox mode and pay with a Stripe test card.
A build carrying a Test Store key has to be a debug build. The SDK refuses to run a release build with a Test Store key.
The icon is drawn rather than stored. tools/icon.mjs writes the mark as SVG and every size is rendered from that one source:
node tools/icon.mjs full | rsvg-convert -w 1024 -h 1024 -o assets/icon.png
node tools/icon.mjs foreground | rsvg-convert -w 512 -h 512 -o assets/android-icon-foreground.png
node tools/icon.mjs mono | rsvg-convert -w 512 -h 512 -o assets/android-icon-monochrome.png
Off the Clock does not give legal advice and does not file anything on your behalf. It shows you the regulation and your own record.
What you do with that is your decision, and it is worth talking to an adult you trust or to Washington State Labor & Industries.
MIT License · see LICENSE




