Organizations
Organizations group team members under a shared billing account with centralized administration. Each member inherits the organization's tier, storage pool, and feature set.
What is an organization?
Section titled “What is an organization?”An organization is a billing and access boundary. When you create one, your personal subscription transfers to the org level. Members you invite share the org's tier — they do not need individual subscriptions.
Organizations are available on the Team ($14.99/user/month) and Enterprise (contact sales) tiers.
Creating an organization
Section titled “Creating an organization”You need a Team or Enterprise subscription before creating an org.
POST /api/v1/org{ "name": "Acme Engineering", "slug": "acme-eng"}Slugs must be lowercase alphanumeric with hyphens, at least 3 characters, and unique across all orgs. The creator automatically becomes an admin.
When the org is created, billing moves to the organization so seats, storage, and tier access are managed centrally.
Seat count is derived from the organization's subscription. Defaults: 5 seats for Team, 25 for Enterprise.
A user can only belong to one organization at a time.
Organizations have these roles:
| Role | Capabilities | |------|-------------| | owner | Everything an admin can do, plus transfer ownership of the organization. Every org has exactly one owner. | | admin | Invite/remove members, change roles, manage billing, activate a license, view all org settings | | member | Use all features included in the org tier, view org info |
Admins cannot change their own role. This prevents accidental last-admin lockout. An organization can never be left without an owner — the last owner cannot be removed or deactivated without first transferring ownership (see Ownership transfer).
Inviting members
Section titled “Inviting members”Only admins can send invites.
POST /api/v1/org/invite{ "email": "dev@company.com", "role": "member"}Invites expire after 7 days. The server checks seat capacity at invite time and again at acceptance time. If all seats are in use, the invite or acceptance is rejected with a seat_limit error.
You cannot invite someone who is already a member, and duplicate pending invites to the same email are blocked.
Starting in v0.10.22, both invite endpoints (/api/v1/org/invite and the multi-org /api/v1/orgs/{org_id}/members/invite) automatically send a notification email to the invitee through the configured email provider. Email dispatch is best-effort — the invite is created even if email delivery fails, and admins can use the resend endpoint below to retry delivery.
Listing and revoking invites
Section titled “Listing and revoking invites”GET /api/v1/org/invites # List pending invites (admin only)GET /api/v1/org/invites/me # List pending invites addressed to me (v0.10.22)DELETE /api/v1/org/invites/{id} # Revoke a pending invite (admin only)POST /api/v1/orgs/{org_id}/invites/{id}/resend # Resend the invite email (admin only, v0.10.22)The new /invites/me endpoint powers the dashboard /invites page where invitees see every org currently waiting on their decision.
Accepting or declining an invite
Section titled “Accepting or declining an invite”POST /api/v1/org/invite/{invite_id}/acceptPOST /api/v1/org/invite/{invite_id}/decline # v0.10.22 — optional body: {"decline_reason": "..."}The authenticated user's email must match the invite email. Users already in an organization cannot accept a new invite — they must leave their current org first.
Seat capacity is rechecked at acceptance time. If the org filled up between invite and acceptance, the request is rejected.
Both accept and decline are protected by atomic rowcount-1 FSM transitions, so two browser tabs can't both succeed on the same invite. Declining records declined_at and the optional decline_reason so admins can see why an invite was turned down.
Managing members
Section titled “Managing members”Admins can change roles and remove members:
PUT /api/v1/org/members/{user_id}/role # Change role (admin only)DELETE /api/v1/org/members/{user_id} # Remove member (admin only)Role must be admin or member. Admins cannot change their own role or remove themselves.
Viewing org info
Section titled “Viewing org info”GET /api/v1/orgReturns the org details, full member list, and the current user's role. Non-org users get an empty response. The response includes:
- Org name, slug, tier
- Seat usage (used vs. limit)
- Storage usage (used bytes vs. limit)
- Each member's email, display name, role, and join date
Org billing
Section titled “Org billing”The organization owns the subscription for all members. Admins manage seats, payment, invoices, upgrades, downgrades, and cancellation from the dashboard billing page.
- Admins access the subscription page to manage the org subscription.
- Members cannot purchase individual plans while in an org. Their tier is determined entirely by the org.
- If a member had a personal subscription before joining, it remains separate from the organization subscription and can be managed from the subscription page.
Storage
Section titled “Storage”| Tier | Storage | |------|---------| | Team | 1 GB per seat (e.g., 5 seats = 5 GB pool) | | Enterprise | Unlimited |
Storage is pooled across all org members. The GET /api/v1/org response includes storage_used_bytes and storage_limit_bytes so admins can monitor usage.
Dashboard
Section titled “Dashboard”Org settings are in Settings > Organization in the web dashboard. From there, admins can view members, send invites, and access subscription management. Members see the org info and their role but cannot modify settings.
Multi-org membership (v0.10.0)
Section titled “Multi-org membership (v0.10.0)”Starting in v0.10.0, a user can belong to more than one organization. The new multi-org API lives at /api/v1/orgs/... and is the canonical surface for member and project management:
GET /api/v1/orgs— list the caller's memberships with role.GET/POST/PUT/DELETE /api/v1/orgs/{org_id}/members[/...]— list, invite, promote/demote, and remove members in a specific org.GET/PUT /api/v1/orgs/{org_id}/settings— read/write the org's knowledge base creation defaults (kb_retention_days,kb_max_context_words,kb_section_page_limit). New org-scoped projects inherit these defaults at create time.GET /api/v1/auth/meincludesdefault_org_id;PUT /api/v1/auth/me/default-orgsets it (membership validated).
Removal preserves all member-authored data — sessions stay user-owned, org-scoped projects auto-transfer to the removing admin (with a ProjectTransfer audit row), KB entries keep their authorship, and pending transfers tied to the removed user's standing are cancelled. The legacy /api/v1/org/members/... routes still work and delegate to the same services as the multi-org routes, so both URL surfaces enforce the same invariants.
Project transfers (v0.10.0)
Section titled “Project transfers (v0.10.0)”Projects carry an explicit org scope (projects.org_id, nullable). Transfers move a project between scopes (personal ↔ org, or org ↔ org) through a durable state machine:
POST /api/v1/projects/{id}/transferinitiates a transfer.POST /api/v1/transfers/{xfer_id}/accept | reject | cancelmutates state. AtomicUPDATE ... WHERE state='pending'prevents double-accept.GET /api/v1/transfers?direction=&state=lists incoming and outgoing transfers.
Auto-accept fires when the initiator and target are the same user/org (e.g. moving a personal project into your own org). Standing is re-validated at accept/reject — a member demoted between initiate and accept loses the right to act on the transfer.
The CLI surface mirrors the API: sfs project transfer --to | --accept | --reject | --cancel, sfs project transfers --direction --state, and sfs config default-org [<id>|--clear]. See the CLI reference for full options.
Session-to-project routing (v0.10.0)
Section titled “Session-to-project routing (v0.10.0)”Sessions carry an explicit project_id (sessions.project_id, nullable). On every upload (POST /sessions and PUT /sessions/{id}/sync) the server resolves the linkage from the workspace's git remote inside the write transaction. Re-sync re-evaluates: pre-v0.10.0 sessions retroactively pick up a project_id when the matching project is created later; stale linkages clear when access is revoked.
Entitlements (v0.11.0)
Section titled “Entitlements (v0.11.0)”As of v0.11.0, an organization's plan tier, seat count, and storage allowance are resolved from a single authoritative entitlement record per organization. This replaced the older arrangement where plan signals were spread across user rows and license tables, which could leave enterprise admins unable to fully see or administer their own orgs.
- The entitlement has its own lifecycle (
active/canceled/expired/revoked), tracked separately from billing health (current/past_due). - Effective tier is resolved entitlement-first across the server, with a safe fall-back to the legacy columns for any record not yet migrated.
- Existing organizations were backfilled deterministically from their strongest existing signal (license, then subscription, then manual grant), so no plan changed for anyone during the upgrade.
This is an internal model change — there is nothing to configure. It is the foundation that makes self-service license activation and ownership transfer possible.
Self-service license activation
Section titled “Self-service license activation”A user who has received a license key can activate it from the dashboard, with required email verification. The flow is available in the dashboard for a user who is not yet in an organization:
- Enter the license key and confirm the previewed organization and tier.
- Submit, then enter the single-use, time-limited code emailed for verification.
- The activated user lands as the owner of the newly created organization.
Activation completes only after the emailed code is confirmed — verification is required, not optional. The activation lookup is non-oracular (it never confirms whether an arbitrary key exists) and the flow is rate-limited. Note that activation creates a new organization from the license; it does not attach a license to an existing organization. Invalid-key and bad/expired-code states are handled inline, and the exact-match-email path completes without a separate code round-trip.
This gives Enterprise customers a direct, self-service path to stand up a licensed organization without manual back-office steps.
Ownership transfer
Section titled “Ownership transfer”Every organization has exactly one owner. The owner can transfer ownership to an existing admin directly from the dashboard members surface, through a two-step flow:
- The current owner initiates a transfer to a target admin.
- The target sees an Accept / Decline banner; the initiator sees a pending banner with Cancel. Each shows the transfer's expiry.
- On accept, the target becomes the new owner.
The owner role is shown in the members list, and the owner row is protected from demotion or removal. Both parties' standing is re-validated at accept time, and a pending transfer expires if it is not accepted. Last-owner safety is enforced throughout: an org can never be left without an owner, so removing or deactivating the last owner is blocked until ownership is transferred, and admins have a force-transfer path with a full audit trail. (This is organization ownership transfer, distinct from project transfers.)