Check access¶
Your backend is the client. End users of your product never call e10s for entitlements.
Use a member API key with check scope (the Create key default). Same resolution, two shapes: POST check (one feature) or GET entitlements (a snapshot).
Pick how you use them:
- Call check when you need a feature
- GET entitlements at session start, or whenever you want a snapshot
- Cache
allowed/limithowever you want - Stay fresh with a TTL, webhooks, or both
Usage stays in your database. GET entitlements and POST check share 100 requests per second per organization — live check on every request is allowed; that bucket is why you might snapshot or cache.
Request shapes: Entitlements API. Credentials: Authentication.
Gate¶
Authenticate the user in your app, take that customer’s id as the subscriber id, then allow or reject (and compare usage for limits). URL-encode the subscriber id.
const KEY = process.env.E10S_MEMBER_API_KEY!
const BASE = "https://api.e10s.io/org"
async function e10s<T>(path: string, init: RequestInit = {}): Promise<T> {
const res = await fetch(`${BASE}${path}`, {
...init,
headers: {
"X-API-Key": KEY,
Accept: "application/json",
...(init.body ? { "Content-Type": "application/json" } : {}),
...init.headers,
},
})
if (!res.ok) throw new Error(`e10s ${res.status}`)
return res.json() as Promise<T>
}
type Flag = { kind: "flag"; allowed: boolean }
type Limit = { kind: "limit"; limit: number | null }
const subscriberId = encodeURIComponent(customerId)
const sso = await e10s<Flag>(
`/subscribers/${subscriberId}/entitlements/check`,
{ method: "POST", body: JSON.stringify({ feature: "sso" }) },
)
if (!sso.allowed) return res.status(403).json({ error: "sso_not_entitled" })
const seats = await e10s<Limit>(
`/subscribers/${subscriberId}/entitlements/check`,
{ method: "POST", body: JSON.stringify({ feature: "seats" }) },
)
const used = await countSeats(customerId)
if (seats.limit !== null && used >= seats.limit) {
return res.status(403).json({ error: "seat_limit_reached" })
}
limit: null means unlimited — skip the comparison.
Default GET list is entitled only. Pass ?all=true if you cache denies.
Failure handling¶
| Situation | Suggested app behavior |
|---|---|
Flag allowed: false |
403 / paywall in your API |
Limit exceeded (used >= limit) |
403 / upgrade prompt — e10s only gave you limit |
| e10s 5xx / timeout | Fail closed (deny) or degrade per your risk tolerance — be explicit |
Unknown feature key |
422 from e10s — treat as configuration error; fix catalog or caller |
| 401 from e10s | Bad/revoked key — fix credentials, don’t treat as “feature off” |
| 403 from e10s | Key is missing a scope. Don’t treat as “feature off”. |
| 429 from e10s | Back off. Do not refetch every subscriber from a webhook handler. |