# Tellagen API Reference

> Generated from the same canonical API contract as the human-readable reference at https://tellagen.com/api-reference.

Documentation contract: `sha256:a5cd8e66988c9c8b615f339b37d49e7572853f4f1e6eb040361a39174365e687`. This fingerprint identifies the generated documentation contract, not the API major version.

## Endpoint index

### Teams

[Teams Markdown export](https://tellagen.com/api-reference/teams/markdown)

- [`GET /api/v1/teams`](https://tellagen.com/api-reference/teams#teams-endpoint-get-api-v1-teams) — List teams
- [`POST /api/v1/teams`](https://tellagen.com/api-reference/teams#teams-endpoint-post-api-v1-teams) — Create team
- [`GET /api/v1/teams/{id}`](https://tellagen.com/api-reference/teams#teams-endpoint-get-api-v1-teams-id) — Get team
- [`PATCH /api/v1/teams/{id}`](https://tellagen.com/api-reference/teams#teams-endpoint-patch-api-v1-teams-id) — Update team
- [`DELETE /api/v1/teams/{id}`](https://tellagen.com/api-reference/teams#teams-endpoint-delete-api-v1-teams-id) — Archive team
- [`POST /api/v1/teams/{id}/restore`](https://tellagen.com/api-reference/teams#teams-endpoint-post-api-v1-teams-id-restore) — Restore team
- [`GET /api/v1/teams/{id}/members`](https://tellagen.com/api-reference/teams#teams-endpoint-get-api-v1-teams-id-members) — List team members
- [`POST /api/v1/teams/{id}/members`](https://tellagen.com/api-reference/teams#teams-endpoint-post-api-v1-teams-id-members) — Add team member
- [`PATCH /api/v1/teams/{id}/members/{userId}`](https://tellagen.com/api-reference/teams#teams-endpoint-patch-api-v1-teams-id-members-userid) — Update team member
- [`DELETE /api/v1/teams/{id}/members/{userId}`](https://tellagen.com/api-reference/teams#teams-endpoint-delete-api-v1-teams-id-members-userid) — Remove team member

## Base URL

`https://{company}.api.tellagen.com/api/v1/`

Replace `{company}` with your company subdomain. Requests and JSON responses use `application/json` unless an endpoint documents an empty response.

## Authentication

Create an API token in **Settings > API keys** and send it as a Bearer token:

`Authorization: Bearer tllg_YOUR_TOKEN`

Tokens are shown only once when created. Grant only the scopes the client needs.

## Integration guides

- [Make your first request](https://tellagen.com/api-reference#first-request)
- [Retries and updates](https://tellagen.com/api-reference#reliable-requests)
- [Rate limits](https://tellagen.com/api-reference/guides/rate-limits)
- [Filter and paginate collections](https://tellagen.com/api-reference/guides/pagination)
- [Create and move an incident through its workflow](https://tellagen.com/api-reference/guides/workflows)
- [Send custom-field values](https://tellagen.com/api-reference/guides/field-values)
- [Receive webhooks (preview)](https://tellagen.com/api-reference/guides/webhooks)

## Versioning

All endpoints in this reference use `/api/v1` and are supported.

**Compatible changes.** Tellagen can add optional response fields or enum values. Ignore response fields that your client does not use.

**Breaking changes.** Tellagen uses a new major version for incompatible changes.

**Deprecated endpoints.** Tellagen marks an endpoint deprecated at least 180 days before removal. Deprecated responses identify the replacement endpoint.

## Scopes

| Scope | Description |
| --- | --- |
| `incidents:read` | List and retrieve incidents, and discover writable workflow statuses and severities |
| `incidents:write` | Create and update incidents and timeline events. Requires an active responder seat for the key owner. |
| `slack_context:read` | Read Slack context collected for incident investigation |
| `services:read` | List and retrieve services, and discover writable service tiers |
| `services:write` | Create, update, and archive services. Requires an active responder seat and the manage_settings role permission. |
| `teams:read` | List and retrieve teams and members |
| `teams:write` | Create, update, and archive teams; manage members. Requires an active responder seat and the manage_teams role permission. |
| `custom_fields:read` | List and retrieve custom field definitions and values |
| `custom_fields:write` | Create, update, and archive custom fields; set incident field values. Definition changes require the manage_settings role permission. Incident value changes require an active responder seat. |
| `webhooks:manage` | Create, pause, resume, revoke, and rotate workspace webhook subscriptions. Requires the manage_settings role permission. |
| `customer_revenue:read` | Read customer revenue and financial impact. Requires the view_customer_revenue role permission and an active key owner membership. |
| `status_updates:email` | Email recorded status updates to the workspace's email groups (never to typed-in addresses). Not granted by default. Requires an active responder seat for the key owner and incidents:write. |

## Prerequisite definitions

- **Active responder seat** (`active_responder`): The API key owner must have an active responder seat.
- **Manage workspace settings** (`manage_settings`): The API key owner must have the manage_settings role permission.
- **Manage teams** (`manage_teams`): The API key owner must have the manage_teams role permission.
- **Incident is not archived** (`mutable_incident`): The incident must not be archived. Workflow-state restrictions are described by each operation.
- **Linked Slack channel** (`slack_channel`): The incident must have a linked Slack channel.

## Errors

Errors always include a stable `code` and a human-readable `message`. They can also include `field`, `details`, and `request_id`.

You can send `X-Request-ID` with a request. If you omit it, the API generates one. The response header and error body return the same value.

### Missing scope (403)

```json
{
  "code": "missing_scope",
  "message": "insufficient scope: incidents:write required",
  "details": {
    "required_scope": "incidents:write"
  },
  "request_id": "01JEXAMPLE8R4N5W6Y7Z"
}
```

### Stale resource version (412)

```json
{
  "code": "precondition_failed",
  "message": "the resource changed; fetch it again and retry with the current resource version",
  "field": "If-Tellagen-Resource-Version",
  "request_id": "01JEXAMPLE8R4N5W6Y7Z"
}
```

### Rate limit (429)

```json
{
  "code": "rate_limited",
  "message": "rate limit exceeded",
  "details": {
    "retry_after_seconds": 1
  },
  "request_id": "01JEXAMPLE8R4N5W6Y7Z"
}
```

When you contact support, include the request ID, method, path, status, and time. Omit tokens and private request bodies.

| Code | Meaning | Next action | Retry |
| --- | --- | --- | --- |
| `authentication_required` | The request has no valid API token or session. | Send an active Bearer token for this workspace. Replace an expired or revoked key. | After correcting authentication. |
| `missing_scope` | The token lacks the scope named in details.required_scope. | Read details.required_scope. Ask a workspace manager for a replacement key with that scope. | After correcting access. |
| `responder_seat_required` | The key owner needs the seat named in details.required_seat. | Ask a workspace manager to review the key owner’s responder seat. | After correcting access. |
| `missing_role_permission` | The key owner lacks details.required_permission or one of details.required_roles. | Ask a workspace manager to review the owner’s role and the permission named in details. | After correcting access. |
| `permission_denied` | Access is denied without exposing private authorization state. | Verify the workspace, key owner, and resource access with a workspace manager. | After correcting access. |
| `invalid_resource_state` | The operation is not valid for the current resource state. | Read the current resource. Restore an archived resource or choose an operation allowed in its current state. | After reconciling state. |
| `invalid_relationship_state` | Restore requires a retained owner or parent relationship to identify an active resource. | Restore the retained parent or owning team before restoring this resource. | After correcting the relationship. |
| `duplicate_slug` | A team already uses the requested slug. | Read the existing team or choose a different slug. | After changing the request. |
| `invalid_json` | The request body is not one valid JSON document. | Send one valid JSON object with Content-Type: application/json. | After correcting the body. |
| `unknown_field` | The request contains an unsupported JSON property named in field. | Remove or correct the property named in field. Use the request schema, not the response schema. | After correcting the body. |
| `request_body_too_large` | The request body is larger than 1 MiB. | Reduce the body to 1 MiB or less. | After reducing the body. |
| `multiple_json_values` | The request body contains more than one JSON document. | Send one JSON document rather than concatenated documents. | After correcting the body. |
| `idempotency_key_reused` | The key was already used with different request content. | Recover the original request for this key. Use a new key only for a different intended operation. | Do not resend changed content with the same key. |
| `idempotency_in_progress` | An equal request with this key is still running. Retry later. | Wait for Retry-After, then resend the identical body with the same key. | After the indicated delay. |
| `idempotency_outcome_unknown` | An earlier request may have completed, but its result could not be stored. Do not retry it automatically. | Read the resource state and contact support with the request ID before deciding whether to create again. | Never retry automatically. |
| `precondition_required` | An API-key PATCH request omitted If-Tellagen-Resource-Version. | Read the resource, then send its Tellagen-Resource-Version as If-Tellagen-Resource-Version. | After adding the current version. |
| `invalid_precondition` | If-Tellagen-Resource-Version is not a positive integer. | Use the positive integer version returned for this resource. Do not use an ETag or a timestamp. | After correcting the header. |
| `precondition_failed` | The resource changed after the client read it. | Read the resource again. Reconcile your intended change with the current state before using its new version. | After reconciling the change. |
| `rate_limited` | Retry after details.retry_after_seconds or the Retry-After header. | Wait for Retry-After or details.retry_after_seconds. Limit concurrency across the workspace. | After the indicated delay, with the original idempotency key where supported. |
| `internal` | The server failed without exposing an internal error. | Keep the request ID. Read current state before retrying a write whose outcome is uncertain. | Retry reads with bounded backoff. Follow operation-specific rules for writes. |
| `invalid_slack_channel_name_request` | The channel-name request is invalid. | Use custom mode with name, or workspace_template mode without name. | After correcting the request. |
| `slack_not_configured` | The workspace has no usable Slack integration. | Ask a workspace manager to connect or repair Slack. | After repairing the integration. |
| `incident_slack_channel_missing` | The incident has no linked Slack channel. | Link a channel through the incident workspace before requesting a rename. | After linking a channel. |
| `slack_channel_not_found` | Slack cannot find the linked channel. | Verify the channel still exists and that the integration can access it. | After repairing channel access. |
| `slack_channel_name_taken` | Another Slack channel uses the requested name. | Choose another custom name or revise the workspace naming template. | After choosing an available name. |
| `invalid_idempotency_key` | The idempotency key has an invalid length or character. | Use 1–128 visible, non-space ASCII characters. A UUID is suitable. | After correcting the key. |

| Status | Meaning |
| --- | --- |
| `400` | Bad Request — invalid input or missing required fields |
| `401` | Unauthorized — missing or invalid token |
| `403` | Forbidden — insufficient scope or responder access |
| `404` | Not Found — resource does not exist |
| `409` | Conflict — resource state prevents the request |
| `412` | Precondition Failed — the supplied resource version is stale |
| `428` | Precondition Required — this operation needs If-Tellagen-Resource-Version |
| `429` | Too Many Requests — rate limit exceeded |
| `500` | Internal Server Error |

Authenticated requests share a workspace limit. Anonymous requests and failed authentication attempts use the trusted client IP. Responses include `X-RateLimit-Limit` and `X-RateLimit-Remaining`. A `429` response also includes `Retry-After`.

## Teams

Creates, lists, reads, updates, archives, and restores teams. This section also manages team memberships.

### `GET /api/v1/teams`

**List teams.** Returns one page sorted by team name and team ID. By default, the response excludes archived teams.

Required scope: `teams:read`

Operation ID: `listTeams`
Contract: `supported`
Retry guidance: You can repeat this request. If the API returns 429, wait for Retry-After. Set a maximum number of attempts.
Prerequisites: None

#### Query parameters

Required nested fields apply when their parent object is present. Optional does not imply nullable.

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `limit` | `integer` | No | The maximum number of items in one page. The default is 50, and the maximum is 100. Minimum: 1. Maximum: 100. Default: 50. |
| `cursor` | `string` | No | The next_cursor value from the previous page. Clients treat this value as opaque and do not create, inspect, or change it. Minimum length: 1 characters. Maximum length: 4096 characters. |
| `include_archived` | `boolean` | No | Controls whether the response includes archived teams. The default is false. Default: false. |

#### Example request

Replace `{company}`, `<token>`, and any other placeholders with your values.

```bash
curl --request GET \
  --url 'https://{company}.api.tellagen.com/api/v1/teams' \
  --header 'Authorization: Bearer <token>'
```

#### Success response

Status: `200 OK`

#### Response fields

Returned means the field is present when its parent object is present. A nullable field can contain null.

| Name | Type | Returned | Description |
| --- | --- | --- | --- |
| `teams` | `object[]` | Always | The teams in this page. |
| `teams[].id` | `integer` | Always | The positive numeric ID of the team. Minimum: 1. |
| `teams[].revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `teams[].company_id` | `integer` | Always | The ID of the workspace that owns the team. Minimum: 1. |
| `teams[].name` | `string` | Always | The display name of the team. |
| `teams[].slug` | `string` | Always | The permanent key used to identify the team. |
| `teams[].description` | `string` | Optional | The workspace description of the team. |
| `teams[].parent_team_id` | `integer` | Optional | The positive ID of the parent team. This field is absent for a root team. Minimum: 1. |
| `teams[].member_count` | `integer` | Optional | The number of active workspace members in the team. Minimum: 0. |
| `teams[].created_at` | `string (RFC 3339)` | Always | The team creation time. |
| `teams[].updated_at` | `string (RFC 3339)` | Always | The time of the latest team change. |
| `teams[].archived_at` | `string (RFC 3339)` | Optional | The team archive time. This field is absent for an active team. |
| `pagination` | `object` | Always | Pagination details for a collection response. |
| `pagination.limit` | `integer` | Always | The maximum number of items requested for this page. Minimum: 1. Maximum: 100. |
| `pagination.has_more` | `boolean` | Always | Indicates whether another page is available. |
| `pagination.next_cursor` | `string` | Optional | The opaque cursor for the next page. This field is absent on the final page. |

Returns teams and pagination fields.

```json
{
  "teams": [
    {
      "id": 42,
      "revision": 2,
      "company_id": 7,
      "name": "Platform Team",
      "slug": "platform_team",
      "description": "Core platform infrastructure",
      "member_count": 12,
      "created_at": "2025-01-10T09:00:00Z",
      "updated_at": "2025-01-10T09:00:00Z"
    }
  ],
  "pagination": {
    "limit": 50,
    "has_more": true,
    "next_cursor": "opaque-server-cursor"
  }
}
```

#### Endpoint error responses

| Status | Meaning |
| --- | --- |
| `400` | The API rejected the request because its syntax or one of its values is invalid. |
| `401` | The request does not contain valid authentication. |
| `403` | Authentication succeeded, but the caller lacks the required API scope or workspace permission. |
| `404` | The requested resource does not exist or is not available to the caller. |
| `409` | The request conflicts with the current resource state. idempotency_key_reused means that the key belongs to different request content. idempotency_in_progress means that an identical request is still in progress. idempotency_outcome_unknown means that an earlier request may have completed, but its result could not be stored and must not be retried automatically. |
| `429` | The request exceeded an applicable sliding limit. Free API keys allow 20 requests per second and 600 requests per 60 seconds. Team API keys allow 100 requests per second and 3,000 requests per 60 seconds. Browser users allow 100 requests per second. Browser company allows 500 requests per second and 5,000 requests per 10 seconds. Browser company means the sum of user traffic for one company. Anonymous client IPs allow 20 requests per second and 200 requests per 10 seconds. The rate limiter does not count requests that it rejects. |
| `500` | An internal server error prevented completion of the request. |

### `POST /api/v1/teams`

**Create team.** Creates a team. If slug is omitted, the API creates it from name. A parent team must be active. If any team already uses the slug, the API returns 409. This includes archived teams.

Required scope: `teams:write`

Operation ID: `createTeam`
Contract: `supported`
Retry guidance: Within 24 hours, retry with the original Idempotency-Key and identical body. If the result is unknown, do not retry automatically. Check whether the resource exists.
Prerequisites: Active responder seat, Manage teams

#### Request headers

Required nested fields apply when their parent object is present. Optional does not imply nullable.

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `Idempotency-Key` | `string` | No | A key for safe retries of a create request. It must contain 1–128 visible, non-space ASCII characters. The same key applies only to an identical request. Minimum length: 1 characters. Maximum length: 128 characters. Pattern: ^[\x21-\x7E]+$. |

#### Example request

Replace `{company}`, `<token>`, and any other placeholders with your values.

For `<idempotency-key>`, choose a unique key on the first attempt. Reuse it only when retrying the identical request.

```bash
curl --request POST \
  --url 'https://{company}.api.tellagen.com/api/v1/teams' \
  --header 'Authorization: Bearer <token>' \
  --header 'Idempotency-Key: <idempotency-key>' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "Platform Team",
  "description": "Core platform infrastructure"
}'
```

#### Request body

Required nested fields apply when their parent object is present. Optional does not imply nullable.

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | `string` | Yes | The team name. The length must be from 1 through 128 characters. Minimum length: 1 characters. Maximum length: 128 characters. |
| `slug` | `string` | No | A permanent team key. It starts with a lowercase letter and contains lowercase letters, digits, or underscores. If slug is omitted, the API creates it from name. |
| `description` | `string` | No | The workspace description of the team. |
| `parent_team_id` | `integer` | No | The positive ID of an active parent team. If the request omits parent_team_id, the API creates a root team. Minimum: 1. |

Request JSON:

```json
{
  "name": "Platform Team",
  "description": "Core platform infrastructure"
}
```

#### Success response

Status: `201 Created`

#### Response fields

Returned means the field is present when its parent object is present. A nullable field can contain null.

| Name | Type | Returned | Description |
| --- | --- | --- | --- |
| `team` | `object` | Always | The complete public record for a team. |
| `team.id` | `integer` | Always | The positive numeric ID of the team. Minimum: 1. |
| `team.revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `team.company_id` | `integer` | Always | The ID of the workspace that owns the team. Minimum: 1. |
| `team.name` | `string` | Always | The display name of the team. |
| `team.slug` | `string` | Always | The permanent key used to identify the team. |
| `team.description` | `string` | Optional | The workspace description of the team. |
| `team.parent_team_id` | `integer` | Optional | The positive ID of the parent team. This field is absent for a root team. Minimum: 1. |
| `team.member_count` | `integer` | Optional | The number of active workspace members in the team. Minimum: 0. |
| `team.created_at` | `string (RFC 3339)` | Always | The team creation time. |
| `team.updated_at` | `string (RFC 3339)` | Always | The time of the latest team change. |
| `team.archived_at` | `string (RFC 3339)` | Optional | The team archive time. This field is absent for an active team. |

Returns the created team.

```json
{
  "team": {
    "id": 42,
    "revision": 1,
    "company_id": 7,
    "name": "Platform Team",
    "slug": "platform_team",
    "description": "Core platform infrastructure",
    "created_at": "2025-01-30T12:00:00Z",
    "updated_at": "2025-01-30T12:00:00Z"
  }
}
```

#### Response headers

Returned means the field is present when its parent object is present. A nullable field can contain null.

| Name | Type | Returned | Description |
| --- | --- | --- | --- |
| `Tellagen-Resource-Version` | `integer` | Always | The positive resource version for If-Tellagen-Resource-Version in the next API-key PATCH request. Minimum: 1. |

#### Endpoint error responses

Endpoint-specific codes: `idempotency_key_reused`, `idempotency_in_progress`, `idempotency_outcome_unknown`

| Status | Meaning |
| --- | --- |
| `400` | The API rejected the request because its syntax or one of its values is invalid. |
| `401` | The request does not contain valid authentication. |
| `403` | Authentication succeeded, but the caller lacks the required API scope or workspace permission. |
| `404` | The requested resource does not exist or is not available to the caller. |
| `409` | The request conflicts with the current resource state. idempotency_key_reused means that the key belongs to different request content. idempotency_in_progress means that an identical request is still in progress. idempotency_outcome_unknown means that an earlier request may have completed, but its result could not be stored and must not be retried automatically. |
| `429` | The request exceeded an applicable sliding limit. Free API keys allow 20 requests per second and 600 requests per 60 seconds. Team API keys allow 100 requests per second and 3,000 requests per 60 seconds. Browser users allow 100 requests per second. Browser company allows 500 requests per second and 5,000 requests per 10 seconds. Browser company means the sum of user traffic for one company. Anonymous client IPs allow 20 requests per second and 200 requests per 10 seconds. The rate limiter does not count requests that it rejects. |
| `500` | An internal server error prevented completion of the request. |

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

**Get team.** Returns one team by its positive numeric ID. A team remains available by ID after it is archived.

Required scope: `teams:read`

Operation ID: `getTeam`
Contract: `supported`
Retry guidance: You can repeat this request. If the API returns 429, wait for Retry-After. Set a maximum number of attempts.
Prerequisites: None

#### Path parameters

Required nested fields apply when their parent object is present. Optional does not imply nullable.

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `integer` | Yes | A positive numeric team ID returned by this API. Minimum: 1. |

#### Example request

Replace `{company}`, `<token>`, and any other placeholders with your values.

```bash
curl --request GET \
  --url 'https://{company}.api.tellagen.com/api/v1/teams/{id}' \
  --header 'Authorization: Bearer <token>'
```

#### Success response

Status: `200 OK`

#### Response fields

Returned means the field is present when its parent object is present. A nullable field can contain null.

| Name | Type | Returned | Description |
| --- | --- | --- | --- |
| `team` | `object` | Always | The complete public record for a team. |
| `team.id` | `integer` | Always | The positive numeric ID of the team. Minimum: 1. |
| `team.revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `team.company_id` | `integer` | Always | The ID of the workspace that owns the team. Minimum: 1. |
| `team.name` | `string` | Always | The display name of the team. |
| `team.slug` | `string` | Always | The permanent key used to identify the team. |
| `team.description` | `string` | Optional | The workspace description of the team. |
| `team.parent_team_id` | `integer` | Optional | The positive ID of the parent team. This field is absent for a root team. Minimum: 1. |
| `team.member_count` | `integer` | Optional | The number of active workspace members in the team. Minimum: 0. |
| `team.created_at` | `string (RFC 3339)` | Always | The team creation time. |
| `team.updated_at` | `string (RFC 3339)` | Always | The time of the latest team change. |
| `team.archived_at` | `string (RFC 3339)` | Optional | The team archive time. This field is absent for an active team. |

Returns the team.

```json
{
  "team": {
    "id": 42,
    "revision": 2,
    "company_id": 7,
    "name": "Platform Team",
    "slug": "platform_team",
    "description": "Core platform infrastructure",
    "created_at": "2025-01-10T09:00:00Z",
    "updated_at": "2025-01-10T09:00:00Z"
  }
}
```

#### Response headers

Returned means the field is present when its parent object is present. A nullable field can contain null.

| Name | Type | Returned | Description |
| --- | --- | --- | --- |
| `Tellagen-Resource-Version` | `integer` | Always | The positive resource version for If-Tellagen-Resource-Version in the next API-key PATCH request. Minimum: 1. |

#### Endpoint error responses

| Status | Meaning |
| --- | --- |
| `400` | The API rejected the request because its syntax or one of its values is invalid. |
| `401` | The request does not contain valid authentication. |
| `403` | Authentication succeeded, but the caller lacks the required API scope or workspace permission. |
| `404` | The requested resource does not exist or is not available to the caller. |
| `409` | The request conflicts with the current resource state. idempotency_key_reused means that the key belongs to different request content. idempotency_in_progress means that an identical request is still in progress. idempotency_outcome_unknown means that an earlier request may have completed, but its result could not be stored and must not be retried automatically. |
| `429` | The request exceeded an applicable sliding limit. Free API keys allow 20 requests per second and 600 requests per 60 seconds. Team API keys allow 100 requests per second and 3,000 requests per 60 seconds. Browser users allow 100 requests per second. Browser company allows 500 requests per second and 5,000 requests per 10 seconds. Browser company means the sum of user traffic for one company. Anonymous client IPs allow 20 requests per second and 200 requests per 10 seconds. The rate limiter does not count requests that it rejects. |
| `500` | An internal server error prevented completion of the request. |

### `PATCH /api/v1/teams/{id}`

**Update team.** Updates an active team. If the request supplies a parent team, that team must be active. The update must not create a hierarchy cycle. Omitted fields stay unchanged.

Required scope: `teams:write`

Operation ID: `updateTeam`
Contract: `supported`
Retry guidance: If the API returns 412, read the resource again. Compare the latest changes before you retry with the new version.
Prerequisites: Active responder seat, Manage teams

#### Path parameters

Required nested fields apply when their parent object is present. Optional does not imply nullable.

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `integer` | Yes | A positive numeric team ID returned by this API. Minimum: 1. |

#### Request headers

Required nested fields apply when their parent object is present. Optional does not imply nullable.

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `If-Tellagen-Resource-Version` | `integer` | Yes | The current positive resource version from the latest response. API-key PATCH requests must send this value. Minimum: 1. |

#### Example request

Replace `{company}`, `<token>`, and any other placeholders with your values.

Use the Tellagen-Resource-Version header from a GET of this resource for `<resource-version>`.

```bash
curl --request PATCH \
  --url 'https://{company}.api.tellagen.com/api/v1/teams/{id}' \
  --header 'Authorization: Bearer <token>' \
  --header 'If-Tellagen-Resource-Version: <resource-version>' \
  --header 'Content-Type: application/json' \
  --data '{
  "description": "Core platform and infrastructure"
}'
```

#### Request body

Required nested fields apply when their parent object is present. Optional does not imply nullable.

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | `string` | No | The team name. The length must be from 1 through 128 characters. Minimum length: 1 characters. Maximum length: 128 characters. |
| `description` | `string` | No | The replacement team description. |
| `parent_team_id` | `integer \| null` | No | The positive ID of an active parent team. A null value makes this team a root team. |

Request JSON:

```json
{
  "description": "Core platform and infrastructure"
}
```

#### Success response

Status: `200 OK`

#### Response fields

Returned means the field is present when its parent object is present. A nullable field can contain null.

| Name | Type | Returned | Description |
| --- | --- | --- | --- |
| `team` | `object` | Always | The complete public record for a team. |
| `team.id` | `integer` | Always | The positive numeric ID of the team. Minimum: 1. |
| `team.revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `team.company_id` | `integer` | Always | The ID of the workspace that owns the team. Minimum: 1. |
| `team.name` | `string` | Always | The display name of the team. |
| `team.slug` | `string` | Always | The permanent key used to identify the team. |
| `team.description` | `string` | Optional | The workspace description of the team. |
| `team.parent_team_id` | `integer` | Optional | The positive ID of the parent team. This field is absent for a root team. Minimum: 1. |
| `team.member_count` | `integer` | Optional | The number of active workspace members in the team. Minimum: 0. |
| `team.created_at` | `string (RFC 3339)` | Always | The team creation time. |
| `team.updated_at` | `string (RFC 3339)` | Always | The time of the latest team change. |
| `team.archived_at` | `string (RFC 3339)` | Optional | The team archive time. This field is absent for an active team. |

Returns the updated team.

```json
{
  "team": {
    "id": 42,
    "revision": 3,
    "company_id": 7,
    "name": "Platform Team",
    "slug": "platform_team",
    "description": "Core platform and infrastructure",
    "created_at": "2025-01-10T09:00:00Z",
    "updated_at": "2025-01-30T12:00:00Z"
  }
}
```

#### Response headers

Returned means the field is present when its parent object is present. A nullable field can contain null.

| Name | Type | Returned | Description |
| --- | --- | --- | --- |
| `Tellagen-Resource-Version` | `integer` | Always | The positive resource version for If-Tellagen-Resource-Version in the next API-key PATCH request. Minimum: 1. |

#### Endpoint error responses

| Status | Meaning |
| --- | --- |
| `400` | The API rejected the request because its syntax or one of its values is invalid. |
| `401` | The request does not contain valid authentication. |
| `403` | Authentication succeeded, but the caller lacks the required API scope or workspace permission. |
| `404` | The requested resource does not exist or is not available to the caller. |
| `409` | The request conflicts with the current resource state. idempotency_key_reused means that the key belongs to different request content. idempotency_in_progress means that an identical request is still in progress. idempotency_outcome_unknown means that an earlier request may have completed, but its result could not be stored and must not be retried automatically. |
| `412` | The resource changed after the client obtained its If-Tellagen-Resource-Version value. |
| `428` | An API-key PATCH request did not include the required If-Tellagen-Resource-Version header. |
| `429` | The request exceeded an applicable sliding limit. Free API keys allow 20 requests per second and 600 requests per 60 seconds. Team API keys allow 100 requests per second and 3,000 requests per 60 seconds. Browser users allow 100 requests per second. Browser company allows 500 requests per second and 5,000 requests per 10 seconds. Browser company means the sum of user traffic for one company. Anonymous client IPs allow 20 requests per second and 200 requests per 10 seconds. The rate limiter does not count requests that it rejects. |
| `500` | An internal server error prevented completion of the request. |

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

**Archive team.** Archives a team. A repeated request returns 204 without changing the archive time or resource version. The API retains memberships and child-team relationships. The archived team remains available by ID. It cannot be updated.

Required scope: `teams:write`

Operation ID: `archiveTeam`
Contract: `supported`
Retry guidance: If the result is unknown, read the resource before another attempt. The error details explain the next step.
Prerequisites: Active responder seat, Manage teams

#### Path parameters

Required nested fields apply when their parent object is present. Optional does not imply nullable.

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `integer` | Yes | A positive numeric team ID returned by this API. Minimum: 1. |

#### Example request

Replace `{company}`, `<token>`, and any other placeholders with your values.

```bash
curl --request DELETE \
  --url 'https://{company}.api.tellagen.com/api/v1/teams/{id}' \
  --header 'Authorization: Bearer <token>'
```

#### Success response

Status: `204 No Content`

No response body.

#### Endpoint error responses

| Status | Meaning |
| --- | --- |
| `400` | The API rejected the request because its syntax or one of its values is invalid. |
| `401` | The request does not contain valid authentication. |
| `403` | Authentication succeeded, but the caller lacks the required API scope or workspace permission. |
| `404` | The requested resource does not exist or is not available to the caller. |
| `409` | The request conflicts with the current resource state. idempotency_key_reused means that the key belongs to different request content. idempotency_in_progress means that an identical request is still in progress. idempotency_outcome_unknown means that an earlier request may have completed, but its result could not be stored and must not be retried automatically. |
| `429` | The request exceeded an applicable sliding limit. Free API keys allow 20 requests per second and 600 requests per 60 seconds. Team API keys allow 100 requests per second and 3,000 requests per 60 seconds. Browser users allow 100 requests per second. Browser company allows 500 requests per second and 5,000 requests per 10 seconds. Browser company means the sum of user traffic for one company. Anonymous client IPs allow 20 requests per second and 200 requests per 10 seconds. The rate limiter does not count requests that it rejects. |
| `500` | An internal server error prevented completion of the request. |

### `POST /api/v1/teams/{id}/restore`

**Restore team.** Restores an archived team and returns the active team. A repeated request returns the active team without changing its resource version. If the team has parent_team_id, that parent team must be active. Otherwise, the API returns 409 invalid_relationship_state. The API retains team memberships.

Required scope: `teams:write`

Operation ID: `restoreTeam`
Contract: `supported`
Retry guidance: If the result is unknown, read the resource before another attempt. The error details explain the next step.
Prerequisites: Active responder seat, Manage teams

#### Path parameters

Required nested fields apply when their parent object is present. Optional does not imply nullable.

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `integer` | Yes | A positive numeric team ID returned by this API. Minimum: 1. |

#### Example request

Replace `{company}`, `<token>`, and any other placeholders with your values.

```bash
curl --request POST \
  --url 'https://{company}.api.tellagen.com/api/v1/teams/{id}/restore' \
  --header 'Authorization: Bearer <token>'
```

#### Success response

Status: `200 OK`

#### Response fields

Returned means the field is present when its parent object is present. A nullable field can contain null.

| Name | Type | Returned | Description |
| --- | --- | --- | --- |
| `team` | `object` | Always | The complete public record for a team. |
| `team.id` | `integer` | Always | The positive numeric ID of the team. Minimum: 1. |
| `team.revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `team.company_id` | `integer` | Always | The ID of the workspace that owns the team. Minimum: 1. |
| `team.name` | `string` | Always | The display name of the team. |
| `team.slug` | `string` | Always | The permanent key used to identify the team. |
| `team.description` | `string` | Optional | The workspace description of the team. |
| `team.parent_team_id` | `integer` | Optional | The positive ID of the parent team. This field is absent for a root team. Minimum: 1. |
| `team.member_count` | `integer` | Optional | The number of active workspace members in the team. Minimum: 0. |
| `team.created_at` | `string (RFC 3339)` | Always | The team creation time. |
| `team.updated_at` | `string (RFC 3339)` | Always | The time of the latest team change. |
| `team.archived_at` | `string (RFC 3339)` | Optional | The team archive time. This field is absent for an active team. |

Returns the complete active team.

```json
{
  "team": {
    "id": 42,
    "revision": 3,
    "company_id": 7,
    "name": "Platform Team",
    "slug": "platform_team",
    "description": "Core platform and infrastructure",
    "created_at": "2025-01-10T09:00:00Z",
    "updated_at": "2025-01-30T12:00:00Z"
  }
}
```

#### Response headers

Returned means the field is present when its parent object is present. A nullable field can contain null.

| Name | Type | Returned | Description |
| --- | --- | --- | --- |
| `Tellagen-Resource-Version` | `integer` | Always | The positive resource version for If-Tellagen-Resource-Version in the next API-key PATCH request. Minimum: 1. |

#### Endpoint error responses

Endpoint-specific codes: `invalid_relationship_state`

| Status | Meaning |
| --- | --- |
| `400` | The API rejected the request because its syntax or one of its values is invalid. |
| `401` | The request does not contain valid authentication. |
| `403` | Authentication succeeded, but the caller lacks the required API scope or workspace permission. |
| `404` | The requested resource does not exist or is not available to the caller. |
| `409` | The request conflicts with the current resource state. idempotency_key_reused means that the key belongs to different request content. idempotency_in_progress means that an identical request is still in progress. idempotency_outcome_unknown means that an earlier request may have completed, but its result could not be stored and must not be retried automatically. |
| `429` | The request exceeded an applicable sliding limit. Free API keys allow 20 requests per second and 600 requests per 60 seconds. Team API keys allow 100 requests per second and 3,000 requests per 60 seconds. Browser users allow 100 requests per second. Browser company allows 500 requests per second and 5,000 requests per 10 seconds. Browser company means the sum of user traffic for one company. Anonymous client IPs allow 20 requests per second and 200 requests per 10 seconds. The rate limiter does not count requests that it rejects. |
| `500` | An internal server error prevented completion of the request. |

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

**List team members.** Returns one page sorted by membership creation time and user ID. Each entry includes membership and user details.

Required scope: `teams:read`

Operation ID: `listTeamMembers`
Contract: `supported`
Retry guidance: You can repeat this request. If the API returns 429, wait for Retry-After. Set a maximum number of attempts.
Prerequisites: None

#### Path parameters

Required nested fields apply when their parent object is present. Optional does not imply nullable.

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `integer` | Yes | A positive numeric team ID returned by this API. Minimum: 1. |

#### Query parameters

Required nested fields apply when their parent object is present. Optional does not imply nullable.

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `limit` | `integer` | No | The maximum number of items in one page. The default is 50, and the maximum is 100. Minimum: 1. Maximum: 100. Default: 50. |
| `cursor` | `string` | No | The next_cursor value from the previous page. Clients treat this value as opaque and do not create, inspect, or change it. Minimum length: 1 characters. Maximum length: 4096 characters. |

#### Example request

Replace `{company}`, `<token>`, and any other placeholders with your values.

```bash
curl --request GET \
  --url 'https://{company}.api.tellagen.com/api/v1/teams/{id}/members' \
  --header 'Authorization: Bearer <token>'
```

#### Success response

Status: `200 OK`

#### Response fields

Returned means the field is present when its parent object is present. A nullable field can contain null.

| Name | Type | Returned | Description |
| --- | --- | --- | --- |
| `members` | `object[]` | Always | The team memberships in this page. |
| `members[].team_id` | `integer` | Always | The positive numeric ID of the team. |
| `members[].user_id` | `integer` | Always | The positive numeric ID of the workspace member. |
| `members[].role` | `string` | Always | The role in this team. Possible values: `"lead"` \| `"member"` |
| `members[].email` | `string` | Always | The email address of the workspace member. |
| `members[].display_name` | `string` | Optional | The display name of the workspace member. |
| `members[].avatar_url` | `string` | Optional | The avatar URL of the workspace member. |
| `pagination` | `object` | Always | Pagination details for a collection response. |
| `pagination.limit` | `integer` | Always | The maximum number of items requested for this page. Minimum: 1. Maximum: 100. |
| `pagination.has_more` | `boolean` | Always | Indicates whether another page is available. |
| `pagination.next_cursor` | `string` | Optional | The opaque cursor for the next page. This field is absent on the final page. |

Returns team members and pagination fields.

```json
{
  "members": [
    {
      "team_id": 42,
      "user_id": 100,
      "role": "lead",
      "email": "lead@example.com",
      "display_name": "Jane Doe",
      "avatar_url": ""
    }
  ],
  "pagination": {
    "limit": 50,
    "has_more": false
  }
}
```

#### Endpoint error responses

| Status | Meaning |
| --- | --- |
| `400` | The API rejected the request because its syntax or one of its values is invalid. |
| `401` | The request does not contain valid authentication. |
| `403` | Authentication succeeded, but the caller lacks the required API scope or workspace permission. |
| `404` | The requested resource does not exist or is not available to the caller. |
| `409` | The request conflicts with the current resource state. idempotency_key_reused means that the key belongs to different request content. idempotency_in_progress means that an identical request is still in progress. idempotency_outcome_unknown means that an earlier request may have completed, but its result could not be stored and must not be retried automatically. |
| `429` | The request exceeded an applicable sliding limit. Free API keys allow 20 requests per second and 600 requests per 60 seconds. Team API keys allow 100 requests per second and 3,000 requests per 60 seconds. Browser users allow 100 requests per second. Browser company allows 500 requests per second and 5,000 requests per 10 seconds. Browser company means the sum of user traffic for one company. Anonymous client IPs allow 20 requests per second and 200 requests per 10 seconds. The rate limiter does not count requests that it rejects. |
| `500` | An internal server error prevented completion of the request. |

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

**Add team member.** Adds an active workspace member to an active team. If the team membership already exists, the API updates its role.

Required scope: `teams:write`

Operation ID: `addTeamMember`
Contract: `supported`
Retry guidance: If the result is unknown, read the resource before another attempt. The error details explain the next step.
Prerequisites: Active responder seat, Manage teams

#### Path parameters

Required nested fields apply when their parent object is present. Optional does not imply nullable.

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `integer` | Yes | A positive numeric team ID returned by this API. Minimum: 1. |

#### Example request

Replace `{company}`, `<token>`, and any other placeholders with your values.

```bash
curl --request POST \
  --url 'https://{company}.api.tellagen.com/api/v1/teams/{id}/members' \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '{
  "user_id": 100,
  "role": "lead"
}'
```

#### Request body

Required nested fields apply when their parent object is present. Optional does not imply nullable.

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `user_id` | `integer` | Yes | The positive ID of the active workspace member to add. Minimum: 1. |
| `role` | `string` | No | The role in this team. If the request omits role, the API uses member. Default: "member". Allowed values: `"lead"` \| `"member"` |

Request JSON:

```json
{
  "user_id": 100,
  "role": "lead"
}
```

#### Success response

Status: `201 Created`

#### Response fields

Returned means the field is present when its parent object is present. A nullable field can contain null.

| Name | Type | Returned | Description |
| --- | --- | --- | --- |
| `member` | `object` | Always | The team membership returned after a create or update request. |
| `member.team_id` | `integer` | Always | The positive numeric ID of the team. Minimum: 1. |
| `member.user_id` | `integer` | Always | The positive numeric ID of the workspace member. Minimum: 1. |
| `member.role` | `string` | Always | The role in this team. Possible values: `"lead"` \| `"member"` |
| `member.created_at` | `string (RFC 3339)` | Always | The membership creation time. |

Returns the created or updated team membership.

```json
{
  "member": {
    "team_id": 42,
    "user_id": 100,
    "role": "lead",
    "created_at": "2025-01-30T12:00:00Z"
  }
}
```

#### Endpoint error responses

| Status | Meaning |
| --- | --- |
| `400` | The API rejected the request because its syntax or one of its values is invalid. |
| `401` | The request does not contain valid authentication. |
| `403` | Authentication succeeded, but the caller lacks the required API scope or workspace permission. |
| `404` | The requested resource does not exist or is not available to the caller. |
| `409` | The request conflicts with the current resource state. idempotency_key_reused means that the key belongs to different request content. idempotency_in_progress means that an identical request is still in progress. idempotency_outcome_unknown means that an earlier request may have completed, but its result could not be stored and must not be retried automatically. |
| `429` | The request exceeded an applicable sliding limit. Free API keys allow 20 requests per second and 600 requests per 60 seconds. Team API keys allow 100 requests per second and 3,000 requests per 60 seconds. Browser users allow 100 requests per second. Browser company allows 500 requests per second and 5,000 requests per 10 seconds. Browser company means the sum of user traffic for one company. Anonymous client IPs allow 20 requests per second and 200 requests per 10 seconds. The rate limiter does not count requests that it rejects. |
| `500` | An internal server error prevented completion of the request. |

### `PATCH /api/v1/teams/{id}/members/{userId}`

**Update team member.** Updates the team role for an active workspace member. If the team membership does not exist, the API returns 404.

Required scope: `teams:write`

Operation ID: `updateTeamMember`
Contract: `supported`
Retry guidance: If the result is unknown, read the resource before another attempt. The error details explain the next step.
Prerequisites: Active responder seat, Manage teams

#### Path parameters

Required nested fields apply when their parent object is present. Optional does not imply nullable.

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `integer` | Yes | A positive numeric team ID returned by this API. Minimum: 1. |
| `userId` | `integer` | Yes | A positive numeric user ID for an active workspace member. Minimum: 1. |

#### Example request

Replace `{company}`, `<token>`, and any other placeholders with your values.

```bash
curl --request PATCH \
  --url 'https://{company}.api.tellagen.com/api/v1/teams/{id}/members/{userId}' \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '{
  "role": "member"
}'
```

#### Request body

Required nested fields apply when their parent object is present. Optional does not imply nullable.

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `role` | `string` | Yes | The new team role. Allowed values: `"lead"` \| `"member"` |

Request JSON:

```json
{
  "role": "member"
}
```

#### Success response

Status: `200 OK`

#### Response fields

Returned means the field is present when its parent object is present. A nullable field can contain null.

| Name | Type | Returned | Description |
| --- | --- | --- | --- |
| `member` | `object` | Always | The team membership returned after a create or update request. |
| `member.team_id` | `integer` | Always | The positive numeric ID of the team. Minimum: 1. |
| `member.user_id` | `integer` | Always | The positive numeric ID of the workspace member. Minimum: 1. |
| `member.role` | `string` | Always | The role in this team. Possible values: `"lead"` \| `"member"` |
| `member.created_at` | `string (RFC 3339)` | Always | The membership creation time. |

Returns the updated team membership.

```json
{
  "member": {
    "team_id": 42,
    "user_id": 100,
    "role": "member",
    "created_at": "2025-01-10T09:00:00Z"
  }
}
```

#### Endpoint error responses

| Status | Meaning |
| --- | --- |
| `400` | The API rejected the request because its syntax or one of its values is invalid. |
| `401` | The request does not contain valid authentication. |
| `403` | Authentication succeeded, but the caller lacks the required API scope or workspace permission. |
| `404` | The requested resource does not exist or is not available to the caller. |
| `409` | The request conflicts with the current resource state. idempotency_key_reused means that the key belongs to different request content. idempotency_in_progress means that an identical request is still in progress. idempotency_outcome_unknown means that an earlier request may have completed, but its result could not be stored and must not be retried automatically. |
| `429` | The request exceeded an applicable sliding limit. Free API keys allow 20 requests per second and 600 requests per 60 seconds. Team API keys allow 100 requests per second and 3,000 requests per 60 seconds. Browser users allow 100 requests per second. Browser company allows 500 requests per second and 5,000 requests per 10 seconds. Browser company means the sum of user traffic for one company. Anonymous client IPs allow 20 requests per second and 200 requests per 10 seconds. The rate limiter does not count requests that it rejects. |
| `500` | An internal server error prevented completion of the request. |

### `DELETE /api/v1/teams/{id}/members/{userId}`

**Remove team member.** Removes one user from the team. The operation does not remove the user from the workspace.

Required scope: `teams:write`

Operation ID: `removeTeamMember`
Contract: `supported`
Retry guidance: If the result is unknown, read the resource before another attempt. The error details explain the next step.
Prerequisites: Active responder seat, Manage teams

#### Path parameters

Required nested fields apply when their parent object is present. Optional does not imply nullable.

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `integer` | Yes | A positive numeric team ID returned by this API. Minimum: 1. |
| `userId` | `integer` | Yes | A positive numeric user ID for an active workspace member. Minimum: 1. |

#### Example request

Replace `{company}`, `<token>`, and any other placeholders with your values.

```bash
curl --request DELETE \
  --url 'https://{company}.api.tellagen.com/api/v1/teams/{id}/members/{userId}' \
  --header 'Authorization: Bearer <token>'
```

#### Success response

Status: `204 No Content`

No response body.

#### Endpoint error responses

| Status | Meaning |
| --- | --- |
| `400` | The API rejected the request because its syntax or one of its values is invalid. |
| `401` | The request does not contain valid authentication. |
| `403` | Authentication succeeded, but the caller lacks the required API scope or workspace permission. |
| `404` | The requested resource does not exist or is not available to the caller. |
| `409` | The request conflicts with the current resource state. idempotency_key_reused means that the key belongs to different request content. idempotency_in_progress means that an identical request is still in progress. idempotency_outcome_unknown means that an earlier request may have completed, but its result could not be stored and must not be retried automatically. |
| `429` | The request exceeded an applicable sliding limit. Free API keys allow 20 requests per second and 600 requests per 60 seconds. Team API keys allow 100 requests per second and 3,000 requests per 60 seconds. Browser users allow 100 requests per second. Browser company allows 500 requests per second and 5,000 requests per 10 seconds. Browser company means the sum of user traffic for one company. Anonymous client IPs allow 20 requests per second and 200 requests per 10 seconds. The rate limiter does not count requests that it rejects. |
| `500` | An internal server error prevented completion of the request. |
