Skip to content

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 / limit however 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.