User is a login: email, password, session. A Customer is a billable identity — who a subscription, a usage counter, or an override belongs to. Every entitlement or billing call names a customer, never a user.
The two are linked automatically for anyone who signs up through Kerne’s own auth: Customer.external_id is set to User.id at signup, invisibly. You never wire this up — pass the user’s id as customer on any server-side call and it resolves.
If you run your own auth (Clerk, Supabase, your own backend) instead, or you bill a company rather than a person, there may be no matching User at all, and that’s fine — a customer doesn’t require one. Pick any string as its external_id (your own user id, an org id) and pass it as customer.
Whether Kerne creates that record for you depends on which call names it first: checkout() creates one if you pass email alongside it; a granted override or kerne.customers.upsert() creates one unconditionally; consume() never does — an unknown id there is rejected, not created. A read (allows(), check()) on an unknown one never creates a row either, it just falls back to your default plan. When in doubt, call kerne.customers.upsert() first — see Customers.
Signing in
- Email and password —
kerne.auth.signup()/kerne.auth.login(). - Magic link —
kerne.auth.startPasswordless(email)emails a one-time link;kerne.auth.loginWithMagicLink(token)verifies it and logs the user in, signing them up on first click if registration allows it. Off by default per tenant.
Two different questions
Both get called “authorization”, and they are not the same thing:Next
Registration modes
Open, invite-only, or closed — and where a waitlist fits in.
Set up authentication
Login, registration and route protection, end to end.
Register & look up customers
upsert / get / list / update — for BYO auth or billing a company as one unit.
