---
title: "Members"
description: "List members, update roles, transfer ownership, and leave an organization."
---

Manage users within your organization. The endpoints on this page scope
to the org bound to your credential — you don't need to pass an org id in
the path.

## Endpoints

| Method | Path | Required role | Description |
|--------|------|---------------|-------------|
| `GET` | `/v1/members` | any member | List members |
| `DELETE` | `/v1/members/{member_id}` | `admin+` | Remove a member |
| `PATCH` | `/v1/members/{member_id}/role` | `owner` | Update a member's role |
| `POST` | `/v1/members/{member_id}/transfer-ownership` | `owner` | Transfer ownership |
| `POST` | `/v1/leave` | any member | Leave the organization |

<Warning>
  Only listing members works with an `sk_live_` API key. Removing a
  member, changing a role, transferring ownership, and leaving need a
  signed-in session (`Authorization: Token <session-token>` plus
  `X-ThunderPhone-Org`) and return `403` to an API key, so a leaked key
  can't take over the organization. See
  [What API keys can't do](/api-reference/introduction#what-api-keys-cant-do).
</Warning>

## Member object

```json
{
  "id": 1,
  "email": "jane@example.com",
  "first_name": "Jane",
  "last_name": "Doe",
  "role": "admin",
  "has_passkey": true,
  "created_at": "2026-04-20T18:24:10.113Z"
}
```

| Field | Type | Description |
|-------|------|-------------|
| `id` | integer | Membership id (not user id) |
| `email` | string | Member's email |
| `first_name`, `last_name` | string | Member's display name |
| `role` | string | `owner`, `admin`, or `member` |
| `has_passkey` | boolean | Whether the member has a passkey registered on their account |
| `created_at` | timestamp | ISO 8601 UTC |

### Role hierarchy

| Role | Can invite | Can remove members | Can change roles | Can transfer ownership |
|------|-----------|--------------------|--------------------|-----------------------|
| `owner` | ✓ | ✓ (including admins) | ✓ | ✓ |
| `admin` | ✓ | ✓ (members only, not admins) | — | — |
| `member` | — | — | — | — |

Each org has exactly one `owner`.

---

## List members

<CodeGroup>
```bash cURL
curl https://api.thunderphone.com/v1/members \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY"
```

```python Python
members = requests.get(
    "https://api.thunderphone.com/v1/members",
    headers={"Authorization": "Bearer sk_live_YOUR_API_KEY"},
).json()
```

```javascript Node.js
const members = await fetch("https://api.thunderphone.com/v1/members", {
  headers: { Authorization: `Bearer ${process.env.THUNDERPHONE_API_KEY}` },
}).then((r) => r.json());
```
</CodeGroup>

Returns a JSON array of [Member objects](#member-object), sorted by `created_at` ascending.

---

## Remove a member

<CodeGroup>
```bash cURL
curl -X DELETE https://api.thunderphone.com/v1/members/5 \
  -H "Authorization: Token YOUR_SESSION_TOKEN" \
  -H "X-ThunderPhone-Org: 42"
```

```python Python
requests.delete(
    f"https://api.thunderphone.com/v1/members/{member_id}",
    headers={"Authorization": "Token YOUR_SESSION_TOKEN", "X-ThunderPhone-Org": "42"},
)
```
</CodeGroup>

Returns `204 No Content`.

| Status | Condition |
|--------|-----------|
| `204` | Removed |
| `400` | Caller tried to remove themself — use [`POST /v1/leave`](#leave-organization) instead |
| `403` | Caller is not admin/owner; caller is admin trying to remove another admin; target is the owner; or the request used an API key |
| `404` | No such member in this org |

<Note>
  You cannot remove yourself with this endpoint — use
  [`POST /v1/leave`](#leave-organization) instead.
</Note>

---

## Update member role

Only the owner can change roles. Supported target roles are `admin` and
`member`. To promote someone to owner, use
[Transfer ownership](#transfer-ownership).

<CodeGroup>
```bash cURL
curl -X PATCH https://api.thunderphone.com/v1/members/5/role \
  -H "Authorization: Token YOUR_SESSION_TOKEN" \
  -H "X-ThunderPhone-Org: 42" \
  -H "Content-Type: application/json" \
  -d '{"role": "admin"}'
```

```python Python
requests.patch(
    f"https://api.thunderphone.com/v1/members/{member_id}/role",
    headers={"Authorization": "Token YOUR_SESSION_TOKEN", "X-ThunderPhone-Org": "42"},
    json={"role": "admin"},
)
```
</CodeGroup>

| Field | Type | Required | Allowed values |
|-------|------|----------|----------------|
| `role` | string | yes | `admin`, `member` |

Returns `200 OK` with the updated [Member object](#member-object).

---

## Transfer ownership

Promotes another member to `owner` and demotes the current owner to
`admin`. Atomic — if any step fails, no change is applied.

<CodeGroup>
```bash cURL
curl -X POST https://api.thunderphone.com/v1/members/5/transfer-ownership \
  -H "Authorization: Token YOUR_SESSION_TOKEN" \
  -H "X-ThunderPhone-Org: 42"
```

```python Python
requests.post(
    f"https://api.thunderphone.com/v1/members/{member_id}/transfer-ownership",
    headers={"Authorization": "Token YOUR_SESSION_TOKEN", "X-ThunderPhone-Org": "42"},
)
```
</CodeGroup>

<Warning>
  Irreversible without the new owner's cooperation. Make sure you trust
  the recipient before calling this endpoint.
</Warning>

Returns `200 OK`:

```json
{ "detail": "Ownership transferred." }
```

Errors:

| Status | Condition |
|--------|-----------|
| `400` | Target is already the owner (e.g. you passed your own membership id) |
| `403` | Caller is not the owner, or the request used an API key |
| `404` | Target membership not found |

---

## Leave organization

Remove the caller's own membership from the org.

<CodeGroup>
```bash cURL
curl -X POST https://api.thunderphone.com/v1/leave \
  -H "Authorization: Token YOUR_SESSION_TOKEN" \
  -H "X-ThunderPhone-Org: 42"
```

```python Python
requests.post(
    "https://api.thunderphone.com/v1/leave",
    headers={"Authorization": "Token YOUR_SESSION_TOKEN", "X-ThunderPhone-Org": "42"},
)
```
</CodeGroup>

Returns `204 No Content`. Errors:

| Status | Condition |
|--------|-----------|
| `403` | Caller is the owner — transfer ownership first, **or** this is the caller's only organization, **or** the request used an API key |
| `404` | Caller is no longer a member of this org |

---

## Related

<CardGroup cols={2}>
  <Card title="Invites" icon="envelope" href="/api-reference/invites">
    Invite users to join this organization.
  </Card>
  <Card title="Organizations" icon="building" href="/api-reference/organizations">
    Manage the org resource itself.
  </Card>
</CardGroup>
