Scopes
A scope keeps one customer's endpoints and events apart from everyone else's. It never grants access by itself.
Every endpoint and every event belongs to exactly one scope. An event only reaches endpoints in its own scope. That is how one installation serves many customers without one customer's events reaching another's endpoints.
| Scope | Value | Typical owner |
|---|---|---|
| Application scope | null | Your platform itself |
| Named scope | { type: 'user', id } | One signed-in user |
| Named scope | { type: 'organization', id } | One organization or team |
| Named scope | { type: 'project', id } | Any grouping your domain needs |
Who receives what
An event published in one scope reaches endpoints in that scope only. There is no inheritance and no fallback:
| Event published in ↓ · Endpoint in → | application | user u_1 | organization o_1 |
|---|---|---|---|
| application | ✓ | ||
user u_1 | ✓ | ||
organization o_1 | ✓ |
Application scope is not a parent of the named scopes. To reach a customer's endpoints, publish in that customer's exact scope.
Choosing a scope in code
Each API selects the scope in its own way:
The root client uses application scope. forScope() returns the same methods bound to a named scope.
import { webhooks } from './webhooks.js'
await webhooks.publish({ type: 'invoice.paid', data }) // application scope
const customer = webhooks.forScope({ type: 'organization', id: organizationId })
await customer.endpoints.create({ url, eventTypes: ['invoice.paid'] })
await customer.publish({ type: 'invoice.paid', data }) // reaches that organization's endpointsScopes don't authorize
forScope() and the provider methods trust the scope you pass. Check that the caller may act for
that customer before choosing the scope. Never pass an unchecked ID from a request body.
The Better Auth routes are the exception: they check the session, organization membership, and permissions on every request. The trusted provider from createBetterAuthManagement() skips those checks, which is what a worker needs.
Rules and limits
typeis 1 to 64 characters andidis 1 to 200. Neither may be blank or contain null bytes.- The same
idunder twotypevalues is two different scopes. - A scope holds at most 1,000 live endpoints.
- Idempotency keys are unique per scope, not globally.
When an owner goes away
The Better Auth plugin retires a user's endpoints when the user is deleted, and an organization's endpoints when the organization is deleted. With standalone management, call removeScope(scope) on a createPostgresManagement() provider from your own deletion workflow. See manage endpoints.