SandboxTeams

Share sandboxes, secrets and templates with your team

A team lets several people use the same sandboxes, secrets and templates. Each member has a role that decides what they can do. Create a team, invite people, then create sandboxes in it; everyone on the team can see them.

Roles

RoleCan
ownerEverything, including deleting the team and handing ownership to another member. The person who creates a team is its owner
adminInvite and remove members, change roles, rename the team, delete any team sandbox, and manage team secrets, templates and network rules
memberCreate team sandboxes, run commands and agents in them, and read secret names and templates
viewerRead team sandboxes, secret names and templates; change nothing

Each team has one owner. Invitations can grant admin, member or viewer.

Create a team

import { Sandbox } from "@tangle-network/sandbox";
 
const client = new Sandbox({
  apiKey: process.env.TANGLE_API_KEY!,
  baseUrl: "https://sandbox.tangle.tools",
});
 
const team = await client.teams.create({ name: "Acme" });
console.log(team.id, team.currentUserRole);

Request: POST /v1/teams with {"name": "Acme"}. orgId (--org-id, org_id) links the team to an organization.

Invite people

Owners and admins invite by email with a role. The invitation carries a token; the person accepts it while signed in with the invited email.

const invitation = await client.teams.invite(team.id, {
  email: "[email protected]",
  role: "member",
});
 
// Bob, signed in with [email protected]:
await bobClient.teams.acceptInvitation(invitation.token);

Accepting with a different email is refused with 403. To cancel an invitation, revoke it by id: client.teams.revokeInvitation(id), revoke_invitation(id) or tangle team revoke-invitation <id>.

Manage members

TaskTypeScriptPythonCLI
List membersteams.listMembers(teamId)teams.list_members(team_id)tangle team members
Change a roleteams.updateMember(teamId, memberId, { role })teams.update_member(team_id, member_id, role=...)tangle team update-member <member-id> --role viewer
Remove a memberteams.removeMember(teamId, memberId)teams.remove_member(team_id, member_id)tangle team remove-member <member-id>
Leave a teamteams.leave(teamId)teams.leave(team_id)tangle team leave --force
Hand over ownershipteams.transferOwnership(teamId, newOwnerCustomerId)client.fetch("POST", f"/v1/teams/{team_id}/transfer", json={"newOwnerCustomerId": ...})tangle team transfer <customer-id> --force
Renameteams.update(teamId, { name })teams.update(team_id, name=...)
Delete the teamteams.delete(teamId)teams.delete(team_id)

Only the owner can transfer ownership or delete the team. After a transfer the old owner becomes an admin. team leave and team transfer ask for confirmation; in a script, pass --force (team leave also accepts --json).

Create sandboxes the team can use

const box = await client.create({ name: "shared-box", teamId: team.id });
 
const shared = await client.list({ scope: `team:${team.id}` });
console.log(shared.map((s) => s.name));

A create with a team id checks that you are the team’s owner, an admin or a member before it starts the sandbox; a viewer cannot create one. Without a team id, the sandbox is personal.

The CLI keeps an active team per profile:

CommandEffect
tangle team switch <name-or-id>Make a team active; sandbox create then creates team sandboxes
tangle team currentShow the active team
tangle team clearGo back to personal sandboxes
tangle sandbox create --team <team>Create in this team once, whatever is active
tangle sandbox create --personalCreate a personal sandbox while a team is active

List sandboxes by scope

GET /v1/sandboxes takes a scope:

scopeLists
personalYour personal sandboxes
teamSandboxes in every team you belong to
team:<id>Sandboxes in one team
allPersonal and team sandboxes together

TypeScript passes it as client.list({ scope }); the CLI as sandbox list --team <team>, --personal or --all-scopes; Python through client.fetch.

Share secrets

Team secrets are available to every team sandbox. Owners and admins set, reveal and delete them; every member can list their names.

await client.teams.upsertSecret(team.id, "NPM_TOKEN", process.env.NPM_TOKEN!);
const names = (await client.teams.listSecrets(team.id)).map((s) => s.name);

Every reveal is written to the audit log. The Python SDK has no team secret methods; use client.fetch with the routes above. Secrets explains how secrets reach a sandbox.

Share templates

A team template is a snapshot the whole team can start sandboxes from. Owners and admins create and delete them; every member can list and use them.

const template = await client.teams.createTemplate(team.id, {
  name: "node-base",
  snapshotId: snapshot.snapshotId,
});
const templates = await client.teams.listTemplates(team.id);

Templates and images covers publishing templates.

Set network rules for the team

A team can set a default network policy for its sandboxes. Owners and admins set and clear it; this is REST only.

GET    /v1/teams/{id}/egress
PUT    /v1/teams/{id}/egress     {"mode": "strict", "allowDomains": ["registry.npmjs.org"]}
DELETE /v1/teams/{id}/egress

GET returns {"policy": null} when the team has no policy. After DELETE, team sandboxes fall back to the platform default. A sandbox’s own policy reports where it came from: sandbox, team or platform. See Network access.

Python and the team API

The Python SDK covers creating, reading, updating and deleting teams, members and invitations. For team secrets, team templates, team network rules, ownership transfer and team-scoped lists, call the REST routes on this page with client.fetch(method, path, json=..., params=...), which returns the raw response without raising.

Next