> ## Documentation Index
> Fetch the complete documentation index at: https://docs.instapods.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Teams

> Create teams, manage members, and send invitations via the API.

Every pod belongs to a team, and every request acts on your **active team**. See
[Teams](/dashboard/teams) for what a team is and how billing is scoped to it.

Most write operations here are owner-only. There are two roles, `owner` and `member`.

## List Your Teams

```
GET /api/teams
```

**Response: `200 OK`**

```json theme={null}
[
  {
    "id": "team_abc123",
    "name": "Acme",
    "slug": "acme",
    "owner_id": "usr_xyz",
    "account_type": "standard",
    "trust_tier": "verified",
    "created_at": "2026-01-15T10:00:00Z",
    "updated_at": "2026-01-15T10:00:00Z"
  }
]
```

Every team you belong to, whether you own it or were invited to it.

## Create a Team

```
POST /api/teams
```

```json theme={null}
{ "name": "Acme" }
```

**Response: `201 Created`** - returns the team. The `slug` is derived from the name; you don't set
it. You become the owner.

## Get a Team

```
GET /api/teams/{id}
```

Returns `404` if the team doesn't exist **or** you're not a member of it - the two are deliberately
indistinguishable.

## Update a Team

```
PUT /api/teams/{id}
```

```json theme={null}
{ "name": "Acme Inc" }
```

Renaming re-derives the slug. Owner-only (`403` otherwise).

## Delete a Team

```
DELETE /api/teams/{id}
```

**Response: `200 OK`**

```json theme={null}
{ "status": "deleted" }
```

Owner-only.

<Warning>
  A team that still owns pods cannot be deleted. You get a `409` naming the count: delete or
  [transfer](/guides/transfer-a-pod) its pods first. This is a guard, not an inconvenience - pods
  orphaned from their team keep running and keep accruing charges with nothing able to bill or reach
  them.
</Warning>

## Switch Active Team

```
POST /api/teams/switch
```

```json theme={null}
{ "team_id": "team_abc123" }
```

Sets which team subsequent requests act on. Returns the updated user object. Returns `403` if you
aren't a member of that team.

<Note>
  An [API token](/api-reference/api-tokens) is bound to the team that was active when you created
  it, and switching does not move it. A token always acts on its own team, which is what makes it
  safe to leave in CI.
</Note>

## Members

### List Members

```
GET /api/teams/{id}/members
```

```json theme={null}
[
  {
    "id": "tm_abc",
    "team_id": "team_abc123",
    "user_id": "usr_xyz",
    "role": "owner",
    "joined_at": "2026-01-15T10:00:00Z"
  }
]
```

### List Members With Names

```
GET /api/teams/{id}/members/details
```

The same list with each member's `name` and `email` resolved, which is what the team settings screen
renders. Use this when you want to show who someone is rather than just their user ID.

```json theme={null}
[
  {
    "id": "tm_abc",
    "team_id": "team_abc123",
    "user_id": "usr_xyz",
    "role": "owner",
    "name": "Ada Lovelace",
    "email": "ada@example.com",
    "joined_at": "2026-01-15T10:00:00Z"
  }
]
```

### Add a Member

```
POST /api/teams/{id}/members
```

```json theme={null}
{
  "email": "ada@example.com",
  "role": "member"
}
```

Adds someone who **already has an InstaPods account** directly, with no invitation step. Use
[invitations](#invitations) for anyone else.

**Response: `201 Created`** - returns the membership. Owner-only.

| Code  | Reason                                                            |
| ----- | ----------------------------------------------------------------- |
| `400` | `email` missing                                                   |
| `404` | No InstaPods account with that email - send an invitation instead |
| `409` | Already a member of this team                                     |

### Remove a Member

```
DELETE /api/teams/{id}/members/{uid}
```

**Response: `200 OK`**

```json theme={null}
{ "status": "removed" }
```

The owner can remove anyone; anyone can remove themselves (that's how you leave a team). The team
owner cannot be removed - transfer ownership or delete the team instead.

## Invitations

For inviting someone who doesn't have an account yet. An invitation is a signed link, valid for
**7 days**.

### Send an Invitation

```
POST /api/teams/{id}/invitations
```

```json theme={null}
{
  "email": "grace@example.com",
  "role": "member"
}
```

**Response: `201 Created`**

```json theme={null}
{
  "id": "inv_abc123",
  "team_id": "team_abc123",
  "email": "grace@example.com",
  "role": "member",
  "token": "8f3c...",
  "invited_by": "usr_xyz",
  "expires_at": "2026-02-27T10:00:00Z",
  "created_at": "2026-02-20T10:00:00Z"
}
```

Owner-only. Returns `409` if that email already belongs to a member of this team.

### List Pending Invitations

```
GET /api/teams/{id}/invitations
```

Owner-only.

### Revoke an Invitation

```
DELETE /api/teams/{id}/invitations/{invId}
```

Owner-only. Returns `{"status": "deleted"}`.

### Look Up an Invitation

```
GET /api/invitations/{token}
```

Public - this is what the invitation landing page calls before the recipient has an account. It
returns only what's needed to render "you've been invited", never the team's contents:

```json theme={null}
{
  "team_name": "Acme",
  "inviter_name": "Ada Lovelace",
  "email": "grace@example.com",
  "role": "member",
  "expires_at": "2026-02-27T10:00:00Z"
}
```

Returns `404` for a token that is unknown or expired.

### Accept an Invitation

```
POST /api/invitations/accept
```

Works two ways, depending on whether the caller is signed in.

**Already signed in** - send just the token:

```json theme={null}
{ "token": "8f3c..." }
```

**No account yet** - sign up and join in one call:

```json theme={null}
{
  "token": "8f3c...",
  "email": "grace@example.com",
  "name": "Grace Hopper",
  "password": "a-strong-password"
}
```

This registers the account, logs it in (setting the session cookie) and joins the team. Omitting any
of `email`, `name` or `password` while signed out returns `401`.

**Response: `200 OK`**

```json theme={null}
{
  "status": "joined",
  "team_id": "team_abc123"
}
```

`status` is `already_member` if you were already on the team; either way the invitation is consumed
and the team becomes your active one.
