# Scopes

> A scope keeps one customer's endpoints and events apart from everyone else's. It never grants access by itself.

Source: https://good-webhooks.dev/docs/concepts/scopes



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 [#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 [#choosing-a-scope-in-code]

Each API selects the scope in its own way:

**createWebhooks**

The root client uses application scope. `forScope()` returns the same methods bound to a named scope.

```ts
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 endpoints
```

**Management provider**

Provider methods take the scope as their first argument. Pass `null` for application scope.

```ts
await management.create(null, input)
await management.create({ type: 'organization', id: organizationId }, input)
```

**Better Auth client**

An omitted `scope` means the signed-in user. Other scopes are checked against the session. See [permissions](https://good-webhooks.dev/docs/better-auth/permissions.md).

```ts
await authClient.goodWebhooks.create({ url, eventTypes }) // the signed-in user
await authClient.goodWebhooks.create({
  scope: { type: 'organization', id: organizationId },
  url,
  eventTypes,
})
```

## Scopes don't authorize [#scopes-dont-authorize]

> **Warning.** `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 [#rules-and-limits]

* `type` is 1 to 64 characters and `id` is 1 to 200. Neither may be blank or contain null bytes.
* The same `id` under two `type` values 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 [#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](https://good-webhooks.dev/docs/guides/endpoints.md#when-a-scope-owner-leaves).
