# Custom APIs — timesheet demo > A live multi-tenant timesheet API. Keys are public, rotate every two hours, and need no > signup. Every persona sees a different slice of the same data, and the interesting > outcomes are the refusals. ## Start here - https://timesheetdemo.customapis.co/keys Public. The current key set. Fetch this first. - https://timesheetdemo.customapis.co/me Who your key is, what it can reach, and what it will be REFUSED. - https://timesheetdemo.customapis.co/challenges Public. Seven reads and a seven-step write sequence — see below. - https://timesheetdemo.customapis.co/docs.md Full documentation. Add ?key= to see only your own routes. ## Auth Every API call needs `Authorization: Bearer ` — any key from /keys, no signup. Keys rotate every two hours; the previous set works for 15 minutes past a boundary. ## The challenges, and why they are two sets `reads` — seven challenges, ids challenge-1 … challenge-6 (challenge-3b sits between 3 and 4 and is never renumbered). Nothing is consumed, so run them in any order, as often as you like, alongside anyone else doing the same. `writeSequence` — seven steps, in order. Step 1 POSTs a timesheet and returns its id; every later {id} is that id. It is repeatable for exactly that reason: a timesheet approves once, so nothing here re-approves a row it did not make. Do not swap in a fixed id — that works for one run and 409s forever after. ## Read scope is not write scope The curated companies (Harbourline, Kestrel) are read-only for EVERY key. Each key's write permissions point only at the shared Scratch Sandbox (company 3). A write against curated data is a 404 by design — the refusal does not admit the row exists. ## Machine-readable - https://timesheetdemo.customapis.co/openapi.json Every route this API exposes. - https://timesheetdemo.customapis.co/openapi.json?key= Only the routes that key can call. Diff them. ## Try to break it The demo is built so the interesting result is a refusal. Fetch a colleague's timesheet, a different department's, a different company's, and one that never existed — all four return an identical 404. You are meant to attempt this; nothing here is real data.