Skip to content

Organizations in functions

Read the caller's memberships with ctx.user.orgs, create documents in an organization with ctx.db, and manage organizations from a function with admin access.

Organizations are groups of users that documents can belong to. A function sees them in three places.

The caller’s memberships: ctx.user.orgs

In a realm with organizations, ctx.user.orgs is a map from organization id to what the caller is there:

const org = ctx.user?.orgs?.[orgId];
if (!org?.counts) throw ctx.error(403, "forbidden", "not a member that counts");
if (org.claims.plan === "free" && org.roles.includes("member")) { /* … */ }
FieldMeaning
namethe organization’s name
rolesthe effective roles: direct, default and through teams
teamsthe ids of the member’s teams
team_rolesthe team roles that count now, as { team, role, from?, until? }: windows that aren’t open are left out, and so are roles that require a second factor the session lacks
claimsthe organization’s claims
countswhether the membership counts for this session: false when the organization requires a second factor and the session used none
require_mfathe organization’s setting

The memberships are read once, when the call starts, and are the same data access rules use. Check counts before you trust roles.

Creating documents in an organization

ctx.db acts as the caller, so a create names the organization the way an app does, and has the same checks and rules: the caller must be a member that counts (403 not_org_member), and create rules see data._meta.org:

await ctx.db("main").collection("projects").create({ title: "Plan" }, { org: orgId });

In a collection with teams name the team too, { org: orgId, team: teamId }: the caller must belong to it or hold a team role in it (403 not_team_member).

ctx.admin.db (a function with admin: true) may use any active organization, as the admin data route does.

Managing organizations: ctx.admin.orgs

A function with admin: true, in a realm with organizations, has ctx.admin.orgs: full access, with the function as the actor in the audit trail. It is meant for flows such as “a payment arrived: create the organization and set its plan”:

const org = await ctx.admin.orgs.create({ name: input.company, owner: input.user_id });
await ctx.admin.orgs.setClaims(org.id, { plan: "pro", seats: 10 });
await ctx.admin.orgs.invitations.create(org.id, { binding: "email", email: input.admin_email, roles: ["admin"], send: true });
CallDoes
create({ name, owner, slug? })creates an organization; owner (a user id) holds the realm’s creator_roles
get(id), list({ status? })read organizations, whatever their status
setClaims(id, claims)replaces the claims (at most 1 KiB)
members(id), addMember(id, { user, roles }), setRoles(id, user, roles), removeMember(id, user)membership without an invitation; the last manager can’t be removed
assignOwner(id, user)makes the user a manager (for an orphaned organization)
teams.list(id), create, update, delete, addMember, removeMemberteams
teams.members(id, team, { role? })the team’s members and the holders of its team roles, in force now
teams.grantRole(id, team, { user, role, from?, until? }), setRoleWindow, revokeRole(id, team, { user, role })give, re-window and take away a team role (RFC 3339 times); audited with the function as the actor
invitations.create(id, input), list(id), delete(id, invitation)invitations; create answers the token and link

Errors of backd’s own (an unknown organization, a name taken, the last manager) throw a FunctionError with its status and code. Review a function with admin: true as privileged code.

Testing

@backd/functions-testing fakes both: pass user: { …, orgs } for ctx.user.orgs, and orgs: true (or orgs: { orgs: [...] } to start from some) with admin: true for ctx.admin.orgs, kept in memory and checked as backd checks it. orgChanges() lists what the function changed.

const { ctx, orgChanges } = createContext({ admin: true, orgs: true });
await handler(ctx);
assertEquals(orgChanges().map((c) => c.action), ["create", "setClaims"]);

The generated types type all of it: ctx.user.orgs and ctx.admin.orgs appear in the context of a function that has admin: true in a realm with organizations.

Edit this page · Last updated 15 Oct 2026