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

# Invites

> Invite users to join your organization, and accept or decline invites you've received.

Invites are the mechanism for bringing new members into an organization.
The admin-side endpoints create/list/revoke invites scoped to your org.
The token-side endpoints let an invitee look up, accept, or decline an
invite by its secret token — those endpoints don't require you to be a
member of the target org (the token itself identifies it).

## Endpoints

| Method   | Path                                 | Required role | Description                                          |
| -------- | ------------------------------------ | ------------- | ---------------------------------------------------- |
| `GET`    | `/v1/invites`                        | `admin+`      | List pending invites                                 |
| `POST`   | `/v1/invites`                        | `admin+`      | Send an invite                                       |
| `DELETE` | `/v1/invites/{invite_id}`            | `admin+`      | Revoke a pending invite                              |
| `GET`    | `/v1/invites/{token}`                | authenticated | Look up an invite by token                           |
| `POST`   | `/v1/invites/{token}/accept`         | authenticated | Accept an invite                                     |
| `POST`   | `/v1/invites/{token}/decline`        | authenticated | Decline an invite                                    |
| `POST`   | `/v1/invites/{token}/request-access` | authenticated | Ask the inviter to re-issue the invite to your email |

## Invite object (admin list/create)

The list / create endpoints use a trimmed view that omits the raw
token. For the by-token lookup see [the token lookup response](#look-up-by-token).

```json theme={null}
{
  "id": 18,
  "email": "new-teammate@example.com",
  "role": "member",
  "status": "pending",
  "invited_by_email": "owner@example.com",
  "created_at": "2026-04-20T18:24:10.113Z",
  "expires_at": "2026-04-27T18:24:10.113Z"
}
```

| Field              | Type      | Description                                      |
| ------------------ | --------- | ------------------------------------------------ |
| `id`               | integer   | Internal invite id (used by the revoke endpoint) |
| `email`            | string    | Invitee email                                    |
| `role`             | string    | Role granted on acceptance: `admin` or `member`  |
| `status`           | string    | `pending`, `accepted`, or `declined`             |
| `invited_by_email` | string    | Email of the admin/owner who sent the invite     |
| `created_at`       | timestamp | ISO 8601 UTC                                     |
| `expires_at`       | timestamp | 7 days after `created_at` by default             |

<Note>
  The raw invite token is only shown via the email link the invitee
  receives, not in API responses. If you need to craft your own invite
  UX, capture the token from the user-submitted URL rather than the
  API.
</Note>

***

## List pending invites

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.thunderphone.com/v1/invites \
    -H "Authorization: Bearer sk_live_YOUR_API_KEY"
  ```

  ```python Python theme={null}
  invites = requests.get(
      "https://api.thunderphone.com/v1/invites",
      headers={"Authorization": "Bearer sk_live_YOUR_API_KEY"},
  ).json()
  ```
</CodeGroup>

Returns a JSON array of [Invite objects](#invite-object-admin-listcreate)
in `pending` state, sorted by `created_at` descending.

***

## Send an invite

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.thunderphone.com/v1/invites \
    -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "email": "new-teammate@example.com",
      "role": "member"
    }'
  ```

  ```python Python theme={null}
  invite = requests.post(
      "https://api.thunderphone.com/v1/invites",
      headers={"Authorization": "Bearer sk_live_YOUR_API_KEY"},
      json={"email": "new-teammate@example.com", "role": "member"},
  ).json()
  ```
</CodeGroup>

| Field   | Type   | Required | Description         |
| ------- | ------ | -------- | ------------------- |
| `email` | string | yes      | Invitee email       |
| `role`  | string | yes      | `admin` or `member` |

Returns `201 Created` with the [Invite object](#invite-object-admin-listcreate).
The invitee receives an email containing a link of the form
`https://app.thunderphone.com/invite/{token}`.

| Status | Condition                                                               |
| ------ | ----------------------------------------------------------------------- |
| `400`  | Email already has a pending invite for this org, or is already a member |
| `403`  | Caller is not an admin/owner                                            |

***

## Revoke an invite

<CodeGroup>
  ```bash cURL theme={null}
  curl -X DELETE https://api.thunderphone.com/v1/invites/18 \
    -H "Authorization: Bearer sk_live_YOUR_API_KEY"
  ```

  ```python Python theme={null}
  requests.delete(
      f"https://api.thunderphone.com/v1/invites/{invite_id}",
      headers={"Authorization": "Bearer sk_live_YOUR_API_KEY"},
  )
  ```
</CodeGroup>

Returns `204 No Content`. Only invites with status `pending` can be
revoked.

***

## Look up by token

Public lookup used by the dashboard's invite-landing page. **This
endpoint is unauthenticated** — the token itself is the credential.
Used to render the "You've been invited to join *Acme Ops*" UI.

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.thunderphone.com/v1/invites/a1b2c3d4-...
  ```

  ```python Python theme={null}
  invite = requests.get(
      f"https://api.thunderphone.com/v1/invites/{token}",
  ).json()
  ```
</CodeGroup>

Response:

```json theme={null}
{
  "id": 18,
  "org_name": "Acme Ops",
  "email": "new-teammate@example.com",
  "role": "member",
  "status": "pending",
  "invited_by_email": "owner@example.com",
  "created_at": "2026-04-20T18:24:10.113Z",
  "expires_at": "2026-04-27T18:24:10.113Z"
}
```

| Status | Condition                                                          |
| ------ | ------------------------------------------------------------------ |
| `200`  | Pending, not expired — response includes `org_name` for UI display |
| `404`  | Token not found                                                    |
| `410`  | Token exists but is already accepted / declined, or has expired    |

***

## Accept an invite

The caller must be authenticated; their user is added to the target org
with the role specified on the invite.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.thunderphone.com/v1/invites/a1b2c3d4-.../accept \
    -H "Authorization: Bearer sk_live_YOUR_API_KEY"
  ```

  ```python Python theme={null}
  result = requests.post(
      f"https://api.thunderphone.com/v1/invites/{token}/accept",
      headers={"Authorization": "Bearer sk_live_YOUR_API_KEY"},
  ).json()
  ```
</CodeGroup>

Returns `200 OK`:

```json theme={null}
{
  "detail": "Invite accepted.",
  "org_id": 42
}
```

| Status | Condition                                                           |
| ------ | ------------------------------------------------------------------- |
| `200`  | Invite accepted; caller is now a member                             |
| `403`  | Caller is signed in as a user whose email does not match the invite |
| `404`  | Token not found                                                     |
| `410`  | Token exists but is already accepted / declined, or has expired     |

***

## Decline an invite

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.thunderphone.com/v1/invites/a1b2c3d4-.../decline \
    -H "Authorization: Bearer sk_live_YOUR_API_KEY"
  ```

  ```python Python theme={null}
  requests.post(
      f"https://api.thunderphone.com/v1/invites/{token}/decline",
      headers={"Authorization": "Bearer sk_live_YOUR_API_KEY"},
  )
  ```
</CodeGroup>

Returns `204 No Content`.

***

## Request access to an invite

For a signed-in user who received an invite link addressed to a
**different** email (e.g. a forwarded invite): emails the original
inviter asking them to grant access to the caller's account instead.
No membership is created — the inviter decides.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.thunderphone.com/v1/invites/a1b2c3d4-.../request-access \
    -H "Authorization: Bearer sk_live_YOUR_API_KEY"
  ```
</CodeGroup>

No request body. Returns `204 No Content` when the request email was
sent.

| Status | Condition                                                                                             |
| ------ | ----------------------------------------------------------------------------------------------------- |
| `204`  | Access request sent to the inviter                                                                    |
| `400`  | The invite is already addressed to your email (just accept it), or it has no sender to approve access |
| `404`  | Token not found                                                                                       |
| `410`  | Invite already accepted / declined, or expired                                                        |
| `429`  | Rate limited — max 3 access requests per hour per user                                                |
| `503`  | The request email could not be sent                                                                   |

***

## Related

<CardGroup cols={2}>
  <Card title="Members" icon="users" href="/api-reference/members">
    Manage members and roles after they join.
  </Card>

  <Card title="Organizations" icon="building" href="/api-reference/organizations">
    Manage the org resource itself.
  </Card>
</CardGroup>
