Organizations
An organization is a team's shared workspace: one plan, one wallet, and one set of sandboxes, environments, saved snapshots, API keys, webhooks, GitHub connections and CI, with members who work in it. Your own account stays your personal workspace, exactly as before. Nothing changes until you create or join an organization.
$ froog org create Acme # you become its owner
$ froog org switch acme # the CLI, your account keys and MCP act in Acme from now on
$ froog org invite dana@example.com # emails a link (7 days, one use)
$ froog org members # members, caps, usage, pending invites
$ froog new web # bills Acme; every member sees it
$ froog --org personal new scratch # one command in your own account
$ froog org switch personal # back to your own account
Which workspace a request acts in. In order: an organization API key's organization. Otherwise the X-Froog-Org header (an organization's id org_…, its name in any case, or personal, --org or FROOG_ORG in the CLI, org in the SDKs). Otherwise, for an account API key, your account's active organization (froog org switch, PATCH /orgs/active). Else personal. The dashboard doesn't use the active organization: it sends the one picked in its header. A name that matches none of yours gets 404 org_not_found. Two of yours with the same name get 409 ambiguous_org (use the id). GET /account answers with your own id and email, plus org when the request acted in one. A sandbox stays in the workspace it was created in, and a fork in its source's.
| Belongs to the organization | Belongs to each person |
|---|---|
| The sandboxes it pays for, the plan, the wallet, usage and invoices, environments and variables, saved snapshots and share codes, organization API keys, webhooks, OpenTelemetry, GitHub installations and CI, AI subscriptions, alerts and data controls, the audit log | Sign-in connections, SSH keys, account API keys, the active organization, closing the account. Per member: their role, their caps, and who created each sandbox and key |
Roles
| Role | Can |
|---|---|
member | Create sandboxes and use shared ones. Read the environment, billing and usage. Save snapshots. Make organization keys (and see only their own). |
admin | Also invite and remove members, set caps, change the environment, AI proxy keys and settings, webhooks, OpenTelemetry, GitHub and CI settings and alerts, delete saved snapshots and share codes, stop and delete any sandbox, read the audit log, and see every organization key. |
owner | Also the plan, the wallet and seats, data controls, inviting admins, roles, renaming, transferring and deleting the organization. One per organization. |
A member without the role gets 403 org_role_forbidden. The owner can't leave: froog org transfer EMAIL makes another member the owner, and you become an admin. You can't close your account while you own an organization (409).
Members and invites
Owners and admins invite by email (froog org invite EMAIL, --admin invites an admin, owner only). The email holds a link to the dashboard: whoever opens it signs in with any method and joins. froog org accept TOKEN takes the token (finv_…) or the whole link. A link works once, for 7 days (404 invite_not_found, 410 invite_expired), and a new invite to the same address replaces the old one. The answer to the invite holds the link too, for when email isn't set up. An organization needs a plan before it can add members (402 billing_required). Removing a member (froog org remove EMAIL) or a member leaving (froog org leave) stops their organization keys at once. The sandboxes they created stay with the organization.
Shared and private sandboxes
Every member sees every sandbox of the organization. Each one is shared (every member can use it: exec, files, SSH, prompts, ports, start and stop) or private (only its creator uses it, members see it and its usage). New sandboxes start shared, unless the organization's default is private (froog org default-visibility private, owners and admins) or you pass froog new --visibility private (API visibility). A fork of a private sandbox is private. Its creator (or the owner) runs froog unshare NAME or froog share NAME. Lists and GET /sandboxes/{name} add created_by (the member's email), visibility and access: owner (you created it), use or view. Using a teammate's private sandbox gets 403 sandbox_private, also through its saved snapshots (starting from one, browsing its files, a share code. The owner may), and deleting someone else's shared sandbox takes an admin. SSH keys are personal: a shared sandbox lets in every member's keys, a private one only its creator's (while they are a member). froog rewrites authorized_keys on running sandboxes when this changes: share and unshare, a member joining, leaving or being removed, and a member adding or removing a key, so access ends at once rather than at the next start. What a member did inside a shared sandbox while they could (with sudo) stays: delete or rebuild sandboxes you don't trust. A member who isn't an owner or admin can't save a snapshot under a name a teammate's sandbox holds (that would replace their copy): 403 saved_snapshot_name_taken.
Organization API keys
A key made in an organization (froog --org acme api-key create ci --preset ci, or the dashboard with Acme picked) is an organization key. It always acts in that organization, as the member who made it, and never with more than their role. Naming another workspace gets 403 api_key_org_mismatch. It can't change webhooks, AI subscriptions or AI proxy keys and settings, billing or API keys, or use the routes about a person (organizations, SSH keys, sign-in, closing): 403 api_key_org_forbidden. When its member leaves, it stops (401). Members list and revoke their own organization keys. Admins see all of them, with created_by. Your account keys, made before organizations or in your personal workspace, keep meaning your account: they act in an organization only when a request names it or you made it the active one. A key limited to some sandboxes, and the key inside each sandbox, always stays in its own workspace.
Seats, limits and per-member caps
An organization pays the plan price per seat: 5 seats on the $20 plan cost $100 a month, and each seat adds the plan's included usage and saved snapshots. The seats billed are the larger of the members and the seats the owner bought (froog org seats 5, PUT /orgs/{org}/seats). More seats are charged at once, prorated. Fewer cost less from the next invoice, with no credit for the current period, and never go below the members. A member who joins past the seats bought adds a paid seat as they accept. A declined card fails the accept (402, "could not add the paid seat"). Organizations get no free trial. Each seat past the first also adds 20% of the plan's sandboxes at once and start limits, up to the next plan's sandboxes at once. On the largest plan each seat adds the full limits. These limits are one pool for the whole organization. Without a live plan, an organization has the limits of one seat.
One wallet means one member can spend it all, so owners and admins can cap each member (froog org cap EMAIL --running 3 --usage 50, 0 removes a cap, only the owner caps the owner):
| Cap | Limits | Past it |
|---|---|---|
max_running_sandboxes | The organization sandboxes the member runs at once | 429 member_limit_reached on create, fork and start |
max_usage_usd | The member's organization machine time this billing period, at list prices | 402 team_member_cap_reached |
A sandbox counts against its creator, also when a teammate starts it. Within a minute of going over (a cap lowered, or usage that passed it), froog stops the member's newest organization sandboxes. Nothing is lost. Personal sandboxes don't count.
Audit log. Each organization has its own audit log (owners and admins). Every entry names the member: actor_label is their email, with the key's label for a key. Organization changes are there too: org.create, org.invite, org.invite_revoke, org.join, org.leave, org.member_remove, org.member_role, org.member_caps, org.member_cap_stop, org.transfer, org.seats, org.rename, org.default_visibility, org.delete, sandbox.share and sandbox.unshare. Organization emails (alerts, billing) go to the owner.
Delete an organization with froog org delete: the owner, as its last member, after deleting its sandboxes. Its plan ends at once without a refund, its keys stop, and its data is erased 30 days later like a closed account's.
API: GET /orgs ({orgs: [{id, name, kind: personal|org, role, active, members}]}, personal first), POST /orgs {name}, PATCH /orgs/active {org}, GET · PATCH · DELETE /orgs/{org} ({name?, default_visibility?}), GET /orgs/{org}/members, PATCH · DELETE /orgs/{org}/members/{member} ({role?, max_running_sandboxes?, max_usage_usd?}, a member by id or email), POST /orgs/{org}/invites {email, role?}, DELETE /orgs/{org}/invites/{id}, POST /orgs/{org}/leave, POST /orgs/{org}/transfer {member}, PUT /orgs/{org}/seats {seats}, GET /org-invites/{token}, POST /org-invites/accept {token}, and POST /sandboxes/{name}/share and /unshare. Reading organizations needs account.read, changing them account.admin, and accepting an invite a dashboard session or a full-access key. These routes always act for you, whatever the workspace.