Authentication answers "who are you?" Authorization answers the harder question: "are you allowed to do this?" By the time a request reaches this stage, we've already validated the token and confirmed the tenant. Now we need to decide — before the request touches any upstream service — whether this specific identity has permission to call this specific endpoint. That decision runs hundreds of millions of times a day. It needs to be fast, correct, and cheap to reason about when something goes wrong. This post is about the model we use, the simpler approach that served us for a year, and the optimization we eventually built — and why we kept the old path around anyway. The model: three layers, one question Our authorization model has three layers: A role is what a user is granted — something like clinic_admin or billing_specialist . An access level is a coarse permission — something like user:admin or schedule:write . An endpoint declares which access levels are sufficient to reach it.…