import type { BillingAdapter, BillingUser, ApiKeyInfo, OrgMember } from "../types.js"; /** Bidirectional id map between the app's `orgId` and the WorkOS org id. */ export interface WorkOSOrgMap { /** app orgId → WorkOS org id (create/reconcile-on-read as needed). */ toWorkosOrgId(orgId: string): Promise; /** WorkOS org id → app orgId, or null if it maps to nothing. */ toOrgId(workosOrgId: string): Promise; } export interface WorkOSOrgAdapterOptions { apiKey?: string; clientId?: string; /** Key name for new keys. Default "API Key". */ keyName?: string; /** DB-mirror id map. Omit for a WorkOS-only app (identity mapping). */ map?: WorkOSOrgMap; /** Override org creation (e.g. also insert the mirror workspace row + * membership). Omit to use the default (company-domain org + verified * domain + membership). */ ensureOrg?: (user: BillingUser) => Promise<{ orgId: string; }>; /** * Optional company enrichment for an auto-created org's name and logo, from the * new user's email domain. * * **Off by default, and that default is the point.** This used to call * api.clearout.io unconditionally, which meant every deployment using this * adapter sent its customers' email domains to an unrelated third party — on the * critical path of creating a workspace, with no env var to notice it by, no way * to switch it off, and nothing in the docs saying it happened. A nicer org name * is not worth doing that silently on someone else's behalf. * * Opt in with the shipped helper, which is the same call made explicit: * * new WorkOSOrgAdapter({ enrichOrg: lookupCompany }) * * Or pass your own, resolving from records you already hold. Without it the org * is named after the domain — "acme.com" rather than "Acme". */ enrichOrg?: (domain: string) => Promise<{ name?: string; logoUrl?: string; } | null>; } export declare class WorkOSOrgAdapter implements BillingAdapter { private apiKey?; private clientId?; private map?; private ensureOrg?; private enrichOrg?; private widCache; private customerCache; constructor(opts?: WorkOSOrgAdapterOptions); private get workos(); /** app orgId → WorkOS org id (identity when no map is configured). */ private wid; /** Drop what's cached for an org (its WorkOS org id and Stripe customer * pointer) — or for every org when called with no argument. Needed only if * an org is re-pointed at a different WorkOS org or Stripe customer, which * the normal writes here already handle. */ forget(orgId?: string): void; validateApiKey(token: string): Promise<{ orgId: string; keyId?: string; } | null>; getOrgDomains(orgId: string): Promise; getBillingCustomerId(orgId: string): Promise; setBillingCustomerId(orgId: string, customerId: string): Promise; ensureOrgForUser(user: BillingUser): Promise<{ orgId: string; }>; mintApiKey(orgId: string, name: string, _createdBy?: string): Promise<{ id: string; value: string; }>; listApiKeys(orgId: string): Promise; /** Revoke by raw token value (RFC 7009): validate → key id → hard delete. */ revokeApiKeyByToken(token: string): Promise; /** Create an org with no user (auth.md anonymous). No verified domain, so it * never satisfies the internal-org unmetered check. Returns the WorkOS org id * as `orgId` (Pattern A). With a `map` configured there's no mirror row, so * prefer disabling anonymous for mirror apps (identityTypes without it). */ createAnonymousOrg(opts: { name: string; metadata?: Record; }): Promise<{ orgId: string; }>; revokeApiKey(orgId: string, id: string): Promise<{ id: string; name: string; } | null>; /** Active-member count for the org (per-seat credit grants + seat limits). * Auto-paginates so orgs with >100 members aren't undercounted. */ memberCount(orgId: string): Promise; /** Active member ids. What a read has to enumerate over once a per-member record * lives on the member rather than in one org value — see `seats.ts`. */ listMemberIds(orgId: string): Promise; /** * Members WITH their roles, in two paginated reads rather than N+1. * * `listOrganizationMemberships` carries the role and nothing else about the person; * `listUsers({ organizationId })` carries the email and the name and nothing about the * role. So both, joined on the user id — a `getUser` per member would be one HTTP request * per person on a screen that exists to show all of them. * * Every status, not just active: an invited-but-not-accepted membership is a seat already * promised, and a members list that hides them makes an owner wonder where the invitation * went. `status` is on each row so a caller can tell them apart. */ listMembers(orgId: string): Promise; /** Move a member between roles. The last-admin rule is NOT here — it is in `members.ts`, * so a consumer's own screen and a tool refuse identically. */ setMemberRole(orgId: string, userId: string, roleSlug: string): Promise; /** Drop a membership. The USER survives — they may belong to other workspaces, and * deleting a person because they left one team is not this call's business. */ removeMember(orgId: string, userId: string): Promise; /** The membership row id, which is what WorkOS's role/delete calls take. */ private membershipId; getSubscription(orgId: string): Promise<{ plan: string | null; status: string | null; subscriptionId: string | null; periodStart: string | null; periodEnd: string | null; seats: number | null; seatCounts: Record | null; }>; /** Write subscription state onto the org metadata. `plan: undefined` leaves * the plan as-is; `null` clears it (back to the default plan). */ setSubscription(orgId: string, sub: { plan?: string | null; status: string | null; subscriptionId: string | null; periodStart?: string | null; periodEnd: string | null; seats?: number | null; seatCounts?: Record | null; }): Promise; getOrgMetadata(orgId: string): Promise>; /** Merge a patch into the org metadata (null/"" deletes the key). The MERGE is * the server's own (WorkOS keeps omitted keys — measured), so the patch goes * as-is with deletions spelled as null; see setSubscription's note. */ setOrgMetadata(orgId: string, patch: Record): Promise; /** * A member's own metadata — the same 10-key/600-char budget, but per user. * * This is where a per-MEMBER record belongs. Packed into an org value instead, * a per-member map hits a ceiling of about twelve members (measured), and the * overflow fails the whole org metadata write rather than just that record. * See the note at the top of `topup.ts`. */ getUserMetadata(userId: string): Promise>; /** Merge a patch into a member's metadata (null/"" deletes the key). Read-then- * write for the same reason `setOrgMetadata` does it: the update replaces. */ setUserMetadata(userId: string, patch: Record): Promise; /** * Admin/owner check via the user's role in the org. * * The slug comes from `ADMIN_ROLE_SLUG` rather than being spelled here, because * `ensureWorkOSRoles` provisions that role and the doctor checks it — three copies * of the string is how a check comes to disagree with what it checks. If the role * does not exist in the environment, no membership can carry it, so this returns * false and `enforceAdmin` 403s every human. */ /** * Delete the WorkOS organization. * * Only ever call this through `closeWorkspace`, which stops the billing FIRST: the org holds * `stripeCustomerId`, so deleting it destroys the only mapping from a live subscription back * to anything, and the charge keeps recurring with nothing to attribute it to. */ deleteOrg(orgId: string): Promise; getOrgName(orgId: string): Promise; /** * Rename the organization. * * WorkOS's update REPLACES the object, so the name has to be sent with whatever else must * survive — the same trap `setSubscription` documents for metadata. Only `name` is passed * here, which is exactly why a Pattern B app renames through its mirror (`renameOrg` there * keeps the local row and the org in step) rather than calling this directly. */ renameOrg(orgId: string, name: string): Promise; isAdmin(orgId: string, userId: string): Promise; } //# sourceMappingURL=workos-org.d.ts.map