# Tellagen API Reference

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

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

## Endpoint index

### Writable configuration

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

- [`GET /api/v1/configuration/workflow-statuses`](https://tellagen.com/api-reference/configuration#configuration-endpoint-get-api-v1-configuration-workflow-statuses) — List available workflow statuses
- [`GET /api/v1/configuration/severities`](https://tellagen.com/api-reference/configuration#configuration-endpoint-get-api-v1-configuration-severities) — List available severities
- [`GET /api/v1/configuration/service-tiers`](https://tellagen.com/api-reference/configuration#configuration-endpoint-get-api-v1-configuration-service-tiers) — List available service tiers

### Incidents

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

- [`GET /api/v1/incidents`](https://tellagen.com/api-reference/incidents#incidents-endpoint-get-api-v1-incidents) — List incidents
- [`POST /api/v1/incidents`](https://tellagen.com/api-reference/incidents#incidents-endpoint-post-api-v1-incidents) — Create incident
- [`GET /api/v1/incidents/archived`](https://tellagen.com/api-reference/incidents#incidents-endpoint-get-api-v1-incidents-archived) — List archived incidents
- [`GET /api/v1/incident-references/{reference}`](https://tellagen.com/api-reference/incidents#incidents-endpoint-get-api-v1-incident-references-reference) — Get incident by permanent reference
- [`GET /api/v1/incidents/{id}`](https://tellagen.com/api-reference/incidents#incidents-endpoint-get-api-v1-incidents-id) — Get incident
- [`PATCH /api/v1/incidents/{id}`](https://tellagen.com/api-reference/incidents#incidents-endpoint-patch-api-v1-incidents-id) — Update incident
- [`POST /api/v1/incidents/{id}/resolve`](https://tellagen.com/api-reference/incidents#incidents-endpoint-post-api-v1-incidents-id-resolve) — Resolve incident
- [`POST /api/v1/incidents/{id}/close`](https://tellagen.com/api-reference/incidents#incidents-endpoint-post-api-v1-incidents-id-close) — Close incident without resolution
- [`POST /api/v1/incidents/{id}/reopen`](https://tellagen.com/api-reference/incidents#incidents-endpoint-post-api-v1-incidents-id-reopen) — Reopen incident
- [`GET /api/v1/incidents/{id}/provisioning`](https://tellagen.com/api-reference/incidents#incidents-endpoint-get-api-v1-incidents-id-provisioning) — Get incident integration setup
- [`POST /api/v1/incidents/{id}/provisioning/retry`](https://tellagen.com/api-reference/incidents#incidents-endpoint-post-api-v1-incidents-id-provisioning-retry) — Retry incident integration setup
- [`POST /api/v1/incidents/{id}/archive`](https://tellagen.com/api-reference/incidents#incidents-endpoint-post-api-v1-incidents-id-archive) — Archive incident
- [`POST /api/v1/incidents/{id}/restore`](https://tellagen.com/api-reference/incidents#incidents-endpoint-post-api-v1-incidents-id-restore) — Restore incident
- [`POST /api/v1/incidents/{id}/rename-slack-channel`](https://tellagen.com/api-reference/incidents#incidents-endpoint-post-api-v1-incidents-id-rename-slack-channel) — Reapply the Slack channel-name template (deprecated)
- [`POST /api/v1/incidents/{id}/slack/channel-name/preview`](https://tellagen.com/api-reference/incidents#incidents-endpoint-post-api-v1-incidents-id-slack-channel-name-preview) — Preview an incident Slack channel name
- [`PATCH /api/v1/incidents/{id}/slack/channel-name`](https://tellagen.com/api-reference/incidents#incidents-endpoint-patch-api-v1-incidents-id-slack-channel-name) — Update an incident Slack channel name

### Timeline

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

- [`GET /api/v1/incidents/{id}/timeline`](https://tellagen.com/api-reference/timeline#timeline-endpoint-get-api-v1-incidents-id-timeline) — List timeline events
- [`POST /api/v1/incidents/{id}/timeline`](https://tellagen.com/api-reference/timeline#timeline-endpoint-post-api-v1-incidents-id-timeline) — Create timeline event
- [`GET /api/v1/timeline/{id}`](https://tellagen.com/api-reference/timeline#timeline-endpoint-get-api-v1-timeline-id) — Get timeline event
- [`PATCH /api/v1/timeline/{id}`](https://tellagen.com/api-reference/timeline#timeline-endpoint-patch-api-v1-timeline-id) — Update timeline event
- [`DELETE /api/v1/timeline/{id}`](https://tellagen.com/api-reference/timeline#timeline-endpoint-delete-api-v1-timeline-id) — Delete timeline event

### Services

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

- [`GET /api/v1/services`](https://tellagen.com/api-reference/services#services-endpoint-get-api-v1-services) — List services
- [`POST /api/v1/services`](https://tellagen.com/api-reference/services#services-endpoint-post-api-v1-services) — Create service
- [`GET /api/v1/services/{id}`](https://tellagen.com/api-reference/services#services-endpoint-get-api-v1-services-id) — Get service
- [`PATCH /api/v1/services/{id}`](https://tellagen.com/api-reference/services#services-endpoint-patch-api-v1-services-id) — Update service
- [`DELETE /api/v1/services/{id}`](https://tellagen.com/api-reference/services#services-endpoint-delete-api-v1-services-id) — Archive service
- [`POST /api/v1/services/{id}/restore`](https://tellagen.com/api-reference/services#services-endpoint-post-api-v1-services-id-restore) — Restore service

### 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

### Custom Fields

[Custom Fields Markdown export](https://tellagen.com/api-reference/custom-fields/markdown)

- [`GET /api/v1/custom-fields`](https://tellagen.com/api-reference/custom-fields#custom-fields-endpoint-get-api-v1-custom-fields) — List custom fields
- [`POST /api/v1/custom-fields`](https://tellagen.com/api-reference/custom-fields#custom-fields-endpoint-post-api-v1-custom-fields) — Create custom field
- [`GET /api/v1/custom-fields/{id}`](https://tellagen.com/api-reference/custom-fields#custom-fields-endpoint-get-api-v1-custom-fields-id) — Get custom field
- [`PATCH /api/v1/custom-fields/{id}`](https://tellagen.com/api-reference/custom-fields#custom-fields-endpoint-patch-api-v1-custom-fields-id) — Update custom field
- [`DELETE /api/v1/custom-fields/{id}`](https://tellagen.com/api-reference/custom-fields#custom-fields-endpoint-delete-api-v1-custom-fields-id) — Archive custom field
- [`POST /api/v1/custom-fields/{id}/restore`](https://tellagen.com/api-reference/custom-fields#custom-fields-endpoint-post-api-v1-custom-fields-id-restore) — Restore custom field
- [`GET /api/v1/incidents/{id}/custom-fields`](https://tellagen.com/api-reference/custom-fields#custom-fields-endpoint-get-api-v1-incidents-id-custom-fields) — List incident custom-field values
- [`PATCH /api/v1/incidents/{id}/custom-fields`](https://tellagen.com/api-reference/custom-fields#custom-fields-endpoint-patch-api-v1-incidents-id-custom-fields) — Partially update incident custom-field values
- [`DELETE /api/v1/incidents/{id}/custom-fields/{fieldId}`](https://tellagen.com/api-reference/custom-fields#custom-fields-endpoint-delete-api-v1-incidents-id-custom-fields-fieldid) — Unset one incident custom-field value

## 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.

## Make your first request

Use a workspace API key to list incidents. This quickstart makes no changes and succeeds when the workspace has no incidents.

### Prerequisites

For a workspace at https://acme.tellagen.com, the API origin is https://acme.api.tellagen.com. Use acme as the company placeholder in the request URL.

You need your workspace slug, an API key with incidents:read, curl, and a POSIX-compatible shell. Keep the key out of source control and application logs.

- [Create an API key](https://tellagen.com/help/manage-api-keys)

### List incidents

Replace the company and token placeholders with your workspace slug and API key. For a local server, replace the API origin with its address.

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


### Read the response

A successful request returns HTTP 200. The JSON example below is a successful empty workspace response. Authentication failures return 401, and a valid key without incidents:read returns 403. If pagination.has_more is true, follow pagination.next_cursor as described in the pagination guide.

```json
{
  "incidents": [],
  "has_more": false,
  "pagination": {
    "limit": 50,
    "has_more": false
  }
}
```

- [Scopes](https://tellagen.com/api-reference#scopes)
- [401: Check authentication](https://tellagen.com/api-reference#error-status-401)
- [403: Check access](https://tellagen.com/api-reference#error-status-403)
- [Pagination guide](https://tellagen.com/api-reference/guides/pagination)

## Build reliable requests

Treat create retries, concurrent updates, and rate limits as separate cases. The API provides a specific signal for each one.

### Idempotent creates

Send Idempotency-Key on POST /api/v1/incidents, POST /api/v1/incidents/{id}/timeline, POST /api/v1/services, and POST /api/v1/teams. A key is retained for 24 hours and can replay only the identical request body. Generate a new key for a logically new create.

Attach the key on the first create attempt, before any network uncertainty. If the 24-hour retention has expired, do not assume the old key can replay: look up the intended resource first and use a new key only when you have established that a new create is required.

A reused key with different content returns idempotency_key_reused. idempotency_in_progress means the identical request is still being handled and may be retried after Retry-After. idempotency_outcome_unknown means the earlier operation may have completed; do not retry automatically. Reconcile by listing or fetching the resource, or ask an operator to decide.


### Revision-protected updates

API-key clients must send If-Tellagen-Resource-Version on PATCH /api/v1/incidents/{id}, PATCH /api/v1/timeline/{id}, PATCH /api/v1/services/{id}, PATCH /api/v1/teams/{id}, and PATCH /api/v1/custom-fields/{id}. Use the positive revision from the latest response or Tellagen-Resource-Version response header.

Those are the five revision-protected resource PATCH operations. PATCH /api/v1/teams/{id}/members/{userId}, PATCH /api/v1/incidents/{id}/custom-fields, and PATCH /api/v1/incidents/{id}/slack/channel-name use their operation-specific validation and do not take If-Tellagen-Resource-Version.

The token also needs the matching read scope to fetch the resource and its current revision before a protected PATCH. A 412 response means another writer changed the resource. Fetch the latest representation, compare your intended edit with the new state, and then either abandon it or send a reconciled PATCH with the new revision. Never overwrite automatically after a conflict.


### Rate limits and request identity

A 429 response reports the current quota in X-RateLimit-Limit and X-RateLimit-Remaining. Wait for Retry-After before retrying. Bound attempts and add jitter when many workers share one workspace key.

Send a unique X-Request-ID when tracing a workflow. The response echoes an accepted ID or supplies one; record it with the status and structured error code.

Write requests accept one JSON document no larger than 1 MiB. Invalid JSON, multiple JSON values, unknown properties, and oversized bodies fail before application logic runs.

- [Errors and recovery](https://tellagen.com/api-reference#errors)
- [Pagination and bounded retries](https://tellagen.com/api-reference/guides/pagination)

## Filter and paginate collections

Collection endpoints use a limit and an opaque cursor. Process each page once, preserve every filter, and stop when pagination.has_more is false.

### Filtering rules

The default limit is 50 and the maximum is 100. Repeating the same incident filter means OR: status=active&status=investigating accepts either status. Different filters combine with AND. Always URL-encode names and values.

Do not inspect, edit, or create cursor values. Send next_cursor exactly as returned, together with the same filters and limit. An empty items array is valid, including on the first page.


### Filtered curl request

This request accepts either repeated workflow status and restricts results to incidents created at or after the encoded RFC 3339 timestamp.

Replace the company and token placeholders with your workspace slug and API key.

```bash
curl --request GET \
  --url 'https://{company}.api.tellagen.com/api/v1/incidents' \
  --header 'Authorization: Bearer <token>' \
  --get \
  --data-urlencode 'limit=100' \
  --data-urlencode 'status=active' \
  --data-urlencode 'status=investigating' \
  --data-urlencode 'created_from=2026-08-31T12:00:00+03:00'
```


### Page response shapes

| Case | Response fields |
| --- | --- |
| Empty collection | "incidents": [], "pagination": {"limit": 100, "has_more": false} |
| Final non-empty page | "incidents": [{...}], "pagination": {"limit": 100, "has_more": false}. The final page can contain items; next_cursor is absent. |
| First or middle page | "incidents": [...], "pagination": {"limit": 100, "has_more": true, "next_cursor": "opaque-server-cursor"} |
| Active and archived incident collections | May also include restricted_count and history_limited, which describe matching incidents excluded by the workspace history limit. They do not count arbitrary permission-denied results. |


### Run the pagination example

Set TELLAGEN_COMPANY to your workspace slug and TELLAGEN_TOKEN to an API key with incidents:read. For a local server, set TELLAGEN_API_ORIGIN to its address; you can then omit TELLAGEN_COMPANY.

Choose Node.js 22 or later, or Go 1.24 or later. Both examples use the standard library and need no extra packages.

Set TELLAGEN_STATUSES to comma-separated status keys. Set TELLAGEN_CREATED_FROM to an RFC 3339 timestamp when you need a start-date filter.

Both examples read every page, preserve filters, and limit retries after 429. They warn on stderr when history limits make the export incomplete.


### Pagination example

#### Node.js

Save this code as pagination.mjs. Run it with node pagination.mjs.

```javascript
import { setTimeout as sleep } from "node:timers/promises";

async function main() {
  const token = process.env.TELLAGEN_TOKEN;
  if (!token) throw new Error("Set TELLAGEN_TOKEN.");
  let origin = process.env.TELLAGEN_API_ORIGIN;
  if (!origin) {
    const company = process.env.TELLAGEN_COMPANY;
    if (!company) throw new Error("Set TELLAGEN_COMPANY or TELLAGEN_API_ORIGIN.");
    origin = "https://" + company + ".api.tellagen.com";
  }
  origin = origin.replace(/\/+$/, "");

  async function getPage(url) {
    for (let attempt = 0; attempt < 4; attempt++) {
      const response = await fetch(url, {
        headers: { Authorization: "Bearer " + token },
        signal: AbortSignal.timeout(30_000),
      });
      const body = await response.text();
      if (response.status === 429 && attempt < 3) {
        const seconds = Number(response.headers.get("Retry-After") ?? "1");
        const delay = Number.isFinite(seconds) && seconds >= 0 ? seconds : 1;
        await sleep(delay * 1_000);
        continue;
      }
      if (!response.ok) throw new Error("HTTP " + response.status + ": " + body);
      return JSON.parse(body);
    }
  }

  const filters = new URLSearchParams({ limit: "100" });
  const statuses = (process.env.TELLAGEN_STATUSES ?? "").split(",");
  for (const status of statuses.map((value) => value.trim()).filter(Boolean)) {
    filters.append("status", status);
  }
  if (process.env.TELLAGEN_CREATED_FROM) {
    filters.set("created_from", process.env.TELLAGEN_CREATED_FROM);
  }
  const incidents = [];
  let cursor;
  let historyWarningShown = false;
  for (let pageNumber = 0; pageNumber < 1_000; pageNumber++) {
    const params = new URLSearchParams(filters);
    if (cursor) params.set("cursor", cursor);
    const page = await getPage(origin + "/api/v1/incidents?" + params);
    if (!historyWarningShown && (page.history_limited || page.restricted_count > 0)) {
      console.error(
        "Warning: workspace history limit applies; " +
        (page.restricted_count ?? 0) +
        " matching incidents excluded. This is not a complete export.",
      );
      historyWarningShown = true;
    }
    incidents.push(...(page.incidents ?? []));
    if (!page.pagination.has_more) {
      console.log(JSON.stringify(incidents));
      return;
    }
    cursor = page.pagination.next_cursor;
    if (!cursor) throw new Error("has_more was true without next_cursor");
  }
  throw new Error("Stopped after 1000 pages.");
}

main().catch((error) => {
  const token = process.env.TELLAGEN_TOKEN;
  const message = error instanceof Error ? error.message : String(error);
  console.error(token ? message.replaceAll(token, "[redacted]") : message);
  process.exitCode = 1;
});
```

#### Go

Save this code as pagination.go. Run it with go run pagination.go.

```go
package main

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"
)

type pageInfo struct {
	HasMore    bool   `json:"has_more"`
	NextCursor string `json:"next_cursor"`
}

type incidentPage struct {
	Incidents       []json.RawMessage `json:"incidents"`
	Pagination      pageInfo          `json:"pagination"`
	HistoryLimited  bool              `json:"history_limited"`
	RestrictedCount int               `json:"restricted_count"`
}

func main() {
	if err := run(); err != nil {
		fmt.Fprintln(os.Stderr, "pagination:", err)
		os.Exit(1)
	}
}

func run() error {
	token := strings.TrimSpace(os.Getenv("TELLAGEN_TOKEN"))
	if token == "" {
		return fmt.Errorf("TELLAGEN_TOKEN is required")
	}
	origin := strings.TrimSpace(os.Getenv("TELLAGEN_API_ORIGIN"))
	if origin == "" {
		company := strings.TrimSpace(os.Getenv("TELLAGEN_COMPANY"))
		if company == "" {
			return fmt.Errorf("TELLAGEN_COMPANY is required when TELLAGEN_API_ORIGIN is unset")
		}
		origin = "https://" + company + ".api.tellagen.com"
	}
	origin = strings.TrimRight(origin, "/")

	filters := url.Values{"limit": {"100"}}
	for _, status := range strings.Split(os.Getenv("TELLAGEN_STATUSES"), ",") {
		if status = strings.TrimSpace(status); status != "" {
			filters.Add("status", status)
		}
	}
	if createdFrom := strings.TrimSpace(os.Getenv("TELLAGEN_CREATED_FROM")); createdFrom != "" {
		filters.Set("created_from", createdFrom)
	}

	client := &http.Client{Timeout: 30 * time.Second}
	incidents := make([]json.RawMessage, 0)
	cursor := ""
	warned := false
	for pageNumber := 0; pageNumber < 1000; pageNumber++ {
		query := cloneValues(filters)
		if cursor != "" {
			query.Set("cursor", cursor)
		}
		pageURL := origin + "/api/v1/incidents?" + query.Encode()
		var page incidentPage
		for attempt := 0; attempt < 4; attempt++ {
			request, err := http.NewRequest(http.MethodGet, pageURL, nil)
			if err != nil {
				return err
			}
			request.Header.Set("Authorization", "Bearer "+token)
			response, err := client.Do(request)
			if err != nil {
				return err
			}
			body, readErr := io.ReadAll(response.Body)
			response.Body.Close()
			if readErr != nil {
				return readErr
			}
			if response.StatusCode == http.StatusTooManyRequests && attempt < 3 {
				delay, err := retryAfter(response.Header.Get("Retry-After"))
				if err != nil {
					return err
				}
				time.Sleep(delay)
				continue
			}
			if response.StatusCode < 200 || response.StatusCode >= 300 {
				return fmt.Errorf("GET %s returned %s: %s", pageURL, response.Status, strings.TrimSpace(string(body)))
			}
			if err := json.Unmarshal(body, &page); err != nil {
				return fmt.Errorf("decode page: %w", err)
			}
			break
		}

		incidents = append(incidents, page.Incidents...)
		if !warned && (page.HistoryLimited || page.RestrictedCount > 0) {
			fmt.Fprintf(os.Stderr, "warning: history limit applies; %d matching incidents excluded; this is not a complete export\n", page.RestrictedCount)
			warned = true
		}
		if !page.Pagination.HasMore {
			return json.NewEncoder(os.Stdout).Encode(incidents)
		}
		if page.Pagination.NextCursor == "" {
			return fmt.Errorf("has_more was true without next_cursor")
		}
		cursor = page.Pagination.NextCursor
	}
	return fmt.Errorf("stopped after 1000 pages")
}

func cloneValues(source url.Values) url.Values {
	copy := make(url.Values, len(source))
	for key, values := range source {
		copy[key] = append([]string(nil), values...)
	}
	return copy
}

func retryAfter(value string) (time.Duration, error) {
	seconds, err := strconv.Atoi(strings.TrimSpace(value))
	if err != nil || seconds < 0 {
		return 0, fmt.Errorf("invalid Retry-After %q", value)
	}
	return time.Duration(seconds) * time.Second, nil
}

```


### Other collections

The same pagination object is used by active and archived incidents, incident timeline events, services, teams, team members, custom fields, and incident custom-field values. Use the item property documented by that endpoint.

Incident pages have a stable endpoint-defined ordering, with incident ID used to break ties; archived incidents order by archived_at and incident ID. Cursor traversal is not a frozen snapshot: concurrent creates or updates can affect later pages, so consumers should tolerate duplicates and re-read important resources by ID.

- [Reliable requests](https://tellagen.com/api-reference#reliable-requests)
- [Workflow recipe](https://tellagen.com/api-reference/guides/workflows)

## Create and move an incident through its workflow

Discover workspace configuration before creating an incident, then carry returned IDs and revisions through later calls.

### Lifecycle recipe

1. List workflow statuses and select the configured default key.
2. List custom-field definitions. Supply a value with the correct type for every required_on_creation field.
3. Create the incident with a fresh Idempotency-Key and a short_title. Set create_slack_channel to false and invite_usergroups to an empty array when the workflow must not contact Slack.
4. Use incident.id for numeric-ID routes and incident.revision for the first protected PATCH.
5. Use resolve, close, and reopen operations for lifecycle transitions. Inspect each returned incident before the next change.


### Run the workflow example

Set TELLAGEN_COMPANY to your workspace slug and TELLAGEN_TOKEN to your API key. For a local server, set TELLAGEN_API_ORIGIN to its address; you can then omit TELLAGEN_COMPANY.

The key needs incidents:read, incidents:write, and custom_fields:read. Its owner needs an active responder seat.

Choose Node.js 22 or later, or Go 1.24 or later. Both examples use the standard library and need no extra packages.

Each example creates and resolves an incident. Run it in a workspace where you intend these changes.

The examples stop before creation when custom fields are required. They list the field IDs and types.

For a workspace with required fields, adapt the required-field guard and supply the values in custom_field_values before running the example.

After the timeline event, the example reads the incident again. This read supplies the current revision for resolution.


### Workflow example

#### Node.js

Save this code as workflow.mjs. Run it with node workflow.mjs.

```javascript
import crypto from "node:crypto";

async function main() {
  const token = process.env.TELLAGEN_TOKEN;
  if (!token) throw new Error("Set TELLAGEN_TOKEN.");
  let origin = process.env.TELLAGEN_API_ORIGIN;
  if (!origin) {
    const company = process.env.TELLAGEN_COMPANY;
    if (!company) throw new Error("Set TELLAGEN_COMPANY or TELLAGEN_API_ORIGIN.");
    origin = "https://" + company + ".api.tellagen.com";
  }
  origin = origin.replace(/\/+$/, "");

  async function request(method, path, body, extraHeaders = {}) {
    const response = await fetch(origin + path, {
      method,
      headers: {
        Authorization: "Bearer " + token,
        "Content-Type": "application/json",
        ...extraHeaders,
      },
      body: body === undefined ? undefined : JSON.stringify(body),
      signal: AbortSignal.timeout(30_000),
    });
    const text = await response.text();
    if (!response.ok) throw new Error("HTTP " + response.status + ": " + text);
    return JSON.parse(text);
  }

  const statuses = await request("GET", "/api/v1/configuration/workflow-statuses");
  const defaultStatus = statuses.workflow_statuses.find((status) => status.is_default);
  if (!defaultStatus) throw new Error("No default workflow status was returned.");

  async function listCustomFields() {
    const definitions = [];
    let cursor;
    for (let pageNumber = 0; pageNumber < 1_000; pageNumber++) {
      const params = new URLSearchParams({ limit: "100" });
      if (cursor) params.set("cursor", cursor);
      const page = await request("GET", "/api/v1/custom-fields?" + params);
      definitions.push(...page.fields);
      if (!page.pagination.has_more) return definitions;
      cursor = page.pagination.next_cursor;
      if (!cursor) throw new Error("has_more was true without next_cursor");
    }
    throw new Error("Stopped after 1000 custom-field pages.");
  }
  const definitions = await listCustomFields();
  const required = definitions.filter((field) => field.required_on_creation);
  if (required.length) {
    const summary = required.map((field) => field.id + " (" + field.field_type + ")").join(", ");
    throw new Error("Set custom_field_values for required field IDs before creating: " + summary);
  }

  // Keep each create key with its original body if you later reconcile a failed request.
  const createKey = "lifecycle-" + crypto.randomUUID();
  const created = await request("POST", "/api/v1/incidents", {
    title: "API lifecycle verification incident",
    short_title: "api-check",
    status: defaultStatus.key,
    create_slack_channel: false,
    invite_usergroups: [],
    custom_field_values: [],
  }, { "Idempotency-Key": createKey });
  const incident = created.incident;
  const path = "/api/v1/incidents/" + incident.id;
  await request("PATCH", path, {
    impact_summary: "Lifecycle example created this incident.",
  }, { "If-Tellagen-Resource-Version": String(incident.revision) });

  const timelineKey = "timeline-" + crypto.randomUUID();
  const timeline = await request("POST", path + "/timeline", {
    body: "Lifecycle example recorded this verification step.",
    type: "change",
  }, { "Idempotency-Key": timelineKey });

  // Timeline creation changes the incident revision. Read it before resolving.
  const current = await request("GET", path);
  await request("POST", path + "/resolve", {
    resolution_note: "Lifecycle example completed.",
    expected_revision: current.incident.revision,
  });
  const final = await request("GET", path);
  console.log(JSON.stringify({
    id: incident.id,
    revision: final.incident.revision,
    timeline_event_id: timeline.event.id,
  }));
}

main().catch((error) => {
  const token = process.env.TELLAGEN_TOKEN;
  const message = error instanceof Error ? error.message : String(error);
  console.error(token ? message.replaceAll(token, "[redacted]") : message);
  process.exitCode = 1;
});
```

#### Go

Save this code as workflow.go. Run it with go run workflow.go.

```go
package main

import (
	"bytes"
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"
)

type workflowStatus struct {
	Key       string `json:"key"`
	IsDefault bool   `json:"is_default"`
}

type workflowResponse struct {
	Statuses []workflowStatus `json:"workflow_statuses"`
}

type customField struct {
	ID                 int64  `json:"id"`
	FieldType          string `json:"field_type"`
	RequiredOnCreation bool   `json:"required_on_creation"`
}

type customFieldPage struct {
	Fields     []customField `json:"fields"`
	Pagination struct {
		HasMore    bool   `json:"has_more"`
		NextCursor string `json:"next_cursor"`
	} `json:"pagination"`
}

type incident struct {
	ID       int64 `json:"id"`
	Revision int64 `json:"revision"`
}

type incidentEnvelope struct {
	Incident incident `json:"incident"`
}

type timelineEnvelope struct {
	Event struct {
		ID int64 `json:"id"`
	} `json:"event"`
}

type client struct {
	origin string
	token  string
	http   *http.Client
}

func main() {
	if err := run(); err != nil {
		fmt.Fprintln(os.Stderr, "workflow:", err)
		os.Exit(1)
	}
}

func run() error {
	token := strings.TrimSpace(os.Getenv("TELLAGEN_TOKEN"))
	if token == "" {
		return fmt.Errorf("TELLAGEN_TOKEN is required")
	}
	origin := strings.TrimSpace(os.Getenv("TELLAGEN_API_ORIGIN"))
	if origin == "" {
		company := strings.TrimSpace(os.Getenv("TELLAGEN_COMPANY"))
		if company == "" {
			return fmt.Errorf("TELLAGEN_COMPANY is required when TELLAGEN_API_ORIGIN is unset")
		}
		origin = "https://" + company + ".api.tellagen.com"
	}
	c := &client{origin: strings.TrimRight(origin, "/"), token: token, http: &http.Client{Timeout: 30 * time.Second}}

	var workflows workflowResponse
	if err := c.do(http.MethodGet, "/api/v1/configuration/workflow-statuses", nil, "", &workflows); err != nil {
		return err
	}
	defaultStatus := ""
	for _, status := range workflows.Statuses {
		if status.IsDefault {
			defaultStatus = status.Key
			break
		}
	}
	if defaultStatus == "" {
		return fmt.Errorf("workspace returned no default workflow status")
	}

	fields := make([]customField, 0)
	cursor := ""
	for pageNumber := 0; pageNumber < 1000; pageNumber++ {
		query := url.Values{"limit": {"100"}}
		if cursor != "" {
			query.Set("cursor", cursor)
		}
		var page customFieldPage
		if err := c.do(http.MethodGet, "/api/v1/custom-fields?"+query.Encode(), nil, "", &page); err != nil {
			return err
		}
		fields = append(fields, page.Fields...)
		if !page.Pagination.HasMore {
			break
		}
		if page.Pagination.NextCursor == "" {
			return fmt.Errorf("custom fields has_more was true without next_cursor")
		}
		cursor = page.Pagination.NextCursor
		if pageNumber == 999 {
			return fmt.Errorf("stopped after 1000 custom-field pages")
		}
	}
	missing := make([]string, 0)
	for _, field := range fields {
		if field.RequiredOnCreation {
			missing = append(missing, fmt.Sprintf("%d (%s)", field.ID, field.FieldType))
		}
	}
	if len(missing) > 0 {
		return fmt.Errorf("set custom_field_values for required field IDs: %s", strings.Join(missing, ", "))
	}

	createKey, err := uniqueKey("lifecycle")
	if err != nil {
		return err
	}
	createBody := map[string]any{
		"title": "API lifecycle verification incident", "short_title": "api-check",
		"status": defaultStatus, "create_slack_channel": false,
		"invite_usergroups": []string{}, "custom_field_values": []any{},
	}
	var created incidentEnvelope
	if err := c.do(http.MethodPost, "/api/v1/incidents", createBody, createKey, &created); err != nil {
		return err
	}
	id := created.Incident.ID
	revision := created.Incident.Revision

	var updated incidentEnvelope
	if err := c.doWithHeaders(http.MethodPatch, fmt.Sprintf("/api/v1/incidents/%d", id), map[string]any{
		"impact_summary": "Lifecycle example created this incident.",
	}, map[string]string{"If-Tellagen-Resource-Version": fmt.Sprint(revision)}, &updated); err != nil {
		return err
	}
	revision = updated.Incident.Revision

	timelineKey, err := uniqueKey("timeline")
	if err != nil {
		return err
	}
	var timeline timelineEnvelope
	if err := c.do(http.MethodPost, fmt.Sprintf("/api/v1/incidents/%d/timeline", id), map[string]any{
		"body": "Lifecycle example recorded this verification step.", "type": "change",
	}, timelineKey, &timeline); err != nil {
		return err
	}
	var afterTimeline incidentEnvelope
	if err := c.do(http.MethodGet, fmt.Sprintf("/api/v1/incidents/%d", id), nil, "", &afterTimeline); err != nil {
		return err
	}
	revision = afterTimeline.Incident.Revision

	var resolved incidentEnvelope
	if err := c.do(http.MethodPost, fmt.Sprintf("/api/v1/incidents/%d/resolve", id), map[string]any{
		"resolution_note": "Lifecycle example completed.", "expected_revision": revision,
	}, "", &resolved); err != nil {
		return err
	}

	var final incidentEnvelope
	if err := c.do(http.MethodGet, fmt.Sprintf("/api/v1/incidents/%d", id), nil, "", &final); err != nil {
		return err
	}
	return json.NewEncoder(os.Stdout).Encode(struct {
		ID              int64 `json:"id"`
		Revision        int64 `json:"revision"`
		TimelineEventID int64 `json:"timeline_event_id"`
	}{id, final.Incident.Revision, timeline.Event.ID})
}

func (c *client) do(method, path string, body any, idempotencyKey string, target any) error {
	headers := map[string]string{}
	if idempotencyKey != "" {
		headers["Idempotency-Key"] = idempotencyKey
	}
	return c.doWithHeaders(method, path, body, headers, target)
}

func (c *client) doWithHeaders(method, path string, body any, headers map[string]string, target any) error {
	var reader io.Reader
	if body != nil {
		encoded, err := json.Marshal(body)
		if err != nil {
			return err
		}
		reader = bytes.NewReader(encoded)
	}
	request, err := http.NewRequest(method, c.origin+path, reader)
	if err != nil {
		return err
	}
	// Prevent automatic replay of write bodies after a connection failure.
	request.GetBody = nil
	request.Header.Set("Authorization", "Bearer "+c.token)
	if body != nil {
		request.Header.Set("Content-Type", "application/json")
	}
	for name, value := range headers {
		request.Header.Set(name, value)
	}
	response, err := c.http.Do(request)
	if err != nil {
		return err
	}
	defer response.Body.Close()
	responseBody, err := io.ReadAll(response.Body)
	if err != nil {
		return err
	}
	if response.StatusCode < 200 || response.StatusCode >= 300 {
		return fmt.Errorf("%s %s returned %s: %s", method, path, response.Status, strings.TrimSpace(string(responseBody)))
	}
	if target == nil || len(responseBody) == 0 {
		return nil
	}
	if err := json.Unmarshal(responseBody, target); err != nil {
		return fmt.Errorf("decode %s %s: %w", method, path, err)
	}
	return nil
}

func uniqueKey(prefix string) (string, error) {
	bytes := make([]byte, 16)
	if _, err := rand.Read(bytes); err != nil {
		return "", fmt.Errorf("generate idempotency key: %w", err)
	}
	return prefix + "-" + hex.EncodeToString(bytes), nil
}

```


### Workflow, archive, and references

A workflow status describes operational state. Resolve changes an incident to resolved and its issue type to postmortem; close ends it without marking it resolved; reopen returns a resolved or closed incident to active. Archive controls normal collection visibility and editability. It is a separate action from workflow status.

Numeric ID routes use incident.id. GET /api/v1/incident-references/{reference} accepts a permanent reference from Tellagen links and messages, not a numeric ID. That opaque reference continues to identify the incident after editable fields change.

When incident creation includes doc_url and import_source_timeline_event, inspect source_timeline_import. Its status can be imported, failed, unavailable, invalid, or unsupported; HTTP 201 alone does not prove that evidence import succeeded.

- [Custom-field values](https://tellagen.com/api-reference/guides/field-values)
- [Reliable requests](https://tellagen.com/api-reference#reliable-requests)

## Send custom-field values

Read each custom-field definition before writing a value. field_type selects the JSON shape, while config can narrow lengths, numeric ranges, currencies, and allowed options.

### Value type matrix

| field_type | JSON example | Validation |
| --- | --- | --- |
| string | "enterprise" | JSON string; config.max_length can limit Unicode characters. |
| number | 12.25 | Finite JSON number; config min, max, and precision can apply. |
| boolean | true | JSON true or false. |
| date | "2026-08-31" | String in exact YYYY-MM-DD form. |
| time | "17:45:30" | String in HH:MM or HH:MM:SS form. |
| datetime | "2026-08-31T17:45:30+03:00" | RFC 3339 string with a time-zone designator. |
| monetary | {"amount": 12.25, "currency": "EUR"} | Object with amount number and three-letter uppercase currency allowed by config. |
| json | {"nested": [1, true]} | Any valid JSON value. |
| select | "gold" | One option value, unless config.allow_custom permits another string. |
| multi_select | ["gold", "silver"] | Array of unique string option values; an empty array is valid. |


### Create and update values

The IDs 50 and 51 below are illustrative. Replace them with actual field IDs from your workspace. Match each value and currency to its field definition.

On incident creation, custom_field_values is an array of {field_id, value}. The field ID is numeric. Include every definition marked required_on_creation; the API validates all values before Slack work and saves them with the incident in one database transaction.

To change stored values, PATCH /api/v1/incidents/{id}/custom-fields with a non-empty values array. Omitted fields stay unchanged. A repeated request that sends the same values is safe.

```json
{
  "custom_field_values": [
    {"field_id": 50, "value": 1500},
    {"field_id": 51, "value": {"amount": 12.25, "currency": "EUR"}}
  ]
}
```


### Clear or unset a value

JSON null is accepted for a non-required field, but a required field rejects null. To remove a stored value explicitly, call DELETE /api/v1/incidents/{id}/custom-fields/{fieldId}. That request is repeat-safe when no value is stored, and it returns the remaining values. A required field cannot be unset.

An empty string, false, zero, an empty object, and an empty array are JSON values rather than universal clearing signals. They are accepted only when the field type and its config allow them.

- [Workflow recipe](https://tellagen.com/api-reference/guides/workflows)
- [Errors and recovery](https://tellagen.com/api-reference#errors)

## 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. |

## 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`.

## Writable configuration

Lists the workspace-specific keys accepted by incident and service requests. These endpoints also identify the default status, severity, and service tier.

### `GET /api/v1/configuration/workflow-statuses`

**List available workflow statuses.** Returns the workflow statuses that incident requests can use. The response lists statuses in workflow order. Exactly one status has is_default set to true. If a create-incident request omits status, the API uses that status.

Required scope: `incidents:read`

Operation ID: `listWorkflowStatuses`
Contract: `supported`
Retry guidance: This operation is repeat-safe. Respect Retry-After and use bounded retries.
Prerequisites: None

#### Example request

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

```bash
curl --request GET \
  --url 'https://{company}.api.tellagen.com/api/v1/configuration/workflow-statuses' \
  --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 |
| --- | --- | --- | --- |
| `workflow_statuses` | `object[]` | Always | The workflow statuses in configured workflow order. |
| `workflow_statuses[].key` | `string` | Always | A workflow-status key accepted by incident requests. |
| `workflow_statuses[].label` | `string` | Always | The status name shown to users. |
| `workflow_statuses[].order` | `integer` | Always | The zero-based position of the status in the workflow. Minimum: 0. |
| `workflow_statuses[].is_default` | `boolean` | Always | Identifies the default workflow status for new incidents. |
| `workflow_statuses[].is_terminal` | `boolean` | Always | Indicates whether this status ends the active incident workflow. |
| `workflow_statuses[].is_escalation_threshold` | `boolean` | Always | Indicates whether this status is the configured escalation threshold. |
| `workflow_statuses[].color` | `string` | Always | The color token used to display the status. |

Each entry contains a status key, label, display order, default flag, lifecycle flags, and color.

```json
{
  "workflow_statuses": [
    {
      "key": "open",
      "label": "Open",
      "order": 0,
      "is_default": true,
      "is_terminal": false,
      "is_escalation_threshold": true,
      "color": "ember"
    },
    {
      "key": "resolved",
      "label": "Resolved",
      "order": 1,
      "is_default": false,
      "is_terminal": true,
      "is_escalation_threshold": false,
      "color": "moss"
    }
  ]
}
```

#### 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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

### `GET /api/v1/configuration/severities`

**List available severities.** Returns the severity levels that incident requests can use. The response lists severities from the highest score threshold to the lowest. Exactly one severity has is_default set to true. If a create-incident request omits severity, the API uses that severity.

Required scope: `incidents:read`

Operation ID: `listSeverities`
Contract: `supported`
Retry guidance: This operation is repeat-safe. Respect Retry-After and use bounded retries.
Prerequisites: None

#### Example request

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

```bash
curl --request GET \
  --url 'https://{company}.api.tellagen.com/api/v1/configuration/severities' \
  --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 |
| --- | --- | --- | --- |
| `severities` | `object[]` | Always | The severity levels from the highest score threshold to the lowest. |
| `severities[].key` | `string` | Always | A severity key accepted by incident requests. |
| `severities[].label` | `string` | Always | The severity name shown to users. |
| `severities[].order` | `integer` | Always | The zero-based position of the severity in the response. Minimum: 0. |
| `severities[].is_default` | `boolean` | Always | Identifies the default severity for new incidents. |
| `severities[].min_score` | `number` | Always | The minimum calculated score for this severity. Minimum: 0. |
| `severities[].description` | `string` | Always | The workspace description of this severity. |
| `severities[].color` | `string` | Always | The color token used to display the severity. |

Each entry contains a severity key, label, display order, default flag, score threshold, description, and color.

```json
{
  "severities": [
    {
      "key": "sev1",
      "label": "SEV1",
      "order": 0,
      "is_default": false,
      "min_score": 300,
      "description": "Critical - complete outage",
      "color": "ember"
    },
    {
      "key": "sev2",
      "label": "SEV2",
      "order": 1,
      "is_default": true,
      "min_score": 150,
      "description": "Major - degraded experience",
      "color": "amber-500"
    }
  ]
}
```

#### 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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

### `GET /api/v1/configuration/service-tiers`

**List available service tiers.** Returns the service tiers that service requests can use. The response lists tiers in configured rank order. Exactly one tier has is_default set to true. If a create-service request omits tier, the API uses that tier.

Required scope: `services:read`

Operation ID: `listServiceTiers`
Contract: `supported`
Retry guidance: This operation is repeat-safe. Respect Retry-After and use bounded retries.
Prerequisites: None

#### Example request

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

```bash
curl --request GET \
  --url 'https://{company}.api.tellagen.com/api/v1/configuration/service-tiers' \
  --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 |
| --- | --- | --- | --- |
| `service_tiers` | `object[]` | Always | The service tiers in configured rank order. |
| `service_tiers[].key` | `string` | Always | A service-tier key accepted by service requests. |
| `service_tiers[].label` | `string` | Always | The tier name shown to users. |
| `service_tiers[].order` | `integer` | Always | The zero-based position of the tier in configured rank order. Minimum: 0. |
| `service_tiers[].is_default` | `boolean` | Always | Identifies the default tier for new services. |
| `service_tiers[].description` | `string` | Always | The workspace description of this service tier. |

Each entry contains a tier key, label, display order, default flag, and description.

```json
{
  "service_tiers": [
    {
      "key": "t0",
      "label": "T0 - Mission Critical",
      "order": 0,
      "is_default": false,
      "description": "Mission critical"
    },
    {
      "key": "t2",
      "label": "T2 - Important",
      "order": 2,
      "is_default": true,
      "description": "Important"
    }
  ]
}
```

#### 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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

## Incidents

Creates, lists, reads, updates, archives, restores, and changes the lifecycle state of incidents.

### `GET /api/v1/incidents`

**List incidents.** Returns one page of active incidents. The default page size is 50, and the maximum is 100. Values repeated within one set filter combine with OR; different filters combine with AND. Filters apply before pagination and also apply to restricted_count. Omitted sort keeps the legacy created_at-descending order. Extended collection queries use opaque v3 cursors bound to the company, normalized predicates, resolved metadata, and exact typed sort key. Legacy v1/v2 cursors remain accepted only with the legacy default order and legacy-compatible predicates. Pagination is deterministic keyset traversal for unchanged rows, not a cross-request database snapshot.

Required scope: `incidents:read`

Operation ID: `listIncidents`
Contract: `supported`
Retry guidance: This operation is repeat-safe. Respect Retry-After and use bounded retries.
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. |
| `scope` | `string` | No | Selects all incidents or only postmortems. The default is all. Default: "all". Allowed values: `"all"` \| `"postmortem"` |
| `status` | `string[]` | No | Includes incidents with any supplied workflow status. Repeating status supplies more than one accepted value. |
| `severity` | `string[]` | No | Includes incidents with any supplied severity key. Repeating severity supplies more than one accepted value. |
| `service` | `string[]` | No | Includes incidents with any supplied service slug. Repeating service supplies more than one accepted value. |
| `team_id` | `integer[]` | No | Includes incidents with any supplied owning-team ID. Repeating team_id supplies more than one accepted value. Each ID must be positive. |
| `created_from` | `string (RFC 3339)` | No | Includes incidents created at or after this RFC 3339 timestamp. |
| `created_to` | `string (RFC 3339)` | No | Includes incidents created at or before this RFC 3339 timestamp. |
| `created_before` | `string (RFC 3339)` | No | Includes incidents created strictly before this RFC 3339 timestamp. It cannot be combined with created_to. |
| `region` | `string[]` | No | Includes incidents with any supplied case-insensitive region. Repeating region supplies more than one accepted value. Maximum items: 100. |
| `lifecycle` | `string[]` | No | Includes any supplied incident lifecycle: ticket, incident, postmortem, or alert. Maximum items: 100. Allowed values: `"ticket"` \| `"incident"` \| `"postmortem"` \| `"alert"` |
| `service_tier` | `string[]` | No | Includes incidents whose canonical tenant service has any supplied tier. Maximum items: 100. Allowed values: `"t0"` \| `"t1"` \| `"t2"` \| `"t3"` |
| `effective_team_id` | `integer[]` | No | Includes incidents assigned directly to any supplied team or inheriting it from the canonical service. Repeating the parameter supplies more than one accepted positive ID. Maximum items: 100. |
| `unassigned_team` | `boolean` | No | When true, also includes incidents with no direct or canonical-service team. Default: false. |
| `mine` | `boolean` | No | When true, includes incidents assigned to the authenticated user, or created by that user when no active assignee exists. API-key-only authentication cannot use this filter. Default: false. |
| `active_only` | `boolean` | No | When true, excludes workflow stages configured as terminal. False does not add a status restriction. |
| `status_scope` | `string` | No | Selects all statuses, nonterminal ongoing statuses, or one configured workflow-stage slug. Minimum length: 1 characters. Maximum length: 2048 characters. |
| `diagnosis` | `string[]` | No | Includes incidents in any supplied diagnosis state. Repeating diagnosis supplies more than one accepted value. Maximum items: 100. Allowed values: `"not_run"` \| `"in_progress"` \| `"completed"` \| `"needs_attention"` \| `"unavailable"` |
| `sort` | `string` | No | Selects the collection sort field. Custom requires sort_custom_field. Omission keeps legacy created_at order. Allowed values: `"created_at"` \| `"last_event_at"` \| `"severity"` \| `"service"` \| `"diagnosis"` \| `"case"` \| `"regions"` \| `"team"` \| `"impact"` \| `"status"` \| `"owner"` \| `"events"` \| `"custom"` |
| `direction` | `string` | No | Sort direction for both the selected key and incident-ID tiebreaker. Default: "desc". Allowed values: `"asc"` \| `"desc"` |
| `sort_custom_field` | `string` | No | Active custom-field slug used when sort is custom. The server resolves and validates its stored type. Minimum length: 1 characters. Maximum length: 64 characters. |
| `custom_filter` | `string[]` | No | A JSON object with slug, operator, and value. Repeat for up to 32 predicates. Values remain typed JSON; arrays preserve choices containing commas. Supported operators depend on the resolved field type: string is/is_not/contains/starts_with; select is/is_not; boolean is; number and monetary eq/gt/lt/gte/lte/between; date and datetime is/before/after/between/gte/lte; time is/before/after; multiselect includes_any/includes_all/excludes. SQL fragments and client-supplied field types are rejected. Maximum items: 32. |
| `include_counts` | `boolean` | No | When true, returns authoritative matched, all, ongoing, and per-status counts before cursor and limit. Default: false. |
| `impact_status` | `string[]` | No | Includes incidents with any supplied customer-impact status. Allowed values: `"unknown"` \| `"none"` \| `"confirmed"` |
| `affected_customers_op` | `string` | No | Selects the operator for the affected-customer comparison. This parameter and affected_customers_value must appear together. Allowed values: `"<"` \| `">"` \| `"="` |
| `affected_customers_value` | `integer` | No | The non-negative affected-customer count for comparison. This parameter and affected_customers_op must appear together. Minimum: 0. |
| `mrr_op` | `string` | No | Selects the operator for the MRR comparison. This parameter and mrr_value must appear together. Allowed values: `"<"` \| `">"` \| `"="` |
| `mrr_value` | `integer` | No | The non-negative MRR amount for comparison, in minor currency units. This parameter and mrr_op must appear together. Minimum: 0. |
| `mrr_currency` | `string` | No | A three-letter ISO currency code. If mrr_basis is source, this parameter is required. Pattern: ^[A-Z]{3}$. |
| `mrr_basis` | `string` | No | Selects normalized workspace currency values or original source currency values. The default is normalized. Default: "normalized". Allowed values: `"normalized"` \| `"source"` |

#### Example request

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

```bash
curl --request GET \
  --url 'https://{company}.api.tellagen.com/api/v1/incidents' \
  --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 |
| --- | --- | --- | --- |
| `incidents` | `object[]` | Always | The incidents in this page. |
| `incidents[].id` | `integer` | Always | The positive numeric ID of the incident. Minimum: 1. |
| `incidents[].revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `incidents[].company_id` | `integer` | Always | The ID of the workspace that owns the incident. Minimum: 1. |
| `incidents[].human_id` | `string` | Always | The permanent incident reference shown to users. |
| `incidents[].title` | `string` | Always | The editable display title of the incident. |
| `incidents[].service` | `string` | Always | The service slug associated with the incident. |
| `incidents[].regions` | `string[]` | Always | The affected regions. |
| `incidents[].severity` | `string` | Always | The configured severity key. |
| `incidents[].slack_channel_id` | `string` | Always | The linked Slack channel ID. An empty string means that no channel is linked. |
| `incidents[].meet_url` | `string` | Always | The incident meeting URL. An empty string means that no meeting is linked. |
| `incidents[].doc_url` | `string` | Always | The incident document or source URL. An empty string means that no URL is stored. |
| `incidents[].status` | `string` | Always | The configured workflow-status key. |
| `incidents[].status_changed_at` | `string (RFC 3339) \| null` | Always | The time of the latest workflow-status change. A null value means that this time is unavailable. |
| `incidents[].last_event_at` | `string (RFC 3339) \| null` | Always | The start time of the latest timeline event. A null value means that no event exists. |
| `incidents[].last_event_summary` | `string` | Always | A summary of the latest timeline event. |
| `incidents[].issue_type` | `string` | Always | The work type. Possible values: `"ticket"` \| `"incident"` \| `"postmortem"` |
| `incidents[].severity_mode` | `string` | Always | The severity selection mode. Possible values: `"auto"` \| `"manual"` |
| `incidents[].impact_summary` | `string` | Always | The current customer or operational impact summary. |
| `incidents[].customer_impact_status` | `string` | Always | The customer-impact assessment. Possible values: `"unknown"` \| `"none"` \| `"confirmed"` |
| `incidents[].impact_started_at` | `string (RFC 3339) \| null` | Always | The impact start time. A null value means that the time is unknown. |
| `incidents[].first_reported_at` | `string (RFC 3339) \| null` | Always | The time of the first incident report. A null value means that the time is unknown. |
| `incidents[].first_responded_at` | `string (RFC 3339) \| null` | Always | The time of the first response. A null value means that the time is unknown. |
| `incidents[].recovered_at` | `string (RFC 3339) \| null` | Always | The service recovery time. A null value means that recovery is not recorded. |
| `incidents[].duration_seconds` | `integer` | Always | The calculated incident duration in whole seconds. Minimum: 0. |
| `incidents[].duration_anchor` | `string` | Always | The timestamp field from which the API calculates duration. |
| `incidents[].team_id` | `integer \| null` | Always | The owning-team ID. A null value means that the incident has no owning team. |
| `incidents[].event_count` | `integer` | Always | The number of timeline events for the incident. Minimum: 0. |
| `incidents[].investigation_summary` | `object \| null` | Always | A summary of investigation runs. A null value means that no investigation data exists. Must match exactly one of 2 schema variants. |
| `incidents[].investigation_summary.run_count` | `integer` | Always | The total number of investigation runs. Minimum: 0. |
| `incidents[].investigation_summary.latest_run` | `object \| null` | Always | The most recent investigation run. A null value means that no run exists. Must match exactly one of 2 schema variants. |
| `incidents[].investigation_summary.latest_run.status` | `string` | Always | The current investigation-run status. |
| `incidents[].investigation_summary.latest_run.started_at` | `string (RFC 3339)` | Always | The investigation-run start time. |
| `incidents[].investigation_summary.latest_run.completed_at` | `string (RFC 3339)` | Optional | The investigation-run completion time. |
| `incidents[].created_at` | `string (RFC 3339)` | Always | The incident creation time. |
| `incidents[].created_by` | `string` | Always | The identifier of the user who created the incident. |
| `incidents[].custom_fields` | `object` | Always | The incident values keyed by custom-field slug. |
| `incidents[].archived_at` | `string (RFC 3339) \| null` | Always | The incident archive time. A null value means that the incident is active. |
| `incidents[].archived_by` | `integer \| null` | Always | The user ID that archived the incident. A null value means that the incident is active. |
| `incidents[].incident_manager` | `string` | Always | The user identifier of the incident manager. An empty string means that no manager is assigned. |
| `incidents[].incident_responders` | `string[]` | Always | The user identifiers of assigned responders. |
| `incidents[].has_assignees` | `boolean` | Always | Indicates whether the incident has a manager or at least one responder. |
| `has_more` | `boolean` | Always | Deprecated top-level alias for pagination.has_more. |
| `next_cursor` | `string` | Optional | Deprecated top-level alias for pagination.next_cursor. |
| `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. |
| `restricted_count` | `integer` | Optional | The number of matching incidents excluded by the workspace history limit. Minimum: 0. |
| `history_limited` | `boolean` | Optional | Indicates whether the workspace history limit applies to this response. |
| `mrr_currency` | `string` | Optional | The workspace reporting currency used for normalized MRR values. |
| `mrr_conversion_mode` | `string` | Optional | The workspace mode for MRR currency conversion. Possible values: `"normalized"` \| `"source_only"` |
| `mrr_projection_state` | `string` | Optional | The MRR projection state. Possible values: `"ready"` \| `"stale"` \| `"unavailable"` |
| `supported_mrr_currencies` | `string[]` | Optional | The ISO currency codes supported by MRR filters. |
| `collection_counts` | `object` | Optional | Complete collection counts before cursor and limit when include_counts is true. |
| `collection_counts.matched` | `integer` | Always | Count after every predicate, including status selection. Minimum: 0. |
| `collection_counts.all` | `integer` | Always | Count after non-status predicates, excluding status, status_scope, and active_only. Minimum: 0. |
| `collection_counts.ongoing` | `integer` | Always | Nonterminal count after non-status predicates. Minimum: 0. |
| `collection_counts.by_status` | `object` | Always | Exact counts by normalized stored status after non-status predicates. |

Returns incidents and pagination fields. If has_more is true, next_cursor is the cursor for the next request. When include_counts is true, collection_counts describes the complete filtered collection before cursor and limit. Nullable fields are always present. Empty arrays and empty custom_fields objects are also present.

```json
{
  "incidents": [
    {
      "id": 1,
      "revision": 7,
      "company_id": 7,
      "human_id": "INC-2025-001",
      "title": "Payments API requests timing out",
      "service": "payments",
      "regions": [
        "us-east-1"
      ],
      "severity": "sev1",
      "slack_channel_id": "C08ABC123",
      "meet_url": "https://meet.google.com/abc-defg-hij",
      "doc_url": "https://docs.google.com/d/1abc",
      "status": "active",
      "status_changed_at": "2025-01-30T12:05:00Z",
      "last_event_at": "2025-01-30T12:10:00Z",
      "last_event_summary": "Investigating root cause",
      "issue_type": "incident",
      "severity_mode": "manual",
      "impact_summary": "Payment processing delayed for EU customers",
      "customer_impact_status": "confirmed",
      "impact_started_at": "2025-01-30T11:55:00Z",
      "first_reported_at": "2025-01-30T11:58:00Z",
      "first_responded_at": "2025-01-30T12:00:00Z",
      "recovered_at": null,
      "duration_seconds": 900,
      "duration_anchor": "impact_started_at",
      "team_id": 42,
      "event_count": 5,
      "investigation_summary": null,
      "created_at": "2025-01-30T12:00:00Z",
      "created_by": "oncall@example.com",
      "custom_fields": {},
      "archived_at": null,
      "archived_by": null,
      "incident_manager": "oncall@example.com",
      "incident_responders": [
        "responder@example.com"
      ],
      "has_assignees": true
    }
  ],
  "has_more": true,
  "next_cursor": "opaque-server-cursor",
  "pagination": {
    "limit": 50,
    "has_more": true,
    "next_cursor": "opaque-server-cursor"
  },
  "collection_counts": {
    "matched": 12,
    "all": 20,
    "ongoing": 8,
    "by_status": {
      "active": 8,
      "resolved": 12
    }
  }
}
```

#### 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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

### `POST /api/v1/incidents`

**Create incident.** The API validates every field before it creates the incident. Unknown JSON fields return 400. The API trims text and removes duplicate regions. It rejects values that exceed their length limits. It validates the team before saving the incident. The incident, declaration history, changelog, and outgoing setup work commit together. If Slack is configured and create_slack_channel is true, a durable worker creates the channel after the incident is saved. Use getIncidentProvisioning to inspect setup state and retryIncidentProvisioning to retry failed steps.

Required scope: `incidents:write`

Operation ID: `createIncident`
Contract: `supported`
Retry guidance: Retry an identical request with the same Idempotency-Key used on the first attempt, within 24 hours. Never retry an unknown outcome automatically.
Prerequisites: None

#### 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/incidents' \
  --header 'Authorization: Bearer <token>' \
  --header 'Idempotency-Key: <idempotency-key>' \
  --header 'Content-Type: application/json' \
  --data '{
  "title": "Payments API requests timing out",
  "impact_summary": "EU customers cannot complete payment authorization",
  "create_slack_channel": false
}'
```

#### Request body

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

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `title` | `string` | No | The incident display title. The maximum length is 160 characters. If title is omitted, the API derives it from impact_summary or service. Maximum length: 160 characters. |
| `service` | `string` | No | The service slug. The maximum length is 128 characters. Maximum length: 128 characters. |
| `regions` | `string[]` | No | The affected regions. The API trims each value and removes duplicates. |
| `severity` | `string` | No | A severity key from the workspace configuration. If severity is omitted, the API uses the configured default severity. |
| `short_title` | `string` | Conditional | A short slug for the incident reference and Slack channel name. The API normalizes it and limits it to 12 characters. If short_title_required is true, this field is required. This field is not the incident title. Required when: Required when the workspace configuration enables short titles. Maximum length: 12 characters. |
| `impact_summary` | `string` | No | The customer or operational impact summary. This field is separate from title and can change independently. |
| `declaration_evidence` | `string` | No | Alert or log text for the incident_declared timeline event. This field records responder evidence and does not set customer_impact_status. |
| `doc_url` | `string` | No | An absolute HTTP or HTTPS URL stored as an incident reference. The API can import a supported initial report as a separate timeline event. |
| `import_source_timeline_event` | `boolean` | Conditional | If this value is true, the API imports a supported Slack or PagerDuty doc_url as a timeline event. This value requires doc_url. A signed Slack declaration can require import regardless of this value. Required when: This field may be true only when doc_url is provided. |
| `team_id` | `integer` | No | A positive owning-team ID. If the team does not exist, the API returns 404. If the team is archived, the API returns 400. If the request omits team_id, the API uses the service owner. Minimum: 1. |
| `issue_type` | `string` | No | The work type. If the request omits issue_type, the API uses incident. Allowed values: `"ticket"` \| `"incident"` \| `"postmortem"` |
| `status` | `string` | No | An initial workflow-status key from the workspace configuration. If the request omits status, the API uses the configured default status. |
| `severity_mode` | `string` | No | The severity selection mode. If the request omits severity_mode, the API uses auto. Allowed values: `"auto"` \| `"manual"` |
| `create_slack_channel` | `boolean` | No | Controls Slack channel creation. If the request omits create_slack_channel, the API uses the workspace Slack configuration. Slack must be configured before the API can create a channel. |
| `invite_usergroups` | `string[]` | No | The Slack user-group IDs to invite. If the request omits invite_usergroups, the API uses the workspace Slack configuration. |
| `custom_field_values` | `object[]` | No | Custom-field values for the new incident. The API validates every value and each required_on_creation field before Slack work. It saves these values and the incident in one database transaction. |
| `custom_field_values[].field_id` | `integer` | Yes | The positive ID of the custom-field definition. Minimum: 1. |
| `custom_field_values[].value` | `any` | Yes | The value for the custom field. The accepted type depends on field_type. |

Request JSON:

```json
{
  "title": "Payments API requests timing out",
  "impact_summary": "EU customers cannot complete payment authorization",
  "create_slack_channel": false
}
```

#### 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 |
| --- | --- | --- | --- |
| `incident` | `object` | Always | The complete public record for an incident. |
| `incident.id` | `integer` | Always | The positive numeric ID of the incident. Minimum: 1. |
| `incident.revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `incident.company_id` | `integer` | Always | The ID of the workspace that owns the incident. Minimum: 1. |
| `incident.human_id` | `string` | Always | The permanent incident reference shown to users. |
| `incident.title` | `string` | Always | The editable display title of the incident. |
| `incident.service` | `string` | Always | The service slug associated with the incident. |
| `incident.regions` | `string[]` | Always | The affected regions. |
| `incident.severity` | `string` | Always | The configured severity key. |
| `incident.slack_channel_id` | `string` | Always | The linked Slack channel ID. An empty string means that no channel is linked. |
| `incident.meet_url` | `string` | Always | The incident meeting URL. An empty string means that no meeting is linked. |
| `incident.doc_url` | `string` | Always | The incident document or source URL. An empty string means that no URL is stored. |
| `incident.status` | `string` | Always | The configured workflow-status key. |
| `incident.status_changed_at` | `string (RFC 3339) \| null` | Always | The time of the latest workflow-status change. A null value means that this time is unavailable. |
| `incident.last_event_at` | `string (RFC 3339) \| null` | Always | The start time of the latest timeline event. A null value means that no event exists. |
| `incident.last_event_summary` | `string` | Always | A summary of the latest timeline event. |
| `incident.issue_type` | `string` | Always | The work type. Possible values: `"ticket"` \| `"incident"` \| `"postmortem"` |
| `incident.severity_mode` | `string` | Always | The severity selection mode. Possible values: `"auto"` \| `"manual"` |
| `incident.impact_summary` | `string` | Always | The current customer or operational impact summary. |
| `incident.customer_impact_status` | `string` | Always | The customer-impact assessment. Possible values: `"unknown"` \| `"none"` \| `"confirmed"` |
| `incident.impact_started_at` | `string (RFC 3339) \| null` | Always | The impact start time. A null value means that the time is unknown. |
| `incident.first_reported_at` | `string (RFC 3339) \| null` | Always | The time of the first incident report. A null value means that the time is unknown. |
| `incident.first_responded_at` | `string (RFC 3339) \| null` | Always | The time of the first response. A null value means that the time is unknown. |
| `incident.recovered_at` | `string (RFC 3339) \| null` | Always | The service recovery time. A null value means that recovery is not recorded. |
| `incident.duration_seconds` | `integer` | Always | The calculated incident duration in whole seconds. Minimum: 0. |
| `incident.duration_anchor` | `string` | Always | The timestamp field from which the API calculates duration. |
| `incident.team_id` | `integer \| null` | Always | The owning-team ID. A null value means that the incident has no owning team. |
| `incident.event_count` | `integer` | Always | The number of timeline events for the incident. Minimum: 0. |
| `incident.investigation_summary` | `object \| null` | Always | A summary of investigation runs. A null value means that no investigation data exists. Must match exactly one of 2 schema variants. |
| `incident.investigation_summary.run_count` | `integer` | Always | The total number of investigation runs. Minimum: 0. |
| `incident.investigation_summary.latest_run` | `object \| null` | Always | The most recent investigation run. A null value means that no run exists. Must match exactly one of 2 schema variants. |
| `incident.investigation_summary.latest_run.status` | `string` | Always | The current investigation-run status. |
| `incident.investigation_summary.latest_run.started_at` | `string (RFC 3339)` | Always | The investigation-run start time. |
| `incident.investigation_summary.latest_run.completed_at` | `string (RFC 3339)` | Optional | The investigation-run completion time. |
| `incident.created_at` | `string (RFC 3339)` | Always | The incident creation time. |
| `incident.created_by` | `string` | Always | The identifier of the user who created the incident. |
| `incident.custom_fields` | `object` | Always | The incident values keyed by custom-field slug. |
| `incident.archived_at` | `string (RFC 3339) \| null` | Always | The incident archive time. A null value means that the incident is active. |
| `incident.archived_by` | `integer \| null` | Always | The user ID that archived the incident. A null value means that the incident is active. |
| `incident.incident_manager` | `string` | Always | The user identifier of the incident manager. An empty string means that no manager is assigned. |
| `incident.incident_responders` | `string[]` | Always | The user identifiers of assigned responders. |
| `incident.has_assignees` | `boolean` | Always | Indicates whether the incident has a manager or at least one responder. |
| `timeline_event` | `object` | Optional | The complete public record for a timeline event. |
| `timeline_event.id` | `integer` | Always | The positive numeric ID of the timeline event. |
| `timeline_event.revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `timeline_event.incident_id` | `integer` | Always | The positive numeric ID of the incident. |
| `timeline_event.started_at` | `string (RFC 3339)` | Always | The event start time. |
| `timeline_event.at` | `string (RFC 3339)` | Always | A deprecated response alias for started_at. Both fields contain the same value. |
| `timeline_event.ended_at` | `string (RFC 3339) \| null` | Always | The event end time. A null value means that the event has no end time. |
| `timeline_event.is_ongoing` | `boolean` | Always | Indicates whether the event is still in progress. |
| `timeline_event.group_id` | `integer \| null` | Always | The timeline-group ID. A null value means that the event has no group. |
| `timeline_event.is_key` | `boolean` | Always | Indicates whether responders marked this as a key incident event. |
| `timeline_event.source` | `string` | Always | The system or actor that created the event. |
| `timeline_event.source_ref` | `string` | Always | The source-specific reference for the event. |
| `timeline_event.type` | `string` | Always | The event type used for grouping and display. |
| `timeline_event.icon` | `string` | Always | The event icon. The maximum length is 8 Unicode characters. Maximum length: 8 characters. |
| `timeline_event.title` | `string` | Always | The optional display title of the event. |
| `timeline_event.body` | `string` | Always | The event description. |
| `timeline_event.collaboration_generation` | `integer` | Always | The version of collaborative content associated with the event. |
| `timeline_event.tags` | `string[]` | Always | The normalized event tags. |
| `timeline_event.ai_context` | `string` | Always | The saved AI context for a generated event. An empty string means that no context is stored. |
| `timeline_event.created_by` | `string` | Always | The identifier of the user or system that created the event. |
| `timeline_event.comment_count` | `integer` | Always | The number of comments on the event. |
| `timeline_event.attachments` | `object[]` | Always | The files attached to the event. |
| `timeline_event.attachments[].id` | `integer` | Always | The positive numeric ID of the attachment. |
| `timeline_event.attachments[].timeline_event_id` | `integer` | Always | The positive numeric ID of the timeline event. |
| `timeline_event.attachments[].filename` | `string` | Always | The original file name. |
| `timeline_event.attachments[].url` | `string` | Always | A temporary download URL. An empty string means that no URL is currently available. |
| `timeline_event.attachments[].url_available` | `boolean` | Always | Indicates whether url contains an available download URL. |
| `timeline_event.attachments[].content_type` | `string` | Always | The media type of the attached file. |
| `timeline_event.attachments[].size_bytes` | `integer` | Always | The file size in bytes. |
| `timeline_event.attachments[].created_at` | `string (RFC 3339)` | Always | The attachment creation time. |
| `timeline_event.attachments[].created_by` | `string` | Always | The identifier of the user who attached the file. |
| `timeline_event.source_window_started_at` | `string (RFC 3339) \| null` | Always | The start of the source-data window. A null value means that no window is recorded. |
| `timeline_event.source_window_ended_at` | `string (RFC 3339) \| null` | Always | The end of the source-data window. A null value means that no window is recorded. |
| `timeline_event.evidence_links` | `object[]` | Always | The evidence references associated with the event. |
| `timeline_event.evidence_links[].source` | `string` | Always | The system or provider that supplied the evidence. |
| `timeline_event.evidence_links[].url` | `string` | Always | The absolute URL of the evidence. |
| `timeline_event.evidence_links[].label` | `string` | Optional | A short display name for the evidence. |
| `timeline_event.evidence_links[].summary` | `string` | Optional | A short summary of the evidence. |
| `timeline_event.evidence_links[].artifact_id` | `string` | Optional | The identifier of a stored evidence artifact. |
| `timeline_event.evidence_links[].artifact_digest` | `string` | Optional | The digest used to make sure that the artifact content did not change. |
| `timeline_event.evidence_links[].result_bytes` | `integer` | Optional | The original evidence result size in bytes. |
| `timeline_event.evidence_links[].truncated` | `boolean` | Optional | Indicates whether the stored evidence omits part of the original result. |
| `source_timeline_event` | `object` | Optional | The complete public record for a timeline event. |
| `source_timeline_event.id` | `integer` | Always | The positive numeric ID of the timeline event. |
| `source_timeline_event.revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `source_timeline_event.incident_id` | `integer` | Always | The positive numeric ID of the incident. |
| `source_timeline_event.started_at` | `string (RFC 3339)` | Always | The event start time. |
| `source_timeline_event.at` | `string (RFC 3339)` | Always | A deprecated response alias for started_at. Both fields contain the same value. |
| `source_timeline_event.ended_at` | `string (RFC 3339) \| null` | Always | The event end time. A null value means that the event has no end time. |
| `source_timeline_event.is_ongoing` | `boolean` | Always | Indicates whether the event is still in progress. |
| `source_timeline_event.group_id` | `integer \| null` | Always | The timeline-group ID. A null value means that the event has no group. |
| `source_timeline_event.is_key` | `boolean` | Always | Indicates whether responders marked this as a key incident event. |
| `source_timeline_event.source` | `string` | Always | The system or actor that created the event. |
| `source_timeline_event.source_ref` | `string` | Always | The source-specific reference for the event. |
| `source_timeline_event.type` | `string` | Always | The event type used for grouping and display. |
| `source_timeline_event.icon` | `string` | Always | The event icon. The maximum length is 8 Unicode characters. Maximum length: 8 characters. |
| `source_timeline_event.title` | `string` | Always | The optional display title of the event. |
| `source_timeline_event.body` | `string` | Always | The event description. |
| `source_timeline_event.collaboration_generation` | `integer` | Always | The version of collaborative content associated with the event. |
| `source_timeline_event.tags` | `string[]` | Always | The normalized event tags. |
| `source_timeline_event.ai_context` | `string` | Always | The saved AI context for a generated event. An empty string means that no context is stored. |
| `source_timeline_event.created_by` | `string` | Always | The identifier of the user or system that created the event. |
| `source_timeline_event.comment_count` | `integer` | Always | The number of comments on the event. |
| `source_timeline_event.attachments` | `object[]` | Always | The files attached to the event. |
| `source_timeline_event.attachments[].id` | `integer` | Always | The positive numeric ID of the attachment. |
| `source_timeline_event.attachments[].timeline_event_id` | `integer` | Always | The positive numeric ID of the timeline event. |
| `source_timeline_event.attachments[].filename` | `string` | Always | The original file name. |
| `source_timeline_event.attachments[].url` | `string` | Always | A temporary download URL. An empty string means that no URL is currently available. |
| `source_timeline_event.attachments[].url_available` | `boolean` | Always | Indicates whether url contains an available download URL. |
| `source_timeline_event.attachments[].content_type` | `string` | Always | The media type of the attached file. |
| `source_timeline_event.attachments[].size_bytes` | `integer` | Always | The file size in bytes. |
| `source_timeline_event.attachments[].created_at` | `string (RFC 3339)` | Always | The attachment creation time. |
| `source_timeline_event.attachments[].created_by` | `string` | Always | The identifier of the user who attached the file. |
| `source_timeline_event.source_window_started_at` | `string (RFC 3339) \| null` | Always | The start of the source-data window. A null value means that no window is recorded. |
| `source_timeline_event.source_window_ended_at` | `string (RFC 3339) \| null` | Always | The end of the source-data window. A null value means that no window is recorded. |
| `source_timeline_event.evidence_links` | `object[]` | Always | The evidence references associated with the event. |
| `source_timeline_event.evidence_links[].source` | `string` | Always | The system or provider that supplied the evidence. |
| `source_timeline_event.evidence_links[].url` | `string` | Always | The absolute URL of the evidence. |
| `source_timeline_event.evidence_links[].label` | `string` | Optional | A short display name for the evidence. |
| `source_timeline_event.evidence_links[].summary` | `string` | Optional | A short summary of the evidence. |
| `source_timeline_event.evidence_links[].artifact_id` | `string` | Optional | The identifier of a stored evidence artifact. |
| `source_timeline_event.evidence_links[].artifact_digest` | `string` | Optional | The digest used to make sure that the artifact content did not change. |
| `source_timeline_event.evidence_links[].result_bytes` | `integer` | Optional | The original evidence result size in bytes. |
| `source_timeline_event.evidence_links[].truncated` | `boolean` | Optional | Indicates whether the stored evidence omits part of the original result. |
| `source_timeline_import` | `object` | Optional | The result of importing an initial report as a timeline event. |
| `source_timeline_import.status` | `string` | Always | The import result. Possible values: `"imported"` \| `"failed"` \| `"unavailable"` \| `"invalid"` \| `"unsupported"` |
| `source_timeline_import.provider` | `string` | Optional | The detected source provider. When the provider is unavailable, this field is absent. |
| `slack_declaration_attachment` | `object` | Optional | The result of attaching Slack declaration context to the incident. |
| `slack_declaration_attachment.status` | `string` | Always | The attachment result. Possible values: `"attached"` \| `"failed"` |
| `slack_declaration_images` | `object` | Optional | The result of copying images from a Slack declaration. |
| `slack_declaration_images.status` | `string` | Always | The image-copy result. Possible values: `"attached"` \| `"partial"` \| `"failed"` |
| `slack_declaration_images.total` | `integer` | Always | The total number of Slack images considered. Minimum: 0. |
| `slack_declaration_images.attached` | `integer` | Always | The number of images copied successfully. Minimum: 0. |
| `slack_declaration_images.failed` | `integer` | Always | The number of images that the API did not copy. Minimum: 0. |
| `declaration_next` | `string` | Optional | The next incident workspace section requested by a signed Slack declaration. |
| `custom_field_values` | `object[]` | Optional | The custom-field values saved in the same transaction as the incident. |
| `custom_field_values[].id` | `integer` | Always | The positive numeric ID of the stored value. Minimum: 1. |
| `custom_field_values[].incident_id` | `integer` | Always | The positive numeric ID of the incident. Minimum: 1. |
| `custom_field_values[].custom_field_id` | `integer` | Always | The positive numeric ID of the custom-field definition. Minimum: 1. |
| `custom_field_values[].value` | `any` | Optional | The stored field value. Its type depends on field_type. |
| `custom_field_values[].created_at` | `string (RFC 3339)` | Always | The initial value storage time. |
| `custom_field_values[].updated_at` | `string (RFC 3339)` | Always | The time of the latest value change. |

The incident field contains the saved incident. custom_field_values contains values saved in the same database transaction. timeline_event contains the incident_declared event. source_timeline_event contains the imported source event. A 201 response does not guarantee a successful evidence import. source_timeline_import identifies the provider and reports the import result.

```json
{
  "incident": {
    "id": 1,
    "revision": 1,
    "company_id": 7,
    "human_id": "INC-2025-001",
    "title": "Payments API requests timing out",
    "service": "payments",
    "regions": [
      "us-east-1"
    ],
    "severity": "sev2",
    "slack_channel_id": "C08ABC123",
    "meet_url": "https://meet.google.com/abc-defg-hij",
    "doc_url": "https://acme.pagerduty.com/incidents/PXYZ123",
    "status": "active",
    "status_changed_at": "2025-01-30T12:00:00Z",
    "last_event_at": null,
    "last_event_summary": "",
    "issue_type": "incident",
    "severity_mode": "manual",
    "impact_summary": "EU customers cannot complete payment authorization",
    "customer_impact_status": "unknown",
    "impact_started_at": null,
    "first_reported_at": null,
    "first_responded_at": null,
    "recovered_at": null,
    "duration_seconds": 0,
    "duration_anchor": "created_at",
    "team_id": 42,
    "event_count": 0,
    "investigation_summary": null,
    "created_at": "2025-01-30T12:00:00Z",
    "created_by": "oncall@example.com",
    "custom_fields": {},
    "archived_at": null,
    "archived_by": null,
    "incident_manager": "",
    "incident_responders": [],
    "has_assignees": false
  },
  "timeline_event": {
    "id": 101,
    "revision": 1,
    "incident_id": 1,
    "started_at": "2025-01-30T12:00:00Z",
    "at": "2025-01-30T12:00:00Z",
    "ended_at": null,
    "is_ongoing": false,
    "group_id": null,
    "is_key": false,
    "source": "system",
    "source_ref": "",
    "type": "incident_declared",
    "icon": "🚨",
    "title": "Incident declared",
    "body": "PagerDuty reports elevated payment authorization latency.",
    "collaboration_generation": 1,
    "tags": [],
    "ai_context": "",
    "created_by": "oncall@example.com",
    "comment_count": 0,
    "attachments": [],
    "source_window_started_at": null,
    "source_window_ended_at": null,
    "evidence_links": []
  },
  "source_timeline_event": {
    "id": 102,
    "revision": 1,
    "incident_id": 1,
    "started_at": "2025-01-30T12:00:00.000001Z",
    "at": "2025-01-30T12:00:00.000001Z",
    "ended_at": null,
    "is_ongoing": false,
    "group_id": null,
    "is_key": false,
    "source": "pagerduty",
    "source_ref": "pagerduty:PXYZ123",
    "type": "alert",
    "icon": "🚨",
    "title": "Payment authorization latency",
    "body": "PagerDuty incident PXYZ123 is triggered.",
    "collaboration_generation": 1,
    "tags": [],
    "ai_context": "",
    "created_by": "oncall@example.com",
    "comment_count": 0,
    "attachments": [],
    "source_window_started_at": null,
    "source_window_ended_at": null,
    "evidence_links": []
  },
  "source_timeline_import": {
    "provider": "pagerduty",
    "status": "imported"
  },
  "custom_field_values": [
    {
      "id": 1,
      "incident_id": 1,
      "custom_field_id": 50,
      "value": 1500,
      "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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

#### Related operations

- [List available workflow statuses](https://tellagen.com/api-reference/configuration#configuration-endpoint-get-api-v1-configuration-workflow-statuses) (`GET /api/v1/configuration/workflow-statuses`)
- [List available severities](https://tellagen.com/api-reference/configuration#configuration-endpoint-get-api-v1-configuration-severities) (`GET /api/v1/configuration/severities`)
- [List services](https://tellagen.com/api-reference/services#services-endpoint-get-api-v1-services) (`GET /api/v1/services`)

### `GET /api/v1/incidents/archived`

**List archived incidents.** Returns one page of archived incidents. The default page size is 50, and the maximum is 100. Results are ordered by archived_at and incident ID. The workspace history limit can exclude older incidents.

Required scope: `incidents:read`

Operation ID: `listArchivedIncidents`
Contract: `supported`
Retry guidance: This operation is repeat-safe. Respect Retry-After and use bounded retries.
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. |
| `scope` | `string` | No | Selects all incidents or only postmortems. The default is all. Default: "all". Allowed values: `"all"` \| `"postmortem"` |
| `status` | `string[]` | No | Includes incidents with any supplied workflow status. Repeating status supplies more than one accepted value. |
| `severity` | `string[]` | No | Includes incidents with any supplied severity key. Repeating severity supplies more than one accepted value. |
| `service` | `string[]` | No | Includes incidents with any supplied service slug. Repeating service supplies more than one accepted value. |
| `team_id` | `integer[]` | No | Includes incidents with any supplied owning-team ID. Repeating team_id supplies more than one accepted value. Each ID must be positive. |
| `created_from` | `string (RFC 3339)` | No | Includes incidents created at or after this RFC 3339 timestamp. |
| `created_to` | `string (RFC 3339)` | No | Includes incidents created at or before this RFC 3339 timestamp. |
| `created_before` | `string (RFC 3339)` | No | Includes incidents created strictly before this RFC 3339 timestamp. It cannot be combined with created_to. |
| `region` | `string[]` | No | Includes incidents with any supplied case-insensitive region. Repeating region supplies more than one accepted value. Maximum items: 100. |
| `lifecycle` | `string[]` | No | Includes any supplied incident lifecycle: ticket, incident, postmortem, or alert. Maximum items: 100. Allowed values: `"ticket"` \| `"incident"` \| `"postmortem"` \| `"alert"` |
| `service_tier` | `string[]` | No | Includes incidents whose canonical tenant service has any supplied tier. Maximum items: 100. Allowed values: `"t0"` \| `"t1"` \| `"t2"` \| `"t3"` |
| `effective_team_id` | `integer[]` | No | Includes incidents assigned directly to any supplied team or inheriting it from the canonical service. Repeating the parameter supplies more than one accepted positive ID. Maximum items: 100. |
| `unassigned_team` | `boolean` | No | When true, also includes incidents with no direct or canonical-service team. Default: false. |
| `mine` | `boolean` | No | When true, includes incidents assigned to the authenticated user, or created by that user when no active assignee exists. API-key-only authentication cannot use this filter. Default: false. |
| `active_only` | `boolean` | No | When true, excludes workflow stages configured as terminal. False does not add a status restriction. |
| `status_scope` | `string` | No | Selects all statuses, nonterminal ongoing statuses, or one configured workflow-stage slug. Minimum length: 1 characters. Maximum length: 2048 characters. |
| `diagnosis` | `string[]` | No | Includes incidents in any supplied diagnosis state. Repeating diagnosis supplies more than one accepted value. Maximum items: 100. Allowed values: `"not_run"` \| `"in_progress"` \| `"completed"` \| `"needs_attention"` \| `"unavailable"` |
| `sort` | `string` | No | Selects the collection sort field. Custom requires sort_custom_field. Omission keeps legacy created_at order. Allowed values: `"created_at"` \| `"last_event_at"` \| `"severity"` \| `"service"` \| `"diagnosis"` \| `"case"` \| `"regions"` \| `"team"` \| `"impact"` \| `"status"` \| `"owner"` \| `"events"` \| `"custom"` |
| `direction` | `string` | No | Sort direction for both the selected key and incident-ID tiebreaker. Default: "desc". Allowed values: `"asc"` \| `"desc"` |
| `sort_custom_field` | `string` | No | Active custom-field slug used when sort is custom. The server resolves and validates its stored type. Minimum length: 1 characters. Maximum length: 64 characters. |
| `custom_filter` | `string[]` | No | A JSON object with slug, operator, and value. Repeat for up to 32 predicates. Values remain typed JSON; arrays preserve choices containing commas. Supported operators depend on the resolved field type: string is/is_not/contains/starts_with; select is/is_not; boolean is; number and monetary eq/gt/lt/gte/lte/between; date and datetime is/before/after/between/gte/lte; time is/before/after; multiselect includes_any/includes_all/excludes. SQL fragments and client-supplied field types are rejected. Maximum items: 32. |
| `include_counts` | `boolean` | No | When true, returns authoritative matched, all, ongoing, and per-status counts before cursor and limit. Default: false. |
| `impact_status` | `string[]` | No | Includes incidents with any supplied customer-impact status. Allowed values: `"unknown"` \| `"none"` \| `"confirmed"` |
| `affected_customers_op` | `string` | No | Selects the operator for the affected-customer comparison. This parameter and affected_customers_value must appear together. Allowed values: `"<"` \| `">"` \| `"="` |
| `affected_customers_value` | `integer` | No | The non-negative affected-customer count for comparison. This parameter and affected_customers_op must appear together. Minimum: 0. |
| `mrr_op` | `string` | No | Selects the operator for the MRR comparison. This parameter and mrr_value must appear together. Allowed values: `"<"` \| `">"` \| `"="` |
| `mrr_value` | `integer` | No | The non-negative MRR amount for comparison, in minor currency units. This parameter and mrr_op must appear together. Minimum: 0. |
| `mrr_currency` | `string` | No | A three-letter ISO currency code. If mrr_basis is source, this parameter is required. Pattern: ^[A-Z]{3}$. |
| `mrr_basis` | `string` | No | Selects normalized workspace currency values or original source currency values. The default is normalized. Default: "normalized". Allowed values: `"normalized"` \| `"source"` |

#### Example request

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

```bash
curl --request GET \
  --url 'https://{company}.api.tellagen.com/api/v1/incidents/archived' \
  --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 |
| --- | --- | --- | --- |
| `incidents` | `object[]` | Always | The incidents in this page. |
| `incidents[].id` | `integer` | Always | The positive numeric ID of the incident. Minimum: 1. |
| `incidents[].revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `incidents[].company_id` | `integer` | Always | The ID of the workspace that owns the incident. Minimum: 1. |
| `incidents[].human_id` | `string` | Always | The permanent incident reference shown to users. |
| `incidents[].title` | `string` | Always | The editable display title of the incident. |
| `incidents[].service` | `string` | Always | The service slug associated with the incident. |
| `incidents[].regions` | `string[]` | Always | The affected regions. |
| `incidents[].severity` | `string` | Always | The configured severity key. |
| `incidents[].slack_channel_id` | `string` | Always | The linked Slack channel ID. An empty string means that no channel is linked. |
| `incidents[].meet_url` | `string` | Always | The incident meeting URL. An empty string means that no meeting is linked. |
| `incidents[].doc_url` | `string` | Always | The incident document or source URL. An empty string means that no URL is stored. |
| `incidents[].status` | `string` | Always | The configured workflow-status key. |
| `incidents[].status_changed_at` | `string (RFC 3339) \| null` | Always | The time of the latest workflow-status change. A null value means that this time is unavailable. |
| `incidents[].last_event_at` | `string (RFC 3339) \| null` | Always | The start time of the latest timeline event. A null value means that no event exists. |
| `incidents[].last_event_summary` | `string` | Always | A summary of the latest timeline event. |
| `incidents[].issue_type` | `string` | Always | The work type. Possible values: `"ticket"` \| `"incident"` \| `"postmortem"` |
| `incidents[].severity_mode` | `string` | Always | The severity selection mode. Possible values: `"auto"` \| `"manual"` |
| `incidents[].impact_summary` | `string` | Always | The current customer or operational impact summary. |
| `incidents[].customer_impact_status` | `string` | Always | The customer-impact assessment. Possible values: `"unknown"` \| `"none"` \| `"confirmed"` |
| `incidents[].impact_started_at` | `string (RFC 3339) \| null` | Always | The impact start time. A null value means that the time is unknown. |
| `incidents[].first_reported_at` | `string (RFC 3339) \| null` | Always | The time of the first incident report. A null value means that the time is unknown. |
| `incidents[].first_responded_at` | `string (RFC 3339) \| null` | Always | The time of the first response. A null value means that the time is unknown. |
| `incidents[].recovered_at` | `string (RFC 3339) \| null` | Always | The service recovery time. A null value means that recovery is not recorded. |
| `incidents[].duration_seconds` | `integer` | Always | The calculated incident duration in whole seconds. Minimum: 0. |
| `incidents[].duration_anchor` | `string` | Always | The timestamp field from which the API calculates duration. |
| `incidents[].team_id` | `integer \| null` | Always | The owning-team ID. A null value means that the incident has no owning team. |
| `incidents[].event_count` | `integer` | Always | The number of timeline events for the incident. Minimum: 0. |
| `incidents[].investigation_summary` | `object \| null` | Always | A summary of investigation runs. A null value means that no investigation data exists. Must match exactly one of 2 schema variants. |
| `incidents[].investigation_summary.run_count` | `integer` | Always | The total number of investigation runs. Minimum: 0. |
| `incidents[].investigation_summary.latest_run` | `object \| null` | Always | The most recent investigation run. A null value means that no run exists. Must match exactly one of 2 schema variants. |
| `incidents[].investigation_summary.latest_run.status` | `string` | Always | The current investigation-run status. |
| `incidents[].investigation_summary.latest_run.started_at` | `string (RFC 3339)` | Always | The investigation-run start time. |
| `incidents[].investigation_summary.latest_run.completed_at` | `string (RFC 3339)` | Optional | The investigation-run completion time. |
| `incidents[].created_at` | `string (RFC 3339)` | Always | The incident creation time. |
| `incidents[].created_by` | `string` | Always | The identifier of the user who created the incident. |
| `incidents[].custom_fields` | `object` | Always | The incident values keyed by custom-field slug. |
| `incidents[].archived_at` | `string (RFC 3339) \| null` | Always | The incident archive time. A null value means that the incident is active. |
| `incidents[].archived_by` | `integer \| null` | Always | The user ID that archived the incident. A null value means that the incident is active. |
| `incidents[].incident_manager` | `string` | Always | The user identifier of the incident manager. An empty string means that no manager is assigned. |
| `incidents[].incident_responders` | `string[]` | Always | The user identifiers of assigned responders. |
| `incidents[].has_assignees` | `boolean` | Always | Indicates whether the incident has a manager or at least one responder. |
| `has_more` | `boolean` | Always | Deprecated top-level alias for pagination.has_more. |
| `next_cursor` | `string` | Optional | Deprecated top-level alias for pagination.next_cursor. |
| `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. |
| `restricted_count` | `integer` | Optional | The number of matching incidents excluded by the workspace history limit. Minimum: 0. |
| `history_limited` | `boolean` | Optional | Indicates whether the workspace history limit applies to this response. |
| `mrr_currency` | `string` | Optional | The workspace reporting currency used for normalized MRR values. |
| `mrr_conversion_mode` | `string` | Optional | The workspace mode for MRR currency conversion. Possible values: `"normalized"` \| `"source_only"` |
| `mrr_projection_state` | `string` | Optional | The MRR projection state. Possible values: `"ready"` \| `"stale"` \| `"unavailable"` |
| `supported_mrr_currencies` | `string[]` | Optional | The ISO currency codes supported by MRR filters. |
| `collection_counts` | `object` | Optional | Complete collection counts before cursor and limit when include_counts is true. |
| `collection_counts.matched` | `integer` | Always | Count after every predicate, including status selection. Minimum: 0. |
| `collection_counts.all` | `integer` | Always | Count after non-status predicates, excluding status, status_scope, and active_only. Minimum: 0. |
| `collection_counts.ongoing` | `integer` | Always | Nonterminal count after non-status predicates. Minimum: 0. |
| `collection_counts.by_status` | `object` | Always | Exact counts by normalized stored status after non-status predicates. |

Returns archived incidents, pagination fields, and workspace history-limit details. The filters also apply to restricted_count.

```json
{
  "incidents": [
    {
      "id": 1,
      "revision": 7,
      "company_id": 7,
      "human_id": "INC-2025-001",
      "title": "Payments API requests timing out",
      "service": "payments",
      "regions": [
        "us-east-1"
      ],
      "severity": "sev1",
      "slack_channel_id": "C08ABC123",
      "meet_url": "https://meet.google.com/abc-defg-hij",
      "doc_url": "https://docs.google.com/d/1abc",
      "status": "active",
      "status_changed_at": "2025-01-30T12:05:00Z",
      "last_event_at": "2025-01-30T12:10:00Z",
      "last_event_summary": "Investigating root cause",
      "issue_type": "incident",
      "severity_mode": "manual",
      "impact_summary": "Payment processing delayed for EU customers",
      "customer_impact_status": "confirmed",
      "impact_started_at": "2025-01-30T11:55:00Z",
      "first_reported_at": "2025-01-30T11:58:00Z",
      "first_responded_at": "2025-01-30T12:00:00Z",
      "recovered_at": null,
      "duration_seconds": 900,
      "duration_anchor": "impact_started_at",
      "team_id": 42,
      "event_count": 5,
      "investigation_summary": null,
      "created_at": "2025-01-30T12:00:00Z",
      "created_by": "oncall@example.com",
      "custom_fields": {},
      "archived_at": "2025-02-07T09:30:00Z",
      "archived_by": 42,
      "incident_manager": "oncall@example.com",
      "incident_responders": [
        "responder@example.com"
      ],
      "has_assignees": true
    }
  ],
  "restricted_count": 3,
  "history_limited": true,
  "has_more": false,
  "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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

### `GET /api/v1/incident-references/{reference}`

**Get incident by permanent reference.** Returns an incident from its permanent, opaque reference. This endpoint is suitable for stored references and links in other systems. The numeric-ID endpoint accepts only a positive ID returned by this API.

Required scope: `incidents:read`

Operation ID: `getIncidentByReference`
Contract: `supported`
Retry guidance: This operation is repeat-safe. Respect Retry-After and use bounded retries.
Prerequisites: None

#### Path parameters

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

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `reference` | `string` | Yes | A permanent, opaque reference from Tellagen incident links and messages. This parameter does not accept a numeric incident ID. Minimum length: 1 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/incident-references/{reference}' \
  --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 |
| --- | --- | --- | --- |
| `incident` | `object` | Always | The complete public record for an incident. |
| `incident.id` | `integer` | Always | The positive numeric ID of the incident. Minimum: 1. |
| `incident.revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `incident.company_id` | `integer` | Always | The ID of the workspace that owns the incident. Minimum: 1. |
| `incident.human_id` | `string` | Always | The permanent incident reference shown to users. |
| `incident.title` | `string` | Always | The editable display title of the incident. |
| `incident.service` | `string` | Always | The service slug associated with the incident. |
| `incident.regions` | `string[]` | Always | The affected regions. |
| `incident.severity` | `string` | Always | The configured severity key. |
| `incident.slack_channel_id` | `string` | Always | The linked Slack channel ID. An empty string means that no channel is linked. |
| `incident.meet_url` | `string` | Always | The incident meeting URL. An empty string means that no meeting is linked. |
| `incident.doc_url` | `string` | Always | The incident document or source URL. An empty string means that no URL is stored. |
| `incident.status` | `string` | Always | The configured workflow-status key. |
| `incident.status_changed_at` | `string (RFC 3339) \| null` | Always | The time of the latest workflow-status change. A null value means that this time is unavailable. |
| `incident.last_event_at` | `string (RFC 3339) \| null` | Always | The start time of the latest timeline event. A null value means that no event exists. |
| `incident.last_event_summary` | `string` | Always | A summary of the latest timeline event. |
| `incident.issue_type` | `string` | Always | The work type. Possible values: `"ticket"` \| `"incident"` \| `"postmortem"` |
| `incident.severity_mode` | `string` | Always | The severity selection mode. Possible values: `"auto"` \| `"manual"` |
| `incident.impact_summary` | `string` | Always | The current customer or operational impact summary. |
| `incident.customer_impact_status` | `string` | Always | The customer-impact assessment. Possible values: `"unknown"` \| `"none"` \| `"confirmed"` |
| `incident.impact_started_at` | `string (RFC 3339) \| null` | Always | The impact start time. A null value means that the time is unknown. |
| `incident.first_reported_at` | `string (RFC 3339) \| null` | Always | The time of the first incident report. A null value means that the time is unknown. |
| `incident.first_responded_at` | `string (RFC 3339) \| null` | Always | The time of the first response. A null value means that the time is unknown. |
| `incident.recovered_at` | `string (RFC 3339) \| null` | Always | The service recovery time. A null value means that recovery is not recorded. |
| `incident.duration_seconds` | `integer` | Always | The calculated incident duration in whole seconds. Minimum: 0. |
| `incident.duration_anchor` | `string` | Always | The timestamp field from which the API calculates duration. |
| `incident.team_id` | `integer \| null` | Always | The owning-team ID. A null value means that the incident has no owning team. |
| `incident.event_count` | `integer` | Always | The number of timeline events for the incident. Minimum: 0. |
| `incident.investigation_summary` | `object \| null` | Always | A summary of investigation runs. A null value means that no investigation data exists. Must match exactly one of 2 schema variants. |
| `incident.investigation_summary.run_count` | `integer` | Always | The total number of investigation runs. Minimum: 0. |
| `incident.investigation_summary.latest_run` | `object \| null` | Always | The most recent investigation run. A null value means that no run exists. Must match exactly one of 2 schema variants. |
| `incident.investigation_summary.latest_run.status` | `string` | Always | The current investigation-run status. |
| `incident.investigation_summary.latest_run.started_at` | `string (RFC 3339)` | Always | The investigation-run start time. |
| `incident.investigation_summary.latest_run.completed_at` | `string (RFC 3339)` | Optional | The investigation-run completion time. |
| `incident.created_at` | `string (RFC 3339)` | Always | The incident creation time. |
| `incident.created_by` | `string` | Always | The identifier of the user who created the incident. |
| `incident.custom_fields` | `object` | Always | The incident values keyed by custom-field slug. |
| `incident.archived_at` | `string (RFC 3339) \| null` | Always | The incident archive time. A null value means that the incident is active. |
| `incident.archived_by` | `integer \| null` | Always | The user ID that archived the incident. A null value means that the incident is active. |
| `incident.incident_manager` | `string` | Always | The user identifier of the incident manager. An empty string means that no manager is assigned. |
| `incident.incident_responders` | `string[]` | Always | The user identifiers of assigned responders. |
| `incident.has_assignees` | `boolean` | Always | Indicates whether the incident has a manager or at least one responder. |
| `timeline_event` | `object` | Optional | The complete public record for a timeline event. |
| `timeline_event.id` | `integer` | Always | The positive numeric ID of the timeline event. |
| `timeline_event.revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `timeline_event.incident_id` | `integer` | Always | The positive numeric ID of the incident. |
| `timeline_event.started_at` | `string (RFC 3339)` | Always | The event start time. |
| `timeline_event.at` | `string (RFC 3339)` | Always | A deprecated response alias for started_at. Both fields contain the same value. |
| `timeline_event.ended_at` | `string (RFC 3339) \| null` | Always | The event end time. A null value means that the event has no end time. |
| `timeline_event.is_ongoing` | `boolean` | Always | Indicates whether the event is still in progress. |
| `timeline_event.group_id` | `integer \| null` | Always | The timeline-group ID. A null value means that the event has no group. |
| `timeline_event.is_key` | `boolean` | Always | Indicates whether responders marked this as a key incident event. |
| `timeline_event.source` | `string` | Always | The system or actor that created the event. |
| `timeline_event.source_ref` | `string` | Always | The source-specific reference for the event. |
| `timeline_event.type` | `string` | Always | The event type used for grouping and display. |
| `timeline_event.icon` | `string` | Always | The event icon. The maximum length is 8 Unicode characters. Maximum length: 8 characters. |
| `timeline_event.title` | `string` | Always | The optional display title of the event. |
| `timeline_event.body` | `string` | Always | The event description. |
| `timeline_event.collaboration_generation` | `integer` | Always | The version of collaborative content associated with the event. |
| `timeline_event.tags` | `string[]` | Always | The normalized event tags. |
| `timeline_event.ai_context` | `string` | Always | The saved AI context for a generated event. An empty string means that no context is stored. |
| `timeline_event.created_by` | `string` | Always | The identifier of the user or system that created the event. |
| `timeline_event.comment_count` | `integer` | Always | The number of comments on the event. |
| `timeline_event.attachments` | `object[]` | Always | The files attached to the event. |
| `timeline_event.attachments[].id` | `integer` | Always | The positive numeric ID of the attachment. |
| `timeline_event.attachments[].timeline_event_id` | `integer` | Always | The positive numeric ID of the timeline event. |
| `timeline_event.attachments[].filename` | `string` | Always | The original file name. |
| `timeline_event.attachments[].url` | `string` | Always | A temporary download URL. An empty string means that no URL is currently available. |
| `timeline_event.attachments[].url_available` | `boolean` | Always | Indicates whether url contains an available download URL. |
| `timeline_event.attachments[].content_type` | `string` | Always | The media type of the attached file. |
| `timeline_event.attachments[].size_bytes` | `integer` | Always | The file size in bytes. |
| `timeline_event.attachments[].created_at` | `string (RFC 3339)` | Always | The attachment creation time. |
| `timeline_event.attachments[].created_by` | `string` | Always | The identifier of the user who attached the file. |
| `timeline_event.source_window_started_at` | `string (RFC 3339) \| null` | Always | The start of the source-data window. A null value means that no window is recorded. |
| `timeline_event.source_window_ended_at` | `string (RFC 3339) \| null` | Always | The end of the source-data window. A null value means that no window is recorded. |
| `timeline_event.evidence_links` | `object[]` | Always | The evidence references associated with the event. |
| `timeline_event.evidence_links[].source` | `string` | Always | The system or provider that supplied the evidence. |
| `timeline_event.evidence_links[].url` | `string` | Always | The absolute URL of the evidence. |
| `timeline_event.evidence_links[].label` | `string` | Optional | A short display name for the evidence. |
| `timeline_event.evidence_links[].summary` | `string` | Optional | A short summary of the evidence. |
| `timeline_event.evidence_links[].artifact_id` | `string` | Optional | The identifier of a stored evidence artifact. |
| `timeline_event.evidence_links[].artifact_digest` | `string` | Optional | The digest used to make sure that the artifact content did not change. |
| `timeline_event.evidence_links[].result_bytes` | `integer` | Optional | The original evidence result size in bytes. |
| `timeline_event.evidence_links[].truncated` | `boolean` | Optional | Indicates whether the stored evidence omits part of the original result. |

Returns the complete incident record. The reference continues to identify the same incident after editable fields change.

```json
{
  "incident": {
    "id": 1,
    "revision": 7,
    "company_id": 7,
    "human_id": "INC-2025-001",
    "title": "Payments API requests timing out",
    "service": "payments",
    "regions": [
      "us-east-1"
    ],
    "severity": "sev1",
    "slack_channel_id": "C08ABC123",
    "meet_url": "https://meet.google.com/abc-defg-hij",
    "doc_url": "https://docs.google.com/d/1abc",
    "status": "active",
    "status_changed_at": "2025-01-30T12:05:00Z",
    "last_event_at": "2025-01-30T12:10:00Z",
    "last_event_summary": "Investigating root cause",
    "issue_type": "incident",
    "severity_mode": "manual",
    "impact_summary": "Payment processing delayed for EU customers",
    "customer_impact_status": "confirmed",
    "impact_started_at": "2025-01-30T11:55:00Z",
    "first_reported_at": "2025-01-30T11:58:00Z",
    "first_responded_at": "2025-01-30T12:00:00Z",
    "recovered_at": null,
    "duration_seconds": 900,
    "duration_anchor": "impact_started_at",
    "team_id": 42,
    "event_count": 5,
    "investigation_summary": null,
    "created_at": "2025-01-30T12:00:00Z",
    "created_by": "oncall@example.com",
    "custom_fields": {},
    "archived_at": null,
    "archived_by": null,
    "incident_manager": "oncall@example.com",
    "incident_responders": [
      "responder@example.com"
    ],
    "has_assignees": true
  }
}
```

#### 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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

#### Related operations

- [Get incident](https://tellagen.com/api-reference/incidents#incidents-endpoint-get-api-v1-incidents-id) (`GET /api/v1/incidents/{id}`)

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

**Get incident.** Returns one incident by its positive numeric ID. The permanent-reference endpoint is suitable for stored references, external links, and workflows in other systems.

Required scope: `incidents:read`

Operation ID: `getIncident`
Contract: `supported`
Retry guidance: This operation is repeat-safe. Respect Retry-After and use bounded retries.
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 incident ID returned by this API. The permanent incident reference is suitable for stored references and external links. 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/incidents/{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 |
| --- | --- | --- | --- |
| `incident` | `object` | Always | The complete public record for an incident. |
| `incident.id` | `integer` | Always | The positive numeric ID of the incident. Minimum: 1. |
| `incident.revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `incident.company_id` | `integer` | Always | The ID of the workspace that owns the incident. Minimum: 1. |
| `incident.human_id` | `string` | Always | The permanent incident reference shown to users. |
| `incident.title` | `string` | Always | The editable display title of the incident. |
| `incident.service` | `string` | Always | The service slug associated with the incident. |
| `incident.regions` | `string[]` | Always | The affected regions. |
| `incident.severity` | `string` | Always | The configured severity key. |
| `incident.slack_channel_id` | `string` | Always | The linked Slack channel ID. An empty string means that no channel is linked. |
| `incident.meet_url` | `string` | Always | The incident meeting URL. An empty string means that no meeting is linked. |
| `incident.doc_url` | `string` | Always | The incident document or source URL. An empty string means that no URL is stored. |
| `incident.status` | `string` | Always | The configured workflow-status key. |
| `incident.status_changed_at` | `string (RFC 3339) \| null` | Always | The time of the latest workflow-status change. A null value means that this time is unavailable. |
| `incident.last_event_at` | `string (RFC 3339) \| null` | Always | The start time of the latest timeline event. A null value means that no event exists. |
| `incident.last_event_summary` | `string` | Always | A summary of the latest timeline event. |
| `incident.issue_type` | `string` | Always | The work type. Possible values: `"ticket"` \| `"incident"` \| `"postmortem"` |
| `incident.severity_mode` | `string` | Always | The severity selection mode. Possible values: `"auto"` \| `"manual"` |
| `incident.impact_summary` | `string` | Always | The current customer or operational impact summary. |
| `incident.customer_impact_status` | `string` | Always | The customer-impact assessment. Possible values: `"unknown"` \| `"none"` \| `"confirmed"` |
| `incident.impact_started_at` | `string (RFC 3339) \| null` | Always | The impact start time. A null value means that the time is unknown. |
| `incident.first_reported_at` | `string (RFC 3339) \| null` | Always | The time of the first incident report. A null value means that the time is unknown. |
| `incident.first_responded_at` | `string (RFC 3339) \| null` | Always | The time of the first response. A null value means that the time is unknown. |
| `incident.recovered_at` | `string (RFC 3339) \| null` | Always | The service recovery time. A null value means that recovery is not recorded. |
| `incident.duration_seconds` | `integer` | Always | The calculated incident duration in whole seconds. Minimum: 0. |
| `incident.duration_anchor` | `string` | Always | The timestamp field from which the API calculates duration. |
| `incident.team_id` | `integer \| null` | Always | The owning-team ID. A null value means that the incident has no owning team. |
| `incident.event_count` | `integer` | Always | The number of timeline events for the incident. Minimum: 0. |
| `incident.investigation_summary` | `object \| null` | Always | A summary of investigation runs. A null value means that no investigation data exists. Must match exactly one of 2 schema variants. |
| `incident.investigation_summary.run_count` | `integer` | Always | The total number of investigation runs. Minimum: 0. |
| `incident.investigation_summary.latest_run` | `object \| null` | Always | The most recent investigation run. A null value means that no run exists. Must match exactly one of 2 schema variants. |
| `incident.investigation_summary.latest_run.status` | `string` | Always | The current investigation-run status. |
| `incident.investigation_summary.latest_run.started_at` | `string (RFC 3339)` | Always | The investigation-run start time. |
| `incident.investigation_summary.latest_run.completed_at` | `string (RFC 3339)` | Optional | The investigation-run completion time. |
| `incident.created_at` | `string (RFC 3339)` | Always | The incident creation time. |
| `incident.created_by` | `string` | Always | The identifier of the user who created the incident. |
| `incident.custom_fields` | `object` | Always | The incident values keyed by custom-field slug. |
| `incident.archived_at` | `string (RFC 3339) \| null` | Always | The incident archive time. A null value means that the incident is active. |
| `incident.archived_by` | `integer \| null` | Always | The user ID that archived the incident. A null value means that the incident is active. |
| `incident.incident_manager` | `string` | Always | The user identifier of the incident manager. An empty string means that no manager is assigned. |
| `incident.incident_responders` | `string[]` | Always | The user identifiers of assigned responders. |
| `incident.has_assignees` | `boolean` | Always | Indicates whether the incident has a manager or at least one responder. |
| `timeline_event` | `object` | Optional | The complete public record for a timeline event. |
| `timeline_event.id` | `integer` | Always | The positive numeric ID of the timeline event. |
| `timeline_event.revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `timeline_event.incident_id` | `integer` | Always | The positive numeric ID of the incident. |
| `timeline_event.started_at` | `string (RFC 3339)` | Always | The event start time. |
| `timeline_event.at` | `string (RFC 3339)` | Always | A deprecated response alias for started_at. Both fields contain the same value. |
| `timeline_event.ended_at` | `string (RFC 3339) \| null` | Always | The event end time. A null value means that the event has no end time. |
| `timeline_event.is_ongoing` | `boolean` | Always | Indicates whether the event is still in progress. |
| `timeline_event.group_id` | `integer \| null` | Always | The timeline-group ID. A null value means that the event has no group. |
| `timeline_event.is_key` | `boolean` | Always | Indicates whether responders marked this as a key incident event. |
| `timeline_event.source` | `string` | Always | The system or actor that created the event. |
| `timeline_event.source_ref` | `string` | Always | The source-specific reference for the event. |
| `timeline_event.type` | `string` | Always | The event type used for grouping and display. |
| `timeline_event.icon` | `string` | Always | The event icon. The maximum length is 8 Unicode characters. Maximum length: 8 characters. |
| `timeline_event.title` | `string` | Always | The optional display title of the event. |
| `timeline_event.body` | `string` | Always | The event description. |
| `timeline_event.collaboration_generation` | `integer` | Always | The version of collaborative content associated with the event. |
| `timeline_event.tags` | `string[]` | Always | The normalized event tags. |
| `timeline_event.ai_context` | `string` | Always | The saved AI context for a generated event. An empty string means that no context is stored. |
| `timeline_event.created_by` | `string` | Always | The identifier of the user or system that created the event. |
| `timeline_event.comment_count` | `integer` | Always | The number of comments on the event. |
| `timeline_event.attachments` | `object[]` | Always | The files attached to the event. |
| `timeline_event.attachments[].id` | `integer` | Always | The positive numeric ID of the attachment. |
| `timeline_event.attachments[].timeline_event_id` | `integer` | Always | The positive numeric ID of the timeline event. |
| `timeline_event.attachments[].filename` | `string` | Always | The original file name. |
| `timeline_event.attachments[].url` | `string` | Always | A temporary download URL. An empty string means that no URL is currently available. |
| `timeline_event.attachments[].url_available` | `boolean` | Always | Indicates whether url contains an available download URL. |
| `timeline_event.attachments[].content_type` | `string` | Always | The media type of the attached file. |
| `timeline_event.attachments[].size_bytes` | `integer` | Always | The file size in bytes. |
| `timeline_event.attachments[].created_at` | `string (RFC 3339)` | Always | The attachment creation time. |
| `timeline_event.attachments[].created_by` | `string` | Always | The identifier of the user who attached the file. |
| `timeline_event.source_window_started_at` | `string (RFC 3339) \| null` | Always | The start of the source-data window. A null value means that no window is recorded. |
| `timeline_event.source_window_ended_at` | `string (RFC 3339) \| null` | Always | The end of the source-data window. A null value means that no window is recorded. |
| `timeline_event.evidence_links` | `object[]` | Always | The evidence references associated with the event. |
| `timeline_event.evidence_links[].source` | `string` | Always | The system or provider that supplied the evidence. |
| `timeline_event.evidence_links[].url` | `string` | Always | The absolute URL of the evidence. |
| `timeline_event.evidence_links[].label` | `string` | Optional | A short display name for the evidence. |
| `timeline_event.evidence_links[].summary` | `string` | Optional | A short summary of the evidence. |
| `timeline_event.evidence_links[].artifact_id` | `string` | Optional | The identifier of a stored evidence artifact. |
| `timeline_event.evidence_links[].artifact_digest` | `string` | Optional | The digest used to make sure that the artifact content did not change. |
| `timeline_event.evidence_links[].result_bytes` | `integer` | Optional | The original evidence result size in bytes. |
| `timeline_event.evidence_links[].truncated` | `boolean` | Optional | Indicates whether the stored evidence omits part of the original result. |

Returns the complete incident record. Nullable timestamps, team details, archive details, and investigation_summary are always present. These fields can be null. Arrays and custom_fields are never null.

```json
{
  "incident": {
    "id": 1,
    "revision": 7,
    "company_id": 7,
    "human_id": "INC-2025-001",
    "title": "Payments API requests timing out",
    "service": "payments",
    "regions": [
      "us-east-1"
    ],
    "severity": "sev1",
    "slack_channel_id": "C08ABC123",
    "meet_url": "https://meet.google.com/abc-defg-hij",
    "doc_url": "https://docs.google.com/d/1abc",
    "status": "active",
    "status_changed_at": "2025-01-30T12:05:00Z",
    "last_event_at": "2025-01-30T12:10:00Z",
    "last_event_summary": "Investigating root cause",
    "issue_type": "incident",
    "severity_mode": "manual",
    "impact_summary": "Payment processing delayed for EU customers",
    "customer_impact_status": "confirmed",
    "impact_started_at": "2025-01-30T11:55:00Z",
    "first_reported_at": "2025-01-30T11:58:00Z",
    "first_responded_at": "2025-01-30T12:00:00Z",
    "recovered_at": null,
    "duration_seconds": 900,
    "duration_anchor": "impact_started_at",
    "team_id": 42,
    "event_count": 5,
    "investigation_summary": null,
    "created_at": "2025-01-30T12:00:00Z",
    "created_by": "oncall@example.com",
    "custom_fields": {},
    "archived_at": null,
    "archived_by": null,
    "incident_manager": "oncall@example.com",
    "incident_responders": [
      "responder@example.com"
    ],
    "has_assignees": true
  }
}
```

#### 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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

#### Related operations

- [Get incident by permanent reference](https://tellagen.com/api-reference/incidents#incidents-endpoint-get-api-v1-incident-references-reference) (`GET /api/v1/incident-references/{reference}`)

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

**Update incident.** Updates the supplied incident fields and returns the complete incident. Omitted fields stay unchanged. Unknown fields return 400. API v1 accepts status changes through this operation for compatibility. New clients must use the resolve, close, and reopen operations. The API validates submitted timestamps against each other and stored timestamps. Automatic severity can recalculate after relevant field changes.

Required scope: `incidents:write`

Operation ID: `updateIncident`
Contract: `supported`
Retry guidance: Read the current resource and reconcile changes before retrying with its current version. A stale version returns 412.
Prerequisites: Active responder seat, Incident is not archived

#### 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 incident ID returned by this API. The permanent incident reference is suitable for stored references and external links. 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/incidents/{id}' \
  --header 'Authorization: Bearer <token>' \
  --header 'If-Tellagen-Resource-Version: <resource-version>' \
  --header 'Content-Type: application/json' \
  --data '{
  "title": "Payments API recovered; monitoring authorization backlog",
  "impact_summary": "Full payment outage in EU region"
}'
```

#### Request body

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

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `title` | `string` | No | The incident display title. The maximum length is 160 characters. This field is separate from impact_summary. Maximum length: 160 characters. |
| `service` | `string` | No | The service slug. The maximum length is 128 characters. Maximum length: 128 characters. |
| `regions` | `string[]` | No | The complete replacement list of affected regions. An empty array removes all regions. |
| `severity` | `string` | No | A severity key from the workspace configuration. |
| `slack_channel_id` | `string` | No | The stored Slack channel ID. |
| `meet_url` | `string` | No | The absolute HTTP or HTTPS meeting URL. |
| `doc_url` | `string` | No | An absolute HTTP or HTTPS URL for an incident document or source. |
| `status` | `string` | No | A workflow-status key from the workspace configuration. New clients use the resolve, close, and reopen operations for lifecycle changes. |
| `issue_type` | `string` | No | The work type. Existing alert records remain available for compatibility. Allowed values: `"ticket"` \| `"incident"` \| `"postmortem"` |
| `team_id` | `integer \| null` | No | The positive ID of an active owning team. A null value removes the current team assignment. |
| `severity_mode` | `string` | No | The severity selection mode. Allowed values: `"auto"` \| `"manual"` |
| `impact_summary` | `string` | No | The customer or operational impact summary. |
| `customer_impact_status` | `string` | No | The customer-impact assessment. Allowed values: `"unknown"` \| `"none"` \| `"confirmed"` |
| `impact_started_at` | `string (RFC 3339)` | No | The RFC 3339 impact start time. |
| `first_reported_at` | `string (RFC 3339)` | No | The RFC 3339 time of the first incident report. |
| `first_responded_at` | `string (RFC 3339)` | No | The RFC 3339 time of the first response. |
| `recovered_at` | `string (RFC 3339)` | No | The RFC 3339 service recovery time. |

Request JSON:

```json
{
  "title": "Payments API recovered; monitoring authorization backlog",
  "impact_summary": "Full payment outage in EU region"
}
```

#### 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 |
| --- | --- | --- | --- |
| `incident` | `object` | Always | The complete public record for an incident. |
| `incident.id` | `integer` | Always | The positive numeric ID of the incident. Minimum: 1. |
| `incident.revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `incident.company_id` | `integer` | Always | The ID of the workspace that owns the incident. Minimum: 1. |
| `incident.human_id` | `string` | Always | The permanent incident reference shown to users. |
| `incident.title` | `string` | Always | The editable display title of the incident. |
| `incident.service` | `string` | Always | The service slug associated with the incident. |
| `incident.regions` | `string[]` | Always | The affected regions. |
| `incident.severity` | `string` | Always | The configured severity key. |
| `incident.slack_channel_id` | `string` | Always | The linked Slack channel ID. An empty string means that no channel is linked. |
| `incident.meet_url` | `string` | Always | The incident meeting URL. An empty string means that no meeting is linked. |
| `incident.doc_url` | `string` | Always | The incident document or source URL. An empty string means that no URL is stored. |
| `incident.status` | `string` | Always | The configured workflow-status key. |
| `incident.status_changed_at` | `string (RFC 3339) \| null` | Always | The time of the latest workflow-status change. A null value means that this time is unavailable. |
| `incident.last_event_at` | `string (RFC 3339) \| null` | Always | The start time of the latest timeline event. A null value means that no event exists. |
| `incident.last_event_summary` | `string` | Always | A summary of the latest timeline event. |
| `incident.issue_type` | `string` | Always | The work type. Possible values: `"ticket"` \| `"incident"` \| `"postmortem"` |
| `incident.severity_mode` | `string` | Always | The severity selection mode. Possible values: `"auto"` \| `"manual"` |
| `incident.impact_summary` | `string` | Always | The current customer or operational impact summary. |
| `incident.customer_impact_status` | `string` | Always | The customer-impact assessment. Possible values: `"unknown"` \| `"none"` \| `"confirmed"` |
| `incident.impact_started_at` | `string (RFC 3339) \| null` | Always | The impact start time. A null value means that the time is unknown. |
| `incident.first_reported_at` | `string (RFC 3339) \| null` | Always | The time of the first incident report. A null value means that the time is unknown. |
| `incident.first_responded_at` | `string (RFC 3339) \| null` | Always | The time of the first response. A null value means that the time is unknown. |
| `incident.recovered_at` | `string (RFC 3339) \| null` | Always | The service recovery time. A null value means that recovery is not recorded. |
| `incident.duration_seconds` | `integer` | Always | The calculated incident duration in whole seconds. Minimum: 0. |
| `incident.duration_anchor` | `string` | Always | The timestamp field from which the API calculates duration. |
| `incident.team_id` | `integer \| null` | Always | The owning-team ID. A null value means that the incident has no owning team. |
| `incident.event_count` | `integer` | Always | The number of timeline events for the incident. Minimum: 0. |
| `incident.investigation_summary` | `object \| null` | Always | A summary of investigation runs. A null value means that no investigation data exists. Must match exactly one of 2 schema variants. |
| `incident.investigation_summary.run_count` | `integer` | Always | The total number of investigation runs. Minimum: 0. |
| `incident.investigation_summary.latest_run` | `object \| null` | Always | The most recent investigation run. A null value means that no run exists. Must match exactly one of 2 schema variants. |
| `incident.investigation_summary.latest_run.status` | `string` | Always | The current investigation-run status. |
| `incident.investigation_summary.latest_run.started_at` | `string (RFC 3339)` | Always | The investigation-run start time. |
| `incident.investigation_summary.latest_run.completed_at` | `string (RFC 3339)` | Optional | The investigation-run completion time. |
| `incident.created_at` | `string (RFC 3339)` | Always | The incident creation time. |
| `incident.created_by` | `string` | Always | The identifier of the user who created the incident. |
| `incident.custom_fields` | `object` | Always | The incident values keyed by custom-field slug. |
| `incident.archived_at` | `string (RFC 3339) \| null` | Always | The incident archive time. A null value means that the incident is active. |
| `incident.archived_by` | `integer \| null` | Always | The user ID that archived the incident. A null value means that the incident is active. |
| `incident.incident_manager` | `string` | Always | The user identifier of the incident manager. An empty string means that no manager is assigned. |
| `incident.incident_responders` | `string[]` | Always | The user identifiers of assigned responders. |
| `incident.has_assignees` | `boolean` | Always | Indicates whether the incident has a manager or at least one responder. |
| `timeline_event` | `object` | Optional | The complete public record for a timeline event. |
| `timeline_event.id` | `integer` | Always | The positive numeric ID of the timeline event. |
| `timeline_event.revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `timeline_event.incident_id` | `integer` | Always | The positive numeric ID of the incident. |
| `timeline_event.started_at` | `string (RFC 3339)` | Always | The event start time. |
| `timeline_event.at` | `string (RFC 3339)` | Always | A deprecated response alias for started_at. Both fields contain the same value. |
| `timeline_event.ended_at` | `string (RFC 3339) \| null` | Always | The event end time. A null value means that the event has no end time. |
| `timeline_event.is_ongoing` | `boolean` | Always | Indicates whether the event is still in progress. |
| `timeline_event.group_id` | `integer \| null` | Always | The timeline-group ID. A null value means that the event has no group. |
| `timeline_event.is_key` | `boolean` | Always | Indicates whether responders marked this as a key incident event. |
| `timeline_event.source` | `string` | Always | The system or actor that created the event. |
| `timeline_event.source_ref` | `string` | Always | The source-specific reference for the event. |
| `timeline_event.type` | `string` | Always | The event type used for grouping and display. |
| `timeline_event.icon` | `string` | Always | The event icon. The maximum length is 8 Unicode characters. Maximum length: 8 characters. |
| `timeline_event.title` | `string` | Always | The optional display title of the event. |
| `timeline_event.body` | `string` | Always | The event description. |
| `timeline_event.collaboration_generation` | `integer` | Always | The version of collaborative content associated with the event. |
| `timeline_event.tags` | `string[]` | Always | The normalized event tags. |
| `timeline_event.ai_context` | `string` | Always | The saved AI context for a generated event. An empty string means that no context is stored. |
| `timeline_event.created_by` | `string` | Always | The identifier of the user or system that created the event. |
| `timeline_event.comment_count` | `integer` | Always | The number of comments on the event. |
| `timeline_event.attachments` | `object[]` | Always | The files attached to the event. |
| `timeline_event.attachments[].id` | `integer` | Always | The positive numeric ID of the attachment. |
| `timeline_event.attachments[].timeline_event_id` | `integer` | Always | The positive numeric ID of the timeline event. |
| `timeline_event.attachments[].filename` | `string` | Always | The original file name. |
| `timeline_event.attachments[].url` | `string` | Always | A temporary download URL. An empty string means that no URL is currently available. |
| `timeline_event.attachments[].url_available` | `boolean` | Always | Indicates whether url contains an available download URL. |
| `timeline_event.attachments[].content_type` | `string` | Always | The media type of the attached file. |
| `timeline_event.attachments[].size_bytes` | `integer` | Always | The file size in bytes. |
| `timeline_event.attachments[].created_at` | `string (RFC 3339)` | Always | The attachment creation time. |
| `timeline_event.attachments[].created_by` | `string` | Always | The identifier of the user who attached the file. |
| `timeline_event.source_window_started_at` | `string (RFC 3339) \| null` | Always | The start of the source-data window. A null value means that no window is recorded. |
| `timeline_event.source_window_ended_at` | `string (RFC 3339) \| null` | Always | The end of the source-data window. A null value means that no window is recorded. |
| `timeline_event.evidence_links` | `object[]` | Always | The evidence references associated with the event. |
| `timeline_event.evidence_links[].source` | `string` | Always | The system or provider that supplied the evidence. |
| `timeline_event.evidence_links[].url` | `string` | Always | The absolute URL of the evidence. |
| `timeline_event.evidence_links[].label` | `string` | Optional | A short display name for the evidence. |
| `timeline_event.evidence_links[].summary` | `string` | Optional | A short summary of the evidence. |
| `timeline_event.evidence_links[].artifact_id` | `string` | Optional | The identifier of a stored evidence artifact. |
| `timeline_event.evidence_links[].artifact_digest` | `string` | Optional | The digest used to make sure that the artifact content did not change. |
| `timeline_event.evidence_links[].result_bytes` | `integer` | Optional | The original evidence result size in bytes. |
| `timeline_event.evidence_links[].truncated` | `boolean` | Optional | Indicates whether the stored evidence omits part of the original result. |

Returns the complete updated incident. A transition to resolved changes issue_type to postmortem. A transition from resolved to active changes issue_type to incident.

```json
{
  "incident": {
    "id": 1,
    "revision": 7,
    "company_id": 7,
    "human_id": "INC-2025-001",
    "title": "Payments API recovered; monitoring authorization backlog",
    "service": "payments",
    "regions": [
      "us-east-1"
    ],
    "severity": "sev1",
    "slack_channel_id": "C08ABC123",
    "meet_url": "https://meet.google.com/abc-defg-hij",
    "doc_url": "https://docs.google.com/d/1abc",
    "status": "active",
    "status_changed_at": "2025-01-30T12:00:00Z",
    "last_event_at": "2025-01-30T13:30:00Z",
    "last_event_summary": "Service fully recovered",
    "issue_type": "incident",
    "severity_mode": "manual",
    "impact_summary": "Full payment outage in EU region",
    "customer_impact_status": "confirmed",
    "impact_started_at": "2025-01-30T11:55:00Z",
    "first_reported_at": "2025-01-30T11:58:00Z",
    "first_responded_at": "2025-01-30T12:00:00Z",
    "recovered_at": "2025-01-30T13:30:00Z",
    "duration_seconds": 5700,
    "duration_anchor": "impact_started_at",
    "team_id": 42,
    "event_count": 12,
    "investigation_summary": null,
    "created_at": "2025-01-30T12:00:00Z",
    "created_by": "oncall@example.com",
    "custom_fields": {},
    "archived_at": null,
    "archived_by": null,
    "incident_manager": "oncall@example.com",
    "incident_responders": [
      "responder@example.com"
    ],
    "has_assignees": true
  }
}
```

#### 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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

#### Related operations

- [Resolve incident](https://tellagen.com/api-reference/incidents#incidents-endpoint-post-api-v1-incidents-id-resolve) (`POST /api/v1/incidents/{id}/resolve`)
- [Close incident without resolution](https://tellagen.com/api-reference/incidents#incidents-endpoint-post-api-v1-incidents-id-close) (`POST /api/v1/incidents/{id}/close`)
- [Reopen incident](https://tellagen.com/api-reference/incidents#incidents-endpoint-post-api-v1-incidents-id-reopen) (`POST /api/v1/incidents/{id}/reopen`)

### `POST /api/v1/incidents/{id}/resolve`

**Resolve incident.** Changes an incident to resolved and changes issue_type to postmortem. The API saves the status, optional recovery edit, and resolution note in a lifecycle event atomically. Existing postmortem drafts and versions remain unchanged. The request body is optional. A stale expected_revision returns 412. An already-resolved incident accepts an empty retry without creating another event; a retry with a note or recovery edit returns 412.

Required scope: `incidents:write`

Operation ID: `resolveIncident`
Contract: `supported`
Retry guidance: Do not retry automatically after an uncertain outcome. Read the resource state and follow this operation’s recovery guidance.
Prerequisites: Active responder seat, Incident is not archived

#### 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 incident ID returned by this API. The permanent incident reference is suitable for stored references and external links. 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/incidents/{id}/resolve' \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '{
  "resolution_note": "Rolled back the deployment and verified payment processing.",
  "recovered_at": "2025-01-30T12:04:00Z",
  "expected_revision": 6
}'
```

#### Request body

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

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `resolution_note` | `string` | No | An optional note stored in the resolution timeline event. Leading and trailing whitespace is removed before enforcing the 1000 Unicode character limit. |
| `recovered_at` | `string` | No | The recovery timestamp in RFC 3339 format. Omit to preserve the current value. An empty string clears it. The timestamp must follow recorded impact, report, and response times and must not be in the future. |
| `expected_revision` | `integer` | No | The incident revision displayed to the responder. A mismatch returns 412 without saving any changes. |

Request JSON:

```json
{
  "resolution_note": "Rolled back the deployment and verified payment processing.",
  "recovered_at": "2025-01-30T12:04:00Z",
  "expected_revision": 6
}
```

#### 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 |
| --- | --- | --- | --- |
| `incident` | `object` | Always | The complete public record for an incident. |
| `incident.id` | `integer` | Always | The positive numeric ID of the incident. Minimum: 1. |
| `incident.revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `incident.company_id` | `integer` | Always | The ID of the workspace that owns the incident. Minimum: 1. |
| `incident.human_id` | `string` | Always | The permanent incident reference shown to users. |
| `incident.title` | `string` | Always | The editable display title of the incident. |
| `incident.service` | `string` | Always | The service slug associated with the incident. |
| `incident.regions` | `string[]` | Always | The affected regions. |
| `incident.severity` | `string` | Always | The configured severity key. |
| `incident.slack_channel_id` | `string` | Always | The linked Slack channel ID. An empty string means that no channel is linked. |
| `incident.meet_url` | `string` | Always | The incident meeting URL. An empty string means that no meeting is linked. |
| `incident.doc_url` | `string` | Always | The incident document or source URL. An empty string means that no URL is stored. |
| `incident.status` | `string` | Always | The configured workflow-status key. |
| `incident.status_changed_at` | `string (RFC 3339) \| null` | Always | The time of the latest workflow-status change. A null value means that this time is unavailable. |
| `incident.last_event_at` | `string (RFC 3339) \| null` | Always | The start time of the latest timeline event. A null value means that no event exists. |
| `incident.last_event_summary` | `string` | Always | A summary of the latest timeline event. |
| `incident.issue_type` | `string` | Always | The work type. Possible values: `"ticket"` \| `"incident"` \| `"postmortem"` |
| `incident.severity_mode` | `string` | Always | The severity selection mode. Possible values: `"auto"` \| `"manual"` |
| `incident.impact_summary` | `string` | Always | The current customer or operational impact summary. |
| `incident.customer_impact_status` | `string` | Always | The customer-impact assessment. Possible values: `"unknown"` \| `"none"` \| `"confirmed"` |
| `incident.impact_started_at` | `string (RFC 3339) \| null` | Always | The impact start time. A null value means that the time is unknown. |
| `incident.first_reported_at` | `string (RFC 3339) \| null` | Always | The time of the first incident report. A null value means that the time is unknown. |
| `incident.first_responded_at` | `string (RFC 3339) \| null` | Always | The time of the first response. A null value means that the time is unknown. |
| `incident.recovered_at` | `string (RFC 3339) \| null` | Always | The service recovery time. A null value means that recovery is not recorded. |
| `incident.duration_seconds` | `integer` | Always | The calculated incident duration in whole seconds. Minimum: 0. |
| `incident.duration_anchor` | `string` | Always | The timestamp field from which the API calculates duration. |
| `incident.team_id` | `integer \| null` | Always | The owning-team ID. A null value means that the incident has no owning team. |
| `incident.event_count` | `integer` | Always | The number of timeline events for the incident. Minimum: 0. |
| `incident.investigation_summary` | `object \| null` | Always | A summary of investigation runs. A null value means that no investigation data exists. Must match exactly one of 2 schema variants. |
| `incident.investigation_summary.run_count` | `integer` | Always | The total number of investigation runs. Minimum: 0. |
| `incident.investigation_summary.latest_run` | `object \| null` | Always | The most recent investigation run. A null value means that no run exists. Must match exactly one of 2 schema variants. |
| `incident.investigation_summary.latest_run.status` | `string` | Always | The current investigation-run status. |
| `incident.investigation_summary.latest_run.started_at` | `string (RFC 3339)` | Always | The investigation-run start time. |
| `incident.investigation_summary.latest_run.completed_at` | `string (RFC 3339)` | Optional | The investigation-run completion time. |
| `incident.created_at` | `string (RFC 3339)` | Always | The incident creation time. |
| `incident.created_by` | `string` | Always | The identifier of the user who created the incident. |
| `incident.custom_fields` | `object` | Always | The incident values keyed by custom-field slug. |
| `incident.archived_at` | `string (RFC 3339) \| null` | Always | The incident archive time. A null value means that the incident is active. |
| `incident.archived_by` | `integer \| null` | Always | The user ID that archived the incident. A null value means that the incident is active. |
| `incident.incident_manager` | `string` | Always | The user identifier of the incident manager. An empty string means that no manager is assigned. |
| `incident.incident_responders` | `string[]` | Always | The user identifiers of assigned responders. |
| `incident.has_assignees` | `boolean` | Always | Indicates whether the incident has a manager or at least one responder. |
| `timeline_event` | `object` | Optional | The complete public record for a timeline event. |
| `timeline_event.id` | `integer` | Always | The positive numeric ID of the timeline event. |
| `timeline_event.revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `timeline_event.incident_id` | `integer` | Always | The positive numeric ID of the incident. |
| `timeline_event.started_at` | `string (RFC 3339)` | Always | The event start time. |
| `timeline_event.at` | `string (RFC 3339)` | Always | A deprecated response alias for started_at. Both fields contain the same value. |
| `timeline_event.ended_at` | `string (RFC 3339) \| null` | Always | The event end time. A null value means that the event has no end time. |
| `timeline_event.is_ongoing` | `boolean` | Always | Indicates whether the event is still in progress. |
| `timeline_event.group_id` | `integer \| null` | Always | The timeline-group ID. A null value means that the event has no group. |
| `timeline_event.is_key` | `boolean` | Always | Indicates whether responders marked this as a key incident event. |
| `timeline_event.source` | `string` | Always | The system or actor that created the event. |
| `timeline_event.source_ref` | `string` | Always | The source-specific reference for the event. |
| `timeline_event.type` | `string` | Always | The event type used for grouping and display. |
| `timeline_event.icon` | `string` | Always | The event icon. The maximum length is 8 Unicode characters. Maximum length: 8 characters. |
| `timeline_event.title` | `string` | Always | The optional display title of the event. |
| `timeline_event.body` | `string` | Always | The event description. |
| `timeline_event.collaboration_generation` | `integer` | Always | The version of collaborative content associated with the event. |
| `timeline_event.tags` | `string[]` | Always | The normalized event tags. |
| `timeline_event.ai_context` | `string` | Always | The saved AI context for a generated event. An empty string means that no context is stored. |
| `timeline_event.created_by` | `string` | Always | The identifier of the user or system that created the event. |
| `timeline_event.comment_count` | `integer` | Always | The number of comments on the event. |
| `timeline_event.attachments` | `object[]` | Always | The files attached to the event. |
| `timeline_event.attachments[].id` | `integer` | Always | The positive numeric ID of the attachment. |
| `timeline_event.attachments[].timeline_event_id` | `integer` | Always | The positive numeric ID of the timeline event. |
| `timeline_event.attachments[].filename` | `string` | Always | The original file name. |
| `timeline_event.attachments[].url` | `string` | Always | A temporary download URL. An empty string means that no URL is currently available. |
| `timeline_event.attachments[].url_available` | `boolean` | Always | Indicates whether url contains an available download URL. |
| `timeline_event.attachments[].content_type` | `string` | Always | The media type of the attached file. |
| `timeline_event.attachments[].size_bytes` | `integer` | Always | The file size in bytes. |
| `timeline_event.attachments[].created_at` | `string (RFC 3339)` | Always | The attachment creation time. |
| `timeline_event.attachments[].created_by` | `string` | Always | The identifier of the user who attached the file. |
| `timeline_event.source_window_started_at` | `string (RFC 3339) \| null` | Always | The start of the source-data window. A null value means that no window is recorded. |
| `timeline_event.source_window_ended_at` | `string (RFC 3339) \| null` | Always | The end of the source-data window. A null value means that no window is recorded. |
| `timeline_event.evidence_links` | `object[]` | Always | The evidence references associated with the event. |
| `timeline_event.evidence_links[].source` | `string` | Always | The system or provider that supplied the evidence. |
| `timeline_event.evidence_links[].url` | `string` | Always | The absolute URL of the evidence. |
| `timeline_event.evidence_links[].label` | `string` | Optional | A short display name for the evidence. |
| `timeline_event.evidence_links[].summary` | `string` | Optional | A short summary of the evidence. |
| `timeline_event.evidence_links[].artifact_id` | `string` | Optional | The identifier of a stored evidence artifact. |
| `timeline_event.evidence_links[].artifact_digest` | `string` | Optional | The digest used to make sure that the artifact content did not change. |
| `timeline_event.evidence_links[].result_bytes` | `integer` | Optional | The original evidence result size in bytes. |
| `timeline_event.evidence_links[].truncated` | `boolean` | Optional | Indicates whether the stored evidence omits part of the original result. |

Returns the complete incident and the committed resolution timeline event.

```json
{
  "incident": {
    "id": 1,
    "revision": 7,
    "company_id": 7,
    "human_id": "INC-2025-001",
    "title": "Payments API requests timing out",
    "service": "payments",
    "regions": [
      "us-east-1"
    ],
    "severity": "sev1",
    "slack_channel_id": "C08ABC123",
    "meet_url": "https://meet.google.com/abc-defg-hij",
    "doc_url": "https://docs.google.com/d/1abc",
    "status": "resolved",
    "status_changed_at": "2025-01-30T12:05:00Z",
    "last_event_at": "2025-01-30T12:10:00Z",
    "last_event_summary": "Investigating root cause",
    "issue_type": "postmortem",
    "severity_mode": "manual",
    "impact_summary": "Payment processing delayed for EU customers",
    "customer_impact_status": "confirmed",
    "impact_started_at": "2025-01-30T11:55:00Z",
    "first_reported_at": "2025-01-30T11:58:00Z",
    "first_responded_at": "2025-01-30T12:00:00Z",
    "recovered_at": "2025-01-30T13:30:00Z",
    "duration_seconds": 900,
    "duration_anchor": "impact_started_at",
    "team_id": 42,
    "event_count": 5,
    "investigation_summary": null,
    "created_at": "2025-01-30T12:00:00Z",
    "created_by": "oncall@example.com",
    "custom_fields": {},
    "archived_at": null,
    "archived_by": null,
    "incident_manager": "oncall@example.com",
    "incident_responders": [
      "responder@example.com"
    ],
    "has_assignees": true
  }
}
```

#### 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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

### `POST /api/v1/incidents/{id}/close`

**Close incident without resolution.** Closes an incident from a non-terminal workflow status without marking it resolved. The API records the reason and comment in a lifecycle event.

Required scope: `incidents:write`

Operation ID: `closeIncident`
Contract: `supported`
Retry guidance: Do not retry automatically after an uncertain outcome. Read the resource state and follow this operation’s recovery guidance.
Prerequisites: Active responder seat, Incident is not archived

#### 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 incident ID returned by this API. The permanent incident reference is suitable for stored references and external links. 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/incidents/{id}/close' \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '{
  "reason": "duplicate",
  "comment": "Tracked by the primary payment incident.",
  "related_incident_id": 99
}'
```

#### Request body

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

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `reason` | `string` | Yes | The closure reason. Allowed values: `"duplicate"` \| `"false_alarm"` \| `"test_or_accidental"` \| `"not_an_incident"` \| `"other"` |
| `comment` | `string` | Yes | The explanation for the closure. The maximum length is 1000 characters. Minimum length: 1 characters. Maximum length: 1000 characters. |
| `related_incident_id` | `integer` | Conditional | The positive ID of the primary incident. If reason is duplicate, this field is required. Required when: Required when reason is "duplicate". Minimum: 1. |

Request JSON:

```json
{
  "reason": "duplicate",
  "comment": "Tracked by the primary payment incident.",
  "related_incident_id": 99
}
```

#### 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 |
| --- | --- | --- | --- |
| `incident` | `object` | Always | The complete public record for an incident. |
| `incident.id` | `integer` | Always | The positive numeric ID of the incident. Minimum: 1. |
| `incident.revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `incident.company_id` | `integer` | Always | The ID of the workspace that owns the incident. Minimum: 1. |
| `incident.human_id` | `string` | Always | The permanent incident reference shown to users. |
| `incident.title` | `string` | Always | The editable display title of the incident. |
| `incident.service` | `string` | Always | The service slug associated with the incident. |
| `incident.regions` | `string[]` | Always | The affected regions. |
| `incident.severity` | `string` | Always | The configured severity key. |
| `incident.slack_channel_id` | `string` | Always | The linked Slack channel ID. An empty string means that no channel is linked. |
| `incident.meet_url` | `string` | Always | The incident meeting URL. An empty string means that no meeting is linked. |
| `incident.doc_url` | `string` | Always | The incident document or source URL. An empty string means that no URL is stored. |
| `incident.status` | `string` | Always | The configured workflow-status key. |
| `incident.status_changed_at` | `string (RFC 3339) \| null` | Always | The time of the latest workflow-status change. A null value means that this time is unavailable. |
| `incident.last_event_at` | `string (RFC 3339) \| null` | Always | The start time of the latest timeline event. A null value means that no event exists. |
| `incident.last_event_summary` | `string` | Always | A summary of the latest timeline event. |
| `incident.issue_type` | `string` | Always | The work type. Possible values: `"ticket"` \| `"incident"` \| `"postmortem"` |
| `incident.severity_mode` | `string` | Always | The severity selection mode. Possible values: `"auto"` \| `"manual"` |
| `incident.impact_summary` | `string` | Always | The current customer or operational impact summary. |
| `incident.customer_impact_status` | `string` | Always | The customer-impact assessment. Possible values: `"unknown"` \| `"none"` \| `"confirmed"` |
| `incident.impact_started_at` | `string (RFC 3339) \| null` | Always | The impact start time. A null value means that the time is unknown. |
| `incident.first_reported_at` | `string (RFC 3339) \| null` | Always | The time of the first incident report. A null value means that the time is unknown. |
| `incident.first_responded_at` | `string (RFC 3339) \| null` | Always | The time of the first response. A null value means that the time is unknown. |
| `incident.recovered_at` | `string (RFC 3339) \| null` | Always | The service recovery time. A null value means that recovery is not recorded. |
| `incident.duration_seconds` | `integer` | Always | The calculated incident duration in whole seconds. Minimum: 0. |
| `incident.duration_anchor` | `string` | Always | The timestamp field from which the API calculates duration. |
| `incident.team_id` | `integer \| null` | Always | The owning-team ID. A null value means that the incident has no owning team. |
| `incident.event_count` | `integer` | Always | The number of timeline events for the incident. Minimum: 0. |
| `incident.investigation_summary` | `object \| null` | Always | A summary of investigation runs. A null value means that no investigation data exists. Must match exactly one of 2 schema variants. |
| `incident.investigation_summary.run_count` | `integer` | Always | The total number of investigation runs. Minimum: 0. |
| `incident.investigation_summary.latest_run` | `object \| null` | Always | The most recent investigation run. A null value means that no run exists. Must match exactly one of 2 schema variants. |
| `incident.investigation_summary.latest_run.status` | `string` | Always | The current investigation-run status. |
| `incident.investigation_summary.latest_run.started_at` | `string (RFC 3339)` | Always | The investigation-run start time. |
| `incident.investigation_summary.latest_run.completed_at` | `string (RFC 3339)` | Optional | The investigation-run completion time. |
| `incident.created_at` | `string (RFC 3339)` | Always | The incident creation time. |
| `incident.created_by` | `string` | Always | The identifier of the user who created the incident. |
| `incident.custom_fields` | `object` | Always | The incident values keyed by custom-field slug. |
| `incident.archived_at` | `string (RFC 3339) \| null` | Always | The incident archive time. A null value means that the incident is active. |
| `incident.archived_by` | `integer \| null` | Always | The user ID that archived the incident. A null value means that the incident is active. |
| `incident.incident_manager` | `string` | Always | The user identifier of the incident manager. An empty string means that no manager is assigned. |
| `incident.incident_responders` | `string[]` | Always | The user identifiers of assigned responders. |
| `incident.has_assignees` | `boolean` | Always | Indicates whether the incident has a manager or at least one responder. |
| `timeline_event` | `object` | Optional | The complete public record for a timeline event. |
| `timeline_event.id` | `integer` | Always | The positive numeric ID of the timeline event. |
| `timeline_event.revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `timeline_event.incident_id` | `integer` | Always | The positive numeric ID of the incident. |
| `timeline_event.started_at` | `string (RFC 3339)` | Always | The event start time. |
| `timeline_event.at` | `string (RFC 3339)` | Always | A deprecated response alias for started_at. Both fields contain the same value. |
| `timeline_event.ended_at` | `string (RFC 3339) \| null` | Always | The event end time. A null value means that the event has no end time. |
| `timeline_event.is_ongoing` | `boolean` | Always | Indicates whether the event is still in progress. |
| `timeline_event.group_id` | `integer \| null` | Always | The timeline-group ID. A null value means that the event has no group. |
| `timeline_event.is_key` | `boolean` | Always | Indicates whether responders marked this as a key incident event. |
| `timeline_event.source` | `string` | Always | The system or actor that created the event. |
| `timeline_event.source_ref` | `string` | Always | The source-specific reference for the event. |
| `timeline_event.type` | `string` | Always | The event type used for grouping and display. |
| `timeline_event.icon` | `string` | Always | The event icon. The maximum length is 8 Unicode characters. Maximum length: 8 characters. |
| `timeline_event.title` | `string` | Always | The optional display title of the event. |
| `timeline_event.body` | `string` | Always | The event description. |
| `timeline_event.collaboration_generation` | `integer` | Always | The version of collaborative content associated with the event. |
| `timeline_event.tags` | `string[]` | Always | The normalized event tags. |
| `timeline_event.ai_context` | `string` | Always | The saved AI context for a generated event. An empty string means that no context is stored. |
| `timeline_event.created_by` | `string` | Always | The identifier of the user or system that created the event. |
| `timeline_event.comment_count` | `integer` | Always | The number of comments on the event. |
| `timeline_event.attachments` | `object[]` | Always | The files attached to the event. |
| `timeline_event.attachments[].id` | `integer` | Always | The positive numeric ID of the attachment. |
| `timeline_event.attachments[].timeline_event_id` | `integer` | Always | The positive numeric ID of the timeline event. |
| `timeline_event.attachments[].filename` | `string` | Always | The original file name. |
| `timeline_event.attachments[].url` | `string` | Always | A temporary download URL. An empty string means that no URL is currently available. |
| `timeline_event.attachments[].url_available` | `boolean` | Always | Indicates whether url contains an available download URL. |
| `timeline_event.attachments[].content_type` | `string` | Always | The media type of the attached file. |
| `timeline_event.attachments[].size_bytes` | `integer` | Always | The file size in bytes. |
| `timeline_event.attachments[].created_at` | `string (RFC 3339)` | Always | The attachment creation time. |
| `timeline_event.attachments[].created_by` | `string` | Always | The identifier of the user who attached the file. |
| `timeline_event.source_window_started_at` | `string (RFC 3339) \| null` | Always | The start of the source-data window. A null value means that no window is recorded. |
| `timeline_event.source_window_ended_at` | `string (RFC 3339) \| null` | Always | The end of the source-data window. A null value means that no window is recorded. |
| `timeline_event.evidence_links` | `object[]` | Always | The evidence references associated with the event. |
| `timeline_event.evidence_links[].source` | `string` | Always | The system or provider that supplied the evidence. |
| `timeline_event.evidence_links[].url` | `string` | Always | The absolute URL of the evidence. |
| `timeline_event.evidence_links[].label` | `string` | Optional | A short display name for the evidence. |
| `timeline_event.evidence_links[].summary` | `string` | Optional | A short summary of the evidence. |
| `timeline_event.evidence_links[].artifact_id` | `string` | Optional | The identifier of a stored evidence artifact. |
| `timeline_event.evidence_links[].artifact_digest` | `string` | Optional | The digest used to make sure that the artifact content did not change. |
| `timeline_event.evidence_links[].result_bytes` | `integer` | Optional | The original evidence result size in bytes. |
| `timeline_event.evidence_links[].truncated` | `boolean` | Optional | Indicates whether the stored evidence omits part of the original result. |

Returns the complete incident after closure.

```json
{
  "incident": {
    "id": 1,
    "revision": 7,
    "company_id": 7,
    "human_id": "INC-2025-001",
    "title": "Payments API requests timing out",
    "service": "payments",
    "regions": [
      "us-east-1"
    ],
    "severity": "sev1",
    "slack_channel_id": "C08ABC123",
    "meet_url": "https://meet.google.com/abc-defg-hij",
    "doc_url": "https://docs.google.com/d/1abc",
    "status": "closed",
    "status_changed_at": "2025-01-30T12:05:00Z",
    "last_event_at": "2025-01-30T12:10:00Z",
    "last_event_summary": "Investigating root cause",
    "issue_type": "incident",
    "severity_mode": "manual",
    "impact_summary": "Payment processing delayed for EU customers",
    "customer_impact_status": "confirmed",
    "impact_started_at": "2025-01-30T11:55:00Z",
    "first_reported_at": "2025-01-30T11:58:00Z",
    "first_responded_at": "2025-01-30T12:00:00Z",
    "recovered_at": null,
    "duration_seconds": 900,
    "duration_anchor": "impact_started_at",
    "team_id": 42,
    "event_count": 5,
    "investigation_summary": null,
    "created_at": "2025-01-30T12:00:00Z",
    "created_by": "oncall@example.com",
    "custom_fields": {},
    "archived_at": null,
    "archived_by": null,
    "incident_manager": "oncall@example.com",
    "incident_responders": [
      "responder@example.com"
    ],
    "has_assignees": true
  }
}
```

#### 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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

### `POST /api/v1/incidents/{id}/reopen`

**Reopen incident.** Changes a resolved or closed incident to active. A closed incident requires a comment. A comment is optional for a resolved incident. If issue_type is postmortem, the API changes it to incident.

Required scope: `incidents:write`

Operation ID: `reopenIncident`
Contract: `supported`
Retry guidance: Do not retry automatically after an uncertain outcome. Read the resource state and follow this operation’s recovery guidance.
Prerequisites: Active responder seat, Incident is not archived

#### 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 incident ID returned by this API. The permanent incident reference is suitable for stored references and external links. 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/incidents/{id}/reopen' \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '{
  "comment": "Customer-visible errors returned."
}'
```

#### Request body

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

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `comment` | `string` | Conditional | The reason for reopening. A closed incident requires this field. The maximum length is 500 characters. Required when: Required when reopening a closed incident. Maximum length: 500 characters. |

Request JSON:

```json
{
  "comment": "Customer-visible errors returned."
}
```

#### 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 |
| --- | --- | --- | --- |
| `incident` | `object` | Always | The complete public record for an incident. |
| `incident.id` | `integer` | Always | The positive numeric ID of the incident. Minimum: 1. |
| `incident.revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `incident.company_id` | `integer` | Always | The ID of the workspace that owns the incident. Minimum: 1. |
| `incident.human_id` | `string` | Always | The permanent incident reference shown to users. |
| `incident.title` | `string` | Always | The editable display title of the incident. |
| `incident.service` | `string` | Always | The service slug associated with the incident. |
| `incident.regions` | `string[]` | Always | The affected regions. |
| `incident.severity` | `string` | Always | The configured severity key. |
| `incident.slack_channel_id` | `string` | Always | The linked Slack channel ID. An empty string means that no channel is linked. |
| `incident.meet_url` | `string` | Always | The incident meeting URL. An empty string means that no meeting is linked. |
| `incident.doc_url` | `string` | Always | The incident document or source URL. An empty string means that no URL is stored. |
| `incident.status` | `string` | Always | The configured workflow-status key. |
| `incident.status_changed_at` | `string (RFC 3339) \| null` | Always | The time of the latest workflow-status change. A null value means that this time is unavailable. |
| `incident.last_event_at` | `string (RFC 3339) \| null` | Always | The start time of the latest timeline event. A null value means that no event exists. |
| `incident.last_event_summary` | `string` | Always | A summary of the latest timeline event. |
| `incident.issue_type` | `string` | Always | The work type. Possible values: `"ticket"` \| `"incident"` \| `"postmortem"` |
| `incident.severity_mode` | `string` | Always | The severity selection mode. Possible values: `"auto"` \| `"manual"` |
| `incident.impact_summary` | `string` | Always | The current customer or operational impact summary. |
| `incident.customer_impact_status` | `string` | Always | The customer-impact assessment. Possible values: `"unknown"` \| `"none"` \| `"confirmed"` |
| `incident.impact_started_at` | `string (RFC 3339) \| null` | Always | The impact start time. A null value means that the time is unknown. |
| `incident.first_reported_at` | `string (RFC 3339) \| null` | Always | The time of the first incident report. A null value means that the time is unknown. |
| `incident.first_responded_at` | `string (RFC 3339) \| null` | Always | The time of the first response. A null value means that the time is unknown. |
| `incident.recovered_at` | `string (RFC 3339) \| null` | Always | The service recovery time. A null value means that recovery is not recorded. |
| `incident.duration_seconds` | `integer` | Always | The calculated incident duration in whole seconds. Minimum: 0. |
| `incident.duration_anchor` | `string` | Always | The timestamp field from which the API calculates duration. |
| `incident.team_id` | `integer \| null` | Always | The owning-team ID. A null value means that the incident has no owning team. |
| `incident.event_count` | `integer` | Always | The number of timeline events for the incident. Minimum: 0. |
| `incident.investigation_summary` | `object \| null` | Always | A summary of investigation runs. A null value means that no investigation data exists. Must match exactly one of 2 schema variants. |
| `incident.investigation_summary.run_count` | `integer` | Always | The total number of investigation runs. Minimum: 0. |
| `incident.investigation_summary.latest_run` | `object \| null` | Always | The most recent investigation run. A null value means that no run exists. Must match exactly one of 2 schema variants. |
| `incident.investigation_summary.latest_run.status` | `string` | Always | The current investigation-run status. |
| `incident.investigation_summary.latest_run.started_at` | `string (RFC 3339)` | Always | The investigation-run start time. |
| `incident.investigation_summary.latest_run.completed_at` | `string (RFC 3339)` | Optional | The investigation-run completion time. |
| `incident.created_at` | `string (RFC 3339)` | Always | The incident creation time. |
| `incident.created_by` | `string` | Always | The identifier of the user who created the incident. |
| `incident.custom_fields` | `object` | Always | The incident values keyed by custom-field slug. |
| `incident.archived_at` | `string (RFC 3339) \| null` | Always | The incident archive time. A null value means that the incident is active. |
| `incident.archived_by` | `integer \| null` | Always | The user ID that archived the incident. A null value means that the incident is active. |
| `incident.incident_manager` | `string` | Always | The user identifier of the incident manager. An empty string means that no manager is assigned. |
| `incident.incident_responders` | `string[]` | Always | The user identifiers of assigned responders. |
| `incident.has_assignees` | `boolean` | Always | Indicates whether the incident has a manager or at least one responder. |
| `timeline_event` | `object` | Optional | The complete public record for a timeline event. |
| `timeline_event.id` | `integer` | Always | The positive numeric ID of the timeline event. |
| `timeline_event.revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `timeline_event.incident_id` | `integer` | Always | The positive numeric ID of the incident. |
| `timeline_event.started_at` | `string (RFC 3339)` | Always | The event start time. |
| `timeline_event.at` | `string (RFC 3339)` | Always | A deprecated response alias for started_at. Both fields contain the same value. |
| `timeline_event.ended_at` | `string (RFC 3339) \| null` | Always | The event end time. A null value means that the event has no end time. |
| `timeline_event.is_ongoing` | `boolean` | Always | Indicates whether the event is still in progress. |
| `timeline_event.group_id` | `integer \| null` | Always | The timeline-group ID. A null value means that the event has no group. |
| `timeline_event.is_key` | `boolean` | Always | Indicates whether responders marked this as a key incident event. |
| `timeline_event.source` | `string` | Always | The system or actor that created the event. |
| `timeline_event.source_ref` | `string` | Always | The source-specific reference for the event. |
| `timeline_event.type` | `string` | Always | The event type used for grouping and display. |
| `timeline_event.icon` | `string` | Always | The event icon. The maximum length is 8 Unicode characters. Maximum length: 8 characters. |
| `timeline_event.title` | `string` | Always | The optional display title of the event. |
| `timeline_event.body` | `string` | Always | The event description. |
| `timeline_event.collaboration_generation` | `integer` | Always | The version of collaborative content associated with the event. |
| `timeline_event.tags` | `string[]` | Always | The normalized event tags. |
| `timeline_event.ai_context` | `string` | Always | The saved AI context for a generated event. An empty string means that no context is stored. |
| `timeline_event.created_by` | `string` | Always | The identifier of the user or system that created the event. |
| `timeline_event.comment_count` | `integer` | Always | The number of comments on the event. |
| `timeline_event.attachments` | `object[]` | Always | The files attached to the event. |
| `timeline_event.attachments[].id` | `integer` | Always | The positive numeric ID of the attachment. |
| `timeline_event.attachments[].timeline_event_id` | `integer` | Always | The positive numeric ID of the timeline event. |
| `timeline_event.attachments[].filename` | `string` | Always | The original file name. |
| `timeline_event.attachments[].url` | `string` | Always | A temporary download URL. An empty string means that no URL is currently available. |
| `timeline_event.attachments[].url_available` | `boolean` | Always | Indicates whether url contains an available download URL. |
| `timeline_event.attachments[].content_type` | `string` | Always | The media type of the attached file. |
| `timeline_event.attachments[].size_bytes` | `integer` | Always | The file size in bytes. |
| `timeline_event.attachments[].created_at` | `string (RFC 3339)` | Always | The attachment creation time. |
| `timeline_event.attachments[].created_by` | `string` | Always | The identifier of the user who attached the file. |
| `timeline_event.source_window_started_at` | `string (RFC 3339) \| null` | Always | The start of the source-data window. A null value means that no window is recorded. |
| `timeline_event.source_window_ended_at` | `string (RFC 3339) \| null` | Always | The end of the source-data window. A null value means that no window is recorded. |
| `timeline_event.evidence_links` | `object[]` | Always | The evidence references associated with the event. |
| `timeline_event.evidence_links[].source` | `string` | Always | The system or provider that supplied the evidence. |
| `timeline_event.evidence_links[].url` | `string` | Always | The absolute URL of the evidence. |
| `timeline_event.evidence_links[].label` | `string` | Optional | A short display name for the evidence. |
| `timeline_event.evidence_links[].summary` | `string` | Optional | A short summary of the evidence. |
| `timeline_event.evidence_links[].artifact_id` | `string` | Optional | The identifier of a stored evidence artifact. |
| `timeline_event.evidence_links[].artifact_digest` | `string` | Optional | The digest used to make sure that the artifact content did not change. |
| `timeline_event.evidence_links[].result_bytes` | `integer` | Optional | The original evidence result size in bytes. |
| `timeline_event.evidence_links[].truncated` | `boolean` | Optional | Indicates whether the stored evidence omits part of the original result. |

Returns the complete incident after it becomes active.

```json
{
  "incident": {
    "id": 1,
    "revision": 7,
    "company_id": 7,
    "human_id": "INC-2025-001",
    "title": "Payments API requests timing out",
    "service": "payments",
    "regions": [
      "us-east-1"
    ],
    "severity": "sev1",
    "slack_channel_id": "C08ABC123",
    "meet_url": "https://meet.google.com/abc-defg-hij",
    "doc_url": "https://docs.google.com/d/1abc",
    "status": "active",
    "status_changed_at": "2025-01-30T12:05:00Z",
    "last_event_at": "2025-01-30T12:10:00Z",
    "last_event_summary": "Investigating root cause",
    "issue_type": "incident",
    "severity_mode": "manual",
    "impact_summary": "Payment processing delayed for EU customers",
    "customer_impact_status": "confirmed",
    "impact_started_at": "2025-01-30T11:55:00Z",
    "first_reported_at": "2025-01-30T11:58:00Z",
    "first_responded_at": "2025-01-30T12:00:00Z",
    "recovered_at": null,
    "duration_seconds": 900,
    "duration_anchor": "impact_started_at",
    "team_id": 42,
    "event_count": 5,
    "investigation_summary": null,
    "created_at": "2025-01-30T12:00:00Z",
    "created_by": "oncall@example.com",
    "custom_fields": {},
    "archived_at": null,
    "archived_by": null,
    "incident_manager": "oncall@example.com",
    "incident_responders": [
      "responder@example.com"
    ],
    "has_assignees": true
  }
}
```

#### 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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

### `GET /api/v1/incidents/{id}/provisioning`

**Get incident integration setup.** Returns durable integration setup state. Pending work resumes after a server restart. Failed work retries automatically. Completed means all persisted setup steps finished.

Required scope: `incidents:read`

Operation ID: `getIncidentProvisioning`
Contract: `supported`
Retry guidance: This operation is repeat-safe. Respect Retry-After and use bounded retries.
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 incident ID returned by this API. The permanent incident reference is suitable for stored references and external links. 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/incidents/{id}/provisioning' \
  --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 |
| --- | --- | --- | --- |
| `status` | `string` | Always | Aggregate state of persisted incident integration steps. Possible values: `"pending"` \| `"failed"` \| `"completed"` |
| `retryable` | `boolean` | Always | True when a failed step can be made eligible for immediate retry. |

Current setup state. Retryable indicates whether failed steps can be retried manually.

```json
{
  "status": "pending",
  "retryable": 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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

### `POST /api/v1/incidents/{id}/provisioning/retry`

**Retry incident integration setup.** Makes failed setup steps eligible to run again. Completed steps and incident history are not repeated. The caller must have an active responder seat. Demo workspaces cannot retry external setup.

Required scope: `incidents:write`

Operation ID: `retryIncidentProvisioning`
Contract: `supported`
Retry guidance: This operation is repeat-safe. Respect Retry-After and use bounded retries.
Prerequisites: Active responder seat

#### 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 incident ID returned by this API. The permanent incident reference is suitable for stored references and external links. 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/incidents/{id}/provisioning/retry' \
  --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 |
| --- | --- | --- | --- |
| `status` | `string` | Always | Aggregate state of persisted incident integration steps. Possible values: `"pending"` \| `"failed"` \| `"completed"` |
| `retryable` | `boolean` | Always | True when a failed step can be made eligible for immediate retry. |

Current setup state. Retryable indicates whether failed steps can be retried manually.

```json
{
  "status": "pending",
  "retryable": 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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

### `POST /api/v1/incidents/{id}/archive`

**Archive incident.** The caller must have an active responder seat. Archiving removes the incident from default lists without changing its workflow status. A repeated request returns 204 without changing archive details, revision, changelog, or audit records. The API retains incident child records as read-only data.

Required scope: `incidents:write`

Operation ID: `archiveIncident`
Contract: `supported`
Retry guidance: This operation is repeat-safe. Respect Retry-After and use bounded retries.
Prerequisites: Active responder seat

#### 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 incident ID returned by this API. The permanent incident reference is suitable for stored references and external links. 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/incidents/{id}/archive' \
  --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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

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

**Restore incident.** The caller must have an active responder seat. Restoring returns an archived incident to default lists without changing its workflow status. A repeated request returns the active incident without changing its revision, changelog, or audit records. If the incident has team_id, that team must be active.

Required scope: `incidents:write`

Operation ID: `restoreIncident`
Contract: `supported`
Retry guidance: This operation is repeat-safe. Respect Retry-After and use bounded retries.
Prerequisites: Active responder seat

#### 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 incident ID returned by this API. The permanent incident reference is suitable for stored references and external links. 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/incidents/{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 |
| --- | --- | --- | --- |
| `incident` | `object` | Always | The complete public record for an incident. |
| `incident.id` | `integer` | Always | The positive numeric ID of the incident. Minimum: 1. |
| `incident.revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `incident.company_id` | `integer` | Always | The ID of the workspace that owns the incident. Minimum: 1. |
| `incident.human_id` | `string` | Always | The permanent incident reference shown to users. |
| `incident.title` | `string` | Always | The editable display title of the incident. |
| `incident.service` | `string` | Always | The service slug associated with the incident. |
| `incident.regions` | `string[]` | Always | The affected regions. |
| `incident.severity` | `string` | Always | The configured severity key. |
| `incident.slack_channel_id` | `string` | Always | The linked Slack channel ID. An empty string means that no channel is linked. |
| `incident.meet_url` | `string` | Always | The incident meeting URL. An empty string means that no meeting is linked. |
| `incident.doc_url` | `string` | Always | The incident document or source URL. An empty string means that no URL is stored. |
| `incident.status` | `string` | Always | The configured workflow-status key. |
| `incident.status_changed_at` | `string (RFC 3339) \| null` | Always | The time of the latest workflow-status change. A null value means that this time is unavailable. |
| `incident.last_event_at` | `string (RFC 3339) \| null` | Always | The start time of the latest timeline event. A null value means that no event exists. |
| `incident.last_event_summary` | `string` | Always | A summary of the latest timeline event. |
| `incident.issue_type` | `string` | Always | The work type. Possible values: `"ticket"` \| `"incident"` \| `"postmortem"` |
| `incident.severity_mode` | `string` | Always | The severity selection mode. Possible values: `"auto"` \| `"manual"` |
| `incident.impact_summary` | `string` | Always | The current customer or operational impact summary. |
| `incident.customer_impact_status` | `string` | Always | The customer-impact assessment. Possible values: `"unknown"` \| `"none"` \| `"confirmed"` |
| `incident.impact_started_at` | `string (RFC 3339) \| null` | Always | The impact start time. A null value means that the time is unknown. |
| `incident.first_reported_at` | `string (RFC 3339) \| null` | Always | The time of the first incident report. A null value means that the time is unknown. |
| `incident.first_responded_at` | `string (RFC 3339) \| null` | Always | The time of the first response. A null value means that the time is unknown. |
| `incident.recovered_at` | `string (RFC 3339) \| null` | Always | The service recovery time. A null value means that recovery is not recorded. |
| `incident.duration_seconds` | `integer` | Always | The calculated incident duration in whole seconds. Minimum: 0. |
| `incident.duration_anchor` | `string` | Always | The timestamp field from which the API calculates duration. |
| `incident.team_id` | `integer \| null` | Always | The owning-team ID. A null value means that the incident has no owning team. |
| `incident.event_count` | `integer` | Always | The number of timeline events for the incident. Minimum: 0. |
| `incident.investigation_summary` | `object \| null` | Always | A summary of investigation runs. A null value means that no investigation data exists. Must match exactly one of 2 schema variants. |
| `incident.investigation_summary.run_count` | `integer` | Always | The total number of investigation runs. Minimum: 0. |
| `incident.investigation_summary.latest_run` | `object \| null` | Always | The most recent investigation run. A null value means that no run exists. Must match exactly one of 2 schema variants. |
| `incident.investigation_summary.latest_run.status` | `string` | Always | The current investigation-run status. |
| `incident.investigation_summary.latest_run.started_at` | `string (RFC 3339)` | Always | The investigation-run start time. |
| `incident.investigation_summary.latest_run.completed_at` | `string (RFC 3339)` | Optional | The investigation-run completion time. |
| `incident.created_at` | `string (RFC 3339)` | Always | The incident creation time. |
| `incident.created_by` | `string` | Always | The identifier of the user who created the incident. |
| `incident.custom_fields` | `object` | Always | The incident values keyed by custom-field slug. |
| `incident.archived_at` | `string (RFC 3339) \| null` | Always | The incident archive time. A null value means that the incident is active. |
| `incident.archived_by` | `integer \| null` | Always | The user ID that archived the incident. A null value means that the incident is active. |
| `incident.incident_manager` | `string` | Always | The user identifier of the incident manager. An empty string means that no manager is assigned. |
| `incident.incident_responders` | `string[]` | Always | The user identifiers of assigned responders. |
| `incident.has_assignees` | `boolean` | Always | Indicates whether the incident has a manager or at least one responder. |
| `timeline_event` | `object` | Optional | The complete public record for a timeline event. |
| `timeline_event.id` | `integer` | Always | The positive numeric ID of the timeline event. |
| `timeline_event.revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `timeline_event.incident_id` | `integer` | Always | The positive numeric ID of the incident. |
| `timeline_event.started_at` | `string (RFC 3339)` | Always | The event start time. |
| `timeline_event.at` | `string (RFC 3339)` | Always | A deprecated response alias for started_at. Both fields contain the same value. |
| `timeline_event.ended_at` | `string (RFC 3339) \| null` | Always | The event end time. A null value means that the event has no end time. |
| `timeline_event.is_ongoing` | `boolean` | Always | Indicates whether the event is still in progress. |
| `timeline_event.group_id` | `integer \| null` | Always | The timeline-group ID. A null value means that the event has no group. |
| `timeline_event.is_key` | `boolean` | Always | Indicates whether responders marked this as a key incident event. |
| `timeline_event.source` | `string` | Always | The system or actor that created the event. |
| `timeline_event.source_ref` | `string` | Always | The source-specific reference for the event. |
| `timeline_event.type` | `string` | Always | The event type used for grouping and display. |
| `timeline_event.icon` | `string` | Always | The event icon. The maximum length is 8 Unicode characters. Maximum length: 8 characters. |
| `timeline_event.title` | `string` | Always | The optional display title of the event. |
| `timeline_event.body` | `string` | Always | The event description. |
| `timeline_event.collaboration_generation` | `integer` | Always | The version of collaborative content associated with the event. |
| `timeline_event.tags` | `string[]` | Always | The normalized event tags. |
| `timeline_event.ai_context` | `string` | Always | The saved AI context for a generated event. An empty string means that no context is stored. |
| `timeline_event.created_by` | `string` | Always | The identifier of the user or system that created the event. |
| `timeline_event.comment_count` | `integer` | Always | The number of comments on the event. |
| `timeline_event.attachments` | `object[]` | Always | The files attached to the event. |
| `timeline_event.attachments[].id` | `integer` | Always | The positive numeric ID of the attachment. |
| `timeline_event.attachments[].timeline_event_id` | `integer` | Always | The positive numeric ID of the timeline event. |
| `timeline_event.attachments[].filename` | `string` | Always | The original file name. |
| `timeline_event.attachments[].url` | `string` | Always | A temporary download URL. An empty string means that no URL is currently available. |
| `timeline_event.attachments[].url_available` | `boolean` | Always | Indicates whether url contains an available download URL. |
| `timeline_event.attachments[].content_type` | `string` | Always | The media type of the attached file. |
| `timeline_event.attachments[].size_bytes` | `integer` | Always | The file size in bytes. |
| `timeline_event.attachments[].created_at` | `string (RFC 3339)` | Always | The attachment creation time. |
| `timeline_event.attachments[].created_by` | `string` | Always | The identifier of the user who attached the file. |
| `timeline_event.source_window_started_at` | `string (RFC 3339) \| null` | Always | The start of the source-data window. A null value means that no window is recorded. |
| `timeline_event.source_window_ended_at` | `string (RFC 3339) \| null` | Always | The end of the source-data window. A null value means that no window is recorded. |
| `timeline_event.evidence_links` | `object[]` | Always | The evidence references associated with the event. |
| `timeline_event.evidence_links[].source` | `string` | Always | The system or provider that supplied the evidence. |
| `timeline_event.evidence_links[].url` | `string` | Always | The absolute URL of the evidence. |
| `timeline_event.evidence_links[].label` | `string` | Optional | A short display name for the evidence. |
| `timeline_event.evidence_links[].summary` | `string` | Optional | A short summary of the evidence. |
| `timeline_event.evidence_links[].artifact_id` | `string` | Optional | The identifier of a stored evidence artifact. |
| `timeline_event.evidence_links[].artifact_digest` | `string` | Optional | The digest used to make sure that the artifact content did not change. |
| `timeline_event.evidence_links[].result_bytes` | `integer` | Optional | The original evidence result size in bytes. |
| `timeline_event.evidence_links[].truncated` | `boolean` | Optional | Indicates whether the stored evidence omits part of the original result. |

Returns the complete active incident. If its saved team is not active, the API returns 409 invalid_relationship_state.

```json
{
  "incident": {
    "id": 1,
    "revision": 7,
    "company_id": 7,
    "human_id": "INC-2025-001",
    "title": "Payments API requests timing out",
    "service": "payments",
    "regions": [
      "us-east-1"
    ],
    "severity": "sev1",
    "slack_channel_id": "C08ABC123",
    "meet_url": "https://meet.google.com/abc-defg-hij",
    "doc_url": "https://docs.google.com/d/1abc",
    "status": "active",
    "status_changed_at": "2025-01-30T12:05:00Z",
    "last_event_at": "2025-01-30T12:10:00Z",
    "last_event_summary": "Investigating root cause",
    "issue_type": "incident",
    "severity_mode": "manual",
    "impact_summary": "Payment processing delayed for EU customers",
    "customer_impact_status": "confirmed",
    "impact_started_at": "2025-01-30T11:55:00Z",
    "first_reported_at": "2025-01-30T11:58:00Z",
    "first_responded_at": "2025-01-30T12:00:00Z",
    "recovered_at": null,
    "duration_seconds": 900,
    "duration_anchor": "impact_started_at",
    "team_id": 42,
    "event_count": 5,
    "investigation_summary": null,
    "created_at": "2025-01-30T12:00:00Z",
    "created_by": "oncall@example.com",
    "custom_fields": {},
    "archived_at": null,
    "archived_by": null,
    "incident_manager": "oncall@example.com",
    "incident_responders": [
      "responder@example.com"
    ],
    "has_assignees": true
  }
}
```

#### 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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

### `POST /api/v1/incidents/{id}/rename-slack-channel`

**Reapply the Slack channel-name template (deprecated).** This operation is deprecated. It reapplies the workspace channel-name template. It rejects every request body, including whitespace. The replacement is PATCH /api/v1/incidents/{id}/slack/channel-name with mode workspace_template. Every response includes the RFC 9745 Deprecation date and a link to the replacement operation. The Sunset header is absent because Tellagen has not set an end date.

Required scope: `incidents:write`

Operation ID: `syncIncidentSlackChannelName`
Contract: `supported`
Retry guidance: Do not retry automatically after an uncertain outcome. Read the resource state and follow this operation’s recovery guidance.
Prerequisites: Active responder seat, Incident is not archived, Linked Slack channel

**Deprecated.**
Announced: `2026-08-31`. Sunset review: `2027-03-01`.
Successor: `PATCH /api/v1/incidents/{id}/slack/channel-name`

#### 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 incident ID returned by this API. The permanent incident reference is suitable for stored references and external links. 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/incidents/{id}/rename-slack-channel' \
  --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 |
| --- | --- | --- | --- |
| `channel_name` | `string` | Always | The new Slack channel name. |

Returns the new Slack channel name.

```json
{
  "channel_name": "inc-2025-001-payments"
}
```

#### Response headers

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

| Name | Type | Returned | Description |
| --- | --- | --- | --- |
| `Deprecation` | `string` | Always | The date of the deprecation announcement in RFC 9745 format. Pattern: ^@[0-9]+$. |
| `Link` | `string` | Always | A link to the replacement operation with rel="successor-version". |

#### Endpoint error responses

Endpoint-specific codes: `invalid_slack_channel_name_request`, `slack_not_configured`, `incident_slack_channel_missing`, `slack_channel_not_found`, `slack_channel_name_taken`, `invalid_resource_state`

| Status | Meaning |
| --- | --- |
| `400` | The compatibility action received a non-empty request body. |
| `401` | Authentication failed. |
| `403` | The API key lacks access or an active responder seat. |
| `404` | The incident does not exist. |
| `409` | Slack is not configured, the linked channel is missing, the incident is immutable, or the resolved name is already used. |
| `429` | The workspace rate limit is exceeded. |
| `500` | An internal server error prevented completion of the request. |

### `POST /api/v1/incidents/{id}/slack/channel-name/preview`

**Preview an incident Slack channel name.** Calculates and validates the channel name without renaming the Slack channel or creating an audit record. The workspace_template mode must omit name. The custom mode requires name.

Required scope: `incidents:write`

Operation ID: `previewIncidentSlackChannelName`
Contract: `supported`
Retry guidance: This operation is repeat-safe. Respect Retry-After and use bounded retries.
Prerequisites: Active responder seat, Incident is not archived, Linked Slack channel

#### 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 incident ID returned by this API. The permanent incident reference is suitable for stored references and external links. 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/incidents/{id}/slack/channel-name/preview' \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '{
  "mode": "custom",
  "name": "Payments War Room"
}'
```

#### Request body

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

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `mode` | `string` | Yes | Selects how the API creates the Slack channel name. Must match exactly one of 2 schema variants. Allowed values: `"workspace_template"` \| `"custom"` |
| `name` | `string` | Conditional | The proposed channel name for custom mode. It must contain an ASCII letter or digit. The preview and update operations normalize it identically. Required when: Required when mode is "custom". Minimum length: 1 characters. Pattern: .*[A-Za-z0-9].*. |

Request JSON:

```json
{
  "mode": "custom",
  "name": "Payments War Room"
}
```

#### 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 |
| --- | --- | --- | --- |
| `channel_id` | `string` | Always | The Slack channel ID. |
| `previous_name` | `string` | Always | The channel name before the request. |
| `new_name` | `string` | Always | The validated channel name. The maximum length is 80 characters. Minimum length: 1 characters. Maximum length: 80 characters. |
| `mode` | `string` | Always | The naming mode. Possible values: `"workspace_template"` \| `"custom"` |

Returns the current and proposed channel names. A 409 response uses slack_not_configured, incident_slack_channel_missing, slack_channel_not_found, or slack_channel_name_taken.

```json
{
  "channel_id": "C08ABC123",
  "previous_name": "inc-2025-001-payments",
  "new_name": "payments-war-room",
  "mode": "custom"
}
```

#### Endpoint error responses

Endpoint-specific codes: `invalid_slack_channel_name_request`, `slack_not_configured`, `incident_slack_channel_missing`, `slack_channel_not_found`, `slack_channel_name_taken`, `invalid_resource_state`

| Status | Meaning |
| --- | --- |
| `400` | The naming request 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` | Slack is not configured, the linked channel is missing, or the resolved name is already used. |
| `429` | The workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

#### Related operations

- [Update an incident Slack channel name](https://tellagen.com/api-reference/incidents#incidents-endpoint-patch-api-v1-incidents-id-slack-channel-name) (`PATCH /api/v1/incidents/{id}/slack/channel-name`)

### `PATCH /api/v1/incidents/{id}/slack/channel-name`

**Update an incident Slack channel name.** Renames the linked Slack channel with workspace_template or custom mode. The API saves the old name, new name, mode, actor, and time in the incident audit log. It also publishes the saved audit entry as an incident event. If the audit save fails after Slack renames the channel, the API tries to restore the old name and returns an error.

Required scope: `incidents:write`

Operation ID: `updateIncidentSlackChannelName`
Contract: `supported`
Retry guidance: Do not retry automatically after an uncertain outcome. Read the resource state and follow this operation’s recovery guidance.
Prerequisites: Active responder seat, Incident is not archived, Linked Slack channel

#### 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 incident ID returned by this API. The permanent incident reference is suitable for stored references and external links. 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/incidents/{id}/slack/channel-name' \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '{
  "mode": "custom",
  "name": "Payments War Room"
}'
```

#### Request body

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

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `mode` | `string` | Yes | Selects how the API creates the Slack channel name. Must match exactly one of 2 schema variants. Allowed values: `"workspace_template"` \| `"custom"` |
| `name` | `string` | Conditional | The proposed channel name for custom mode. It must contain an ASCII letter or digit. The preview and update operations normalize it identically. Required when: Required when mode is "custom". Minimum length: 1 characters. Pattern: .*[A-Za-z0-9].*. |

Request JSON:

```json
{
  "mode": "custom",
  "name": "Payments War Room"
}
```

#### 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 |
| --- | --- | --- | --- |
| `channel_id` | `string` | Always | The Slack channel ID. |
| `previous_name` | `string` | Always | The channel name before the request. |
| `new_name` | `string` | Always | The validated channel name. The maximum length is 80 characters. Minimum length: 1 characters. Maximum length: 80 characters. |
| `mode` | `string` | Always | The naming mode. Possible values: `"workspace_template"` \| `"custom"` |

Returns the renamed channel. The preview operation validates the same request without changing the channel. A 409 response uses slack_not_configured, incident_slack_channel_missing, slack_channel_not_found, or slack_channel_name_taken.

```json
{
  "channel_id": "C08ABC123",
  "previous_name": "inc-2025-001-payments",
  "new_name": "payments-war-room",
  "mode": "custom"
}
```

#### Endpoint error responses

Endpoint-specific codes: `invalid_slack_channel_name_request`, `slack_not_configured`, `incident_slack_channel_missing`, `slack_channel_not_found`, `slack_channel_name_taken`, `invalid_resource_state`

| Status | Meaning |
| --- | --- |
| `400` | The naming request 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` | Slack is not configured, the linked channel is missing, or the resolved name is already used. |
| `429` | The workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

#### Related operations

- [Preview an incident Slack channel name](https://tellagen.com/api-reference/incidents#incidents-endpoint-post-api-v1-incidents-id-slack-channel-name-preview) (`POST /api/v1/incidents/{id}/slack/channel-name/preview`)

## Timeline

Creates, lists, reads, updates, and removes events from incident timelines.

### `GET /api/v1/incidents/{id}/timeline`

**List timeline events.** Returns one bounded page ordered by (started_at, id), ascending by default. Descending order reverses both fields. Dimensions combine with AND; selected tags and groups each combine with OR. matched_count describes the complete active predicate before cursor/limit. filter_counts includes suggestions and applies only time bounds. latest_started_at is incident-global, independent of all filters. Time bounds use [started_from, started_to). New opaque v2 cursors bind the incident, order and canonical filters; reuse with a different scope returns 400. Legacy timestamp/ID cursors are accepted only with ascending, time-only filters and suggestions included. Pagination is keyset traversal, not a cross-request snapshot; concurrent edits can move rows.

Required scope: `incidents:read`

Operation ID: `listTimelineEvents`
Contract: `supported`
Retry guidance: This operation is repeat-safe. Respect Retry-After and use bounded retries.
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 incident ID returned by this API. The permanent incident reference is suitable for stored references and external links. 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. |
| `order` | `string` | No | Sort direction for both started_at and id. Default: "asc". Allowed values: `"asc"` \| `"desc"` |
| `type_or_source` | `string` | No | Case-insensitive type or source, with outer whitespace ignored. ai matches sources ai, ai_investigation, investigation, or type investigation with source manual. |
| `tag` | `string[]` | No | Match any repeated tag case-insensitively. Tag whitespace is significant. |
| `key_only` | `boolean` | No | Include only key events when true. Accepts true or false. Default: false. |
| `group` | `integer \| string[]` | No | Match any repeated positive group ID or ungrouped for null group. Allowed values: `"ungrouped"` |
| `q` | `string` | No | Literal case-insensitive substring of title, body, ai_context, space-joined tags or group name; outer query whitespace is ignored. Percent and underscore are literal characters. Fields match independently. Null group text is no group ungrouped; empty or unavailable group name is unknown group followed by a hash and its ID. |
| `suggestions` | `string` | No | Include, exclude or select only ai_suggested events, identified by lowercase type without trimming. Only mode applies other predicates only when explicitly supplied; it does not inherit workspace filters. Default: "include". Allowed values: `"include"` \| `"exclude"` \| `"only"` |
| `started_from` | `string (RFC 3339)` | No | Includes events with started_at equal to or later than this RFC 3339 time. |
| `started_to` | `string (RFC 3339)` | No | Includes events with started_at earlier than this RFC 3339 time. |

#### Example request

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

```bash
curl --request GET \
  --url 'https://{company}.api.tellagen.com/api/v1/incidents/{id}/timeline' \
  --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 |
| --- | --- | --- | --- |
| `events` | `object[]` | Always | The timeline events in this page. |
| `events[].id` | `integer` | Always | The positive numeric ID of the timeline event. |
| `events[].revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `events[].incident_id` | `integer` | Always | The positive numeric ID of the incident. |
| `events[].started_at` | `string (RFC 3339)` | Always | The event start time. |
| `events[].at` | `string (RFC 3339)` | Always | A deprecated response alias for started_at. Both fields contain the same value. |
| `events[].ended_at` | `string (RFC 3339) \| null` | Always | The event end time. A null value means that the event has no end time. |
| `events[].is_ongoing` | `boolean` | Always | Indicates whether the event is still in progress. |
| `events[].group_id` | `integer \| null` | Always | The timeline-group ID. A null value means that the event has no group. |
| `events[].is_key` | `boolean` | Always | Indicates whether responders marked this as a key incident event. |
| `events[].source` | `string` | Always | The system or actor that created the event. |
| `events[].source_ref` | `string` | Always | The source-specific reference for the event. |
| `events[].type` | `string` | Always | The event type used for grouping and display. |
| `events[].icon` | `string` | Always | The event icon. The maximum length is 8 Unicode characters. Maximum length: 8 characters. |
| `events[].title` | `string` | Always | The optional display title of the event. |
| `events[].body` | `string` | Always | The event description. |
| `events[].collaboration_generation` | `integer` | Always | The version of collaborative content associated with the event. |
| `events[].tags` | `string[]` | Always | The normalized event tags. |
| `events[].ai_context` | `string` | Always | The saved AI context for a generated event. An empty string means that no context is stored. |
| `events[].created_by` | `string` | Always | The identifier of the user or system that created the event. |
| `events[].comment_count` | `integer` | Always | The number of comments on the event. |
| `events[].attachments` | `object[]` | Always | The files attached to the event. |
| `events[].attachments[].id` | `integer` | Always | The positive numeric ID of the attachment. |
| `events[].attachments[].timeline_event_id` | `integer` | Always | The positive numeric ID of the timeline event. |
| `events[].attachments[].filename` | `string` | Always | The original file name. |
| `events[].attachments[].url` | `string` | Always | A temporary download URL. An empty string means that no URL is currently available. |
| `events[].attachments[].url_available` | `boolean` | Always | Indicates whether url contains an available download URL. |
| `events[].attachments[].content_type` | `string` | Always | The media type of the attached file. |
| `events[].attachments[].size_bytes` | `integer` | Always | The file size in bytes. |
| `events[].attachments[].created_at` | `string (RFC 3339)` | Always | The attachment creation time. |
| `events[].attachments[].created_by` | `string` | Always | The identifier of the user who attached the file. |
| `events[].source_window_started_at` | `string (RFC 3339) \| null` | Always | The start of the source-data window. A null value means that no window is recorded. |
| `events[].source_window_ended_at` | `string (RFC 3339) \| null` | Always | The end of the source-data window. A null value means that no window is recorded. |
| `events[].evidence_links` | `object[]` | Always | The evidence references associated with the event. |
| `events[].evidence_links[].source` | `string` | Always | The system or provider that supplied the evidence. |
| `events[].evidence_links[].url` | `string` | Always | The absolute URL of the evidence. |
| `events[].evidence_links[].label` | `string` | Optional | A short display name for the evidence. |
| `events[].evidence_links[].summary` | `string` | Optional | A short summary of the evidence. |
| `events[].evidence_links[].artifact_id` | `string` | Optional | The identifier of a stored evidence artifact. |
| `events[].evidence_links[].artifact_digest` | `string` | Optional | The digest used to make sure that the artifact content did not change. |
| `events[].evidence_links[].result_bytes` | `integer` | Optional | The original evidence result size in bytes. |
| `events[].evidence_links[].truncated` | `boolean` | Optional | Indicates whether the stored evidence omits part of the original result. |
| `filter_counts` | `object` | Always | Counts for all events that match the timeline time filters. |
| `filter_counts.all_events` | `integer` | Always | The total number of events in the time-filtered collection. |
| `filter_counts.type_counts` | `object` | Always | Event counts keyed by event type. |
| `filter_counts.source_counts` | `object` | Always | Event counts keyed by event source. |
| `filter_counts.tag_counts` | `object` | Always | Event counts keyed by tag. |
| `matched_count` | `integer` | Always | Complete active predicate count before cursor and limit. Minimum: 0. |
| `latest_started_at` | `string (RFC 3339) \| null` | Always | Latest started_at in the incident independent of filters; null for an empty incident. |
| `has_more` | `boolean` | Always | Indicates whether another page is available. |
| `next_cursor` | `string` | Optional | The opaque cursor for the next page. This field is absent on the final page. |
| `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 timeline events, pagination fields, and counts for the complete filtered collection.

```json
{
  "events": [
    {
      "id": 100,
      "revision": 4,
      "incident_id": 1,
      "started_at": "2026-08-31T12:10:00Z",
      "at": "2026-08-31T12:10:00Z",
      "ended_at": null,
      "is_ongoing": false,
      "group_id": null,
      "is_key": false,
      "source": "api",
      "source_ref": "api:123:deploy-42",
      "type": "change",
      "icon": "🔧",
      "title": "Payment service restart",
      "body": "Restarted the payment service.",
      "collaboration_generation": 0,
      "tags": [],
      "ai_context": "",
      "created_by": "api-owner@example.com",
      "comment_count": 0,
      "attachments": [
        {
          "id": 501,
          "timeline_event_id": 100,
          "filename": "restart-log.txt",
          "url": "https://files.example.com/signed/restart-log.txt",
          "url_available": true,
          "content_type": "text/plain",
          "size_bytes": 2048,
          "created_at": "2026-08-31T12:11:00Z",
          "created_by": "oncall@example.com"
        }
      ],
      "source_window_started_at": null,
      "source_window_ended_at": null,
      "evidence_links": []
    }
  ],
  "filter_counts": {
    "all_events": 1,
    "type_counts": {
      "change": 1
    },
    "source_counts": {
      "api": 1
    },
    "tag_counts": {}
  },
  "matched_count": 1,
  "latest_started_at": "2026-08-31T12:10:00Z",
  "has_more": false,
  "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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

### `POST /api/v1/incidents/{id}/timeline`

**Create timeline event.** Creates a timeline event. For API-key requests, the API sets source to api. The API controls source, AI context, evidence provenance, and reserved event types.

Required scope: `incidents:write`

Operation ID: `createTimelineEvent`
Contract: `supported`
Retry guidance: Retry an identical request with the same Idempotency-Key used on the first attempt, within 24 hours. Never retry an unknown outcome automatically.
Prerequisites: Active responder seat, Incident is not archived

#### 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 incident ID returned by this API. The permanent incident reference is suitable for stored references and external links. Minimum: 1. |

#### 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/incidents/{id}/timeline' \
  --header 'Authorization: Bearer <token>' \
  --header 'Idempotency-Key: <idempotency-key>' \
  --header 'Content-Type: application/json' \
  --data '{
  "body": "Restarted the payment service.",
  "started_at": "2026-08-31T12:10:00Z",
  "type": "change",
  "icon": "🔧",
  "external_reference": "deploy-42"
}'
```

#### Request body

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

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `body` | `string` | Yes | The event description. It must contain at least one non-space character. Minimum length: 1 characters. |
| `started_at` | `string (RFC 3339)` | No | The RFC 3339 start time. If the request omits started_at, the API uses the current time. |
| `at` | `string (RFC 3339)` | No | A deprecated request alias for started_at. If both fields are present, their values must match. |
| `ended_at` | `string (RFC 3339)` | No | The RFC 3339 end time. It must be later than started_at. |
| `is_ongoing` | `boolean` | No | Controls whether the event is ongoing. If this value is true, ended_at must be omitted. |
| `group_id` | `integer \| null` | No | The positive timeline-group ID for this incident. A null value creates an event without a group. |
| `is_key` | `boolean` | No | Controls whether responders see this as a key incident event. |
| `type` | `string` | No | The client-defined event type. The API rejects event types reserved for generated events. |
| `icon` | `string` | No | The event icon. The maximum length is 8 Unicode characters. Maximum length: 8 characters. |
| `title` | `string` | No | The optional display title of the event. |
| `tags` | `string[]` | No | The event tags. The API normalizes each value. |
| `external_reference` | `string` | No | A client correlation reference for API-key requests. The API trims, limits, and namespaces the value. The maximum length is 200 characters. Maximum length: 200 characters. |

Request JSON:

```json
{
  "body": "Restarted the payment service.",
  "started_at": "2026-08-31T12:10:00Z",
  "type": "change",
  "icon": "🔧",
  "external_reference": "deploy-42"
}
```

#### 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 |
| --- | --- | --- | --- |
| `event` | `object` | Always | The complete public record for a timeline event. |
| `event.id` | `integer` | Always | The positive numeric ID of the timeline event. |
| `event.revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `event.incident_id` | `integer` | Always | The positive numeric ID of the incident. |
| `event.started_at` | `string (RFC 3339)` | Always | The event start time. |
| `event.at` | `string (RFC 3339)` | Always | A deprecated response alias for started_at. Both fields contain the same value. |
| `event.ended_at` | `string (RFC 3339) \| null` | Always | The event end time. A null value means that the event has no end time. |
| `event.is_ongoing` | `boolean` | Always | Indicates whether the event is still in progress. |
| `event.group_id` | `integer \| null` | Always | The timeline-group ID. A null value means that the event has no group. |
| `event.is_key` | `boolean` | Always | Indicates whether responders marked this as a key incident event. |
| `event.source` | `string` | Always | The system or actor that created the event. |
| `event.source_ref` | `string` | Always | The source-specific reference for the event. |
| `event.type` | `string` | Always | The event type used for grouping and display. |
| `event.icon` | `string` | Always | The event icon. The maximum length is 8 Unicode characters. Maximum length: 8 characters. |
| `event.title` | `string` | Always | The optional display title of the event. |
| `event.body` | `string` | Always | The event description. |
| `event.collaboration_generation` | `integer` | Always | The version of collaborative content associated with the event. |
| `event.tags` | `string[]` | Always | The normalized event tags. |
| `event.ai_context` | `string` | Always | The saved AI context for a generated event. An empty string means that no context is stored. |
| `event.created_by` | `string` | Always | The identifier of the user or system that created the event. |
| `event.comment_count` | `integer` | Always | The number of comments on the event. |
| `event.attachments` | `object[]` | Always | The files attached to the event. |
| `event.attachments[].id` | `integer` | Always | The positive numeric ID of the attachment. |
| `event.attachments[].timeline_event_id` | `integer` | Always | The positive numeric ID of the timeline event. |
| `event.attachments[].filename` | `string` | Always | The original file name. |
| `event.attachments[].url` | `string` | Always | A temporary download URL. An empty string means that no URL is currently available. |
| `event.attachments[].url_available` | `boolean` | Always | Indicates whether url contains an available download URL. |
| `event.attachments[].content_type` | `string` | Always | The media type of the attached file. |
| `event.attachments[].size_bytes` | `integer` | Always | The file size in bytes. |
| `event.attachments[].created_at` | `string (RFC 3339)` | Always | The attachment creation time. |
| `event.attachments[].created_by` | `string` | Always | The identifier of the user who attached the file. |
| `event.source_window_started_at` | `string (RFC 3339) \| null` | Always | The start of the source-data window. A null value means that no window is recorded. |
| `event.source_window_ended_at` | `string (RFC 3339) \| null` | Always | The end of the source-data window. A null value means that no window is recorded. |
| `event.evidence_links` | `object[]` | Always | The evidence references associated with the event. |
| `event.evidence_links[].source` | `string` | Always | The system or provider that supplied the evidence. |
| `event.evidence_links[].url` | `string` | Always | The absolute URL of the evidence. |
| `event.evidence_links[].label` | `string` | Optional | A short display name for the evidence. |
| `event.evidence_links[].summary` | `string` | Optional | A short summary of the evidence. |
| `event.evidence_links[].artifact_id` | `string` | Optional | The identifier of a stored evidence artifact. |
| `event.evidence_links[].artifact_digest` | `string` | Optional | The digest used to make sure that the artifact content did not change. |
| `event.evidence_links[].result_bytes` | `integer` | Optional | The original evidence result size in bytes. |
| `event.evidence_links[].truncated` | `boolean` | Optional | Indicates whether the stored evidence omits part of the original result. |

Returns the created timeline event.

```json
{
  "event": {
    "id": 100,
    "revision": 4,
    "incident_id": 1,
    "started_at": "2026-08-31T12:10:00Z",
    "at": "2026-08-31T12:10:00Z",
    "ended_at": null,
    "is_ongoing": false,
    "group_id": null,
    "is_key": false,
    "source": "api",
    "source_ref": "api:123:deploy-42",
    "type": "change",
    "icon": "🔧",
    "title": "Payment service restart",
    "body": "Restarted the payment service.",
    "collaboration_generation": 0,
    "tags": [],
    "ai_context": "",
    "created_by": "api-owner@example.com",
    "comment_count": 0,
    "attachments": [
      {
        "id": 501,
        "timeline_event_id": 100,
        "filename": "restart-log.txt",
        "url": "https://files.example.com/signed/restart-log.txt",
        "url_available": true,
        "content_type": "text/plain",
        "size_bytes": 2048,
        "created_at": "2026-08-31T12:11:00Z",
        "created_by": "oncall@example.com"
      }
    ],
    "source_window_started_at": null,
    "source_window_ended_at": null,
    "evidence_links": []
  }
}
```

#### 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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

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

**Get timeline event.** Returns one timeline event. Attachment download URLs expire. If url_available is false or a URL expires, a new GET request can return a new URL.

Required scope: `incidents:read`

Operation ID: `getTimelineEvent`
Contract: `supported`
Retry guidance: This operation is repeat-safe. Respect Retry-After and use bounded retries.
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 timeline-event 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/timeline/{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 |
| --- | --- | --- | --- |
| `event` | `object` | Always | The complete public record for a timeline event. |
| `event.id` | `integer` | Always | The positive numeric ID of the timeline event. |
| `event.revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `event.incident_id` | `integer` | Always | The positive numeric ID of the incident. |
| `event.started_at` | `string (RFC 3339)` | Always | The event start time. |
| `event.at` | `string (RFC 3339)` | Always | A deprecated response alias for started_at. Both fields contain the same value. |
| `event.ended_at` | `string (RFC 3339) \| null` | Always | The event end time. A null value means that the event has no end time. |
| `event.is_ongoing` | `boolean` | Always | Indicates whether the event is still in progress. |
| `event.group_id` | `integer \| null` | Always | The timeline-group ID. A null value means that the event has no group. |
| `event.is_key` | `boolean` | Always | Indicates whether responders marked this as a key incident event. |
| `event.source` | `string` | Always | The system or actor that created the event. |
| `event.source_ref` | `string` | Always | The source-specific reference for the event. |
| `event.type` | `string` | Always | The event type used for grouping and display. |
| `event.icon` | `string` | Always | The event icon. The maximum length is 8 Unicode characters. Maximum length: 8 characters. |
| `event.title` | `string` | Always | The optional display title of the event. |
| `event.body` | `string` | Always | The event description. |
| `event.collaboration_generation` | `integer` | Always | The version of collaborative content associated with the event. |
| `event.tags` | `string[]` | Always | The normalized event tags. |
| `event.ai_context` | `string` | Always | The saved AI context for a generated event. An empty string means that no context is stored. |
| `event.created_by` | `string` | Always | The identifier of the user or system that created the event. |
| `event.comment_count` | `integer` | Always | The number of comments on the event. |
| `event.attachments` | `object[]` | Always | The files attached to the event. |
| `event.attachments[].id` | `integer` | Always | The positive numeric ID of the attachment. |
| `event.attachments[].timeline_event_id` | `integer` | Always | The positive numeric ID of the timeline event. |
| `event.attachments[].filename` | `string` | Always | The original file name. |
| `event.attachments[].url` | `string` | Always | A temporary download URL. An empty string means that no URL is currently available. |
| `event.attachments[].url_available` | `boolean` | Always | Indicates whether url contains an available download URL. |
| `event.attachments[].content_type` | `string` | Always | The media type of the attached file. |
| `event.attachments[].size_bytes` | `integer` | Always | The file size in bytes. |
| `event.attachments[].created_at` | `string (RFC 3339)` | Always | The attachment creation time. |
| `event.attachments[].created_by` | `string` | Always | The identifier of the user who attached the file. |
| `event.source_window_started_at` | `string (RFC 3339) \| null` | Always | The start of the source-data window. A null value means that no window is recorded. |
| `event.source_window_ended_at` | `string (RFC 3339) \| null` | Always | The end of the source-data window. A null value means that no window is recorded. |
| `event.evidence_links` | `object[]` | Always | The evidence references associated with the event. |
| `event.evidence_links[].source` | `string` | Always | The system or provider that supplied the evidence. |
| `event.evidence_links[].url` | `string` | Always | The absolute URL of the evidence. |
| `event.evidence_links[].label` | `string` | Optional | A short display name for the evidence. |
| `event.evidence_links[].summary` | `string` | Optional | A short summary of the evidence. |
| `event.evidence_links[].artifact_id` | `string` | Optional | The identifier of a stored evidence artifact. |
| `event.evidence_links[].artifact_digest` | `string` | Optional | The digest used to make sure that the artifact content did not change. |
| `event.evidence_links[].result_bytes` | `integer` | Optional | The original evidence result size in bytes. |
| `event.evidence_links[].truncated` | `boolean` | Optional | Indicates whether the stored evidence omits part of the original result. |

Returns the timeline event.

```json
{
  "event": {
    "id": 100,
    "revision": 4,
    "incident_id": 1,
    "started_at": "2026-08-31T12:10:00Z",
    "at": "2026-08-31T12:10:00Z",
    "ended_at": null,
    "is_ongoing": false,
    "group_id": null,
    "is_key": false,
    "source": "api",
    "source_ref": "api:123:deploy-42",
    "type": "change",
    "icon": "🔧",
    "title": "Payment service restart",
    "body": "Restarted the payment service.",
    "collaboration_generation": 0,
    "tags": [],
    "ai_context": "",
    "created_by": "api-owner@example.com",
    "comment_count": 0,
    "attachments": [
      {
        "id": 501,
        "timeline_event_id": 100,
        "filename": "restart-log.txt",
        "url": "https://files.example.com/signed/restart-log.txt",
        "url_available": true,
        "content_type": "text/plain",
        "size_bytes": 2048,
        "created_at": "2026-08-31T12:11:00Z",
        "created_by": "oncall@example.com"
      }
    ],
    "source_window_started_at": null,
    "source_window_ended_at": null,
    "evidence_links": []
  }
}
```

#### 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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

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

**Update timeline event.** Validates the final event state, then updates the supplied fields. If the request changes no value, the API returns 400 without side effects.

Required scope: `incidents:write`

Operation ID: `updateTimelineEvent`
Contract: `supported`
Retry guidance: Read the current resource and reconcile changes before retrying with its current version. A stale version returns 412.
Prerequisites: Active responder seat, Incident is not archived

#### 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 timeline-event 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/timeline/{id}' \
  --header 'Authorization: Bearer <token>' \
  --header 'If-Tellagen-Resource-Version: <resource-version>' \
  --header 'Content-Type: application/json' \
  --data '{
  "body": "Restarted the payment service and confirmed recovery.",
  "ended_at": "2026-08-31T12:18:00Z"
}'
```

#### Request body

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

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `body` | `string` | No | The replacement event description. It must contain at least one non-space character. Minimum length: 1 characters. |
| `title` | `string` | No | The replacement event title. An empty string removes the title. |
| `started_at` | `string (RFC 3339)` | No | The replacement RFC 3339 start time. |
| `at` | `string (RFC 3339)` | No | A deprecated request alias for started_at. If both fields are present, their values must match. |
| `ended_at` | `string (RFC 3339)` | No | The replacement RFC 3339 end time. It must be later than started_at. |
| `clear_ended_at` | `boolean` | No | A true value removes ended_at. |
| `is_ongoing` | `boolean` | No | Controls whether the event is ongoing. |
| `group_id` | `integer \| null` | No | The positive timeline-group ID. A null value removes the event from its current group. |
| `is_key` | `boolean` | No | Controls whether responders see this as a key incident event. |
| `type` | `string` | No | The client-defined event type. The API rejects event types reserved for generated events. |
| `icon` | `string` | No | The replacement event icon. The maximum length is 8 Unicode characters. Maximum length: 8 characters. |
| `tags` | `string[]` | No | The complete replacement list of event tags. The API normalizes each value. |

Request JSON:

```json
{
  "body": "Restarted the payment service and confirmed recovery.",
  "ended_at": "2026-08-31T12:18:00Z"
}
```

#### 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 |
| --- | --- | --- | --- |
| `event` | `object` | Always | The complete public record for a timeline event. |
| `event.id` | `integer` | Always | The positive numeric ID of the timeline event. |
| `event.revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `event.incident_id` | `integer` | Always | The positive numeric ID of the incident. |
| `event.started_at` | `string (RFC 3339)` | Always | The event start time. |
| `event.at` | `string (RFC 3339)` | Always | A deprecated response alias for started_at. Both fields contain the same value. |
| `event.ended_at` | `string (RFC 3339) \| null` | Always | The event end time. A null value means that the event has no end time. |
| `event.is_ongoing` | `boolean` | Always | Indicates whether the event is still in progress. |
| `event.group_id` | `integer \| null` | Always | The timeline-group ID. A null value means that the event has no group. |
| `event.is_key` | `boolean` | Always | Indicates whether responders marked this as a key incident event. |
| `event.source` | `string` | Always | The system or actor that created the event. |
| `event.source_ref` | `string` | Always | The source-specific reference for the event. |
| `event.type` | `string` | Always | The event type used for grouping and display. |
| `event.icon` | `string` | Always | The event icon. The maximum length is 8 Unicode characters. Maximum length: 8 characters. |
| `event.title` | `string` | Always | The optional display title of the event. |
| `event.body` | `string` | Always | The event description. |
| `event.collaboration_generation` | `integer` | Always | The version of collaborative content associated with the event. |
| `event.tags` | `string[]` | Always | The normalized event tags. |
| `event.ai_context` | `string` | Always | The saved AI context for a generated event. An empty string means that no context is stored. |
| `event.created_by` | `string` | Always | The identifier of the user or system that created the event. |
| `event.comment_count` | `integer` | Always | The number of comments on the event. |
| `event.attachments` | `object[]` | Always | The files attached to the event. |
| `event.attachments[].id` | `integer` | Always | The positive numeric ID of the attachment. |
| `event.attachments[].timeline_event_id` | `integer` | Always | The positive numeric ID of the timeline event. |
| `event.attachments[].filename` | `string` | Always | The original file name. |
| `event.attachments[].url` | `string` | Always | A temporary download URL. An empty string means that no URL is currently available. |
| `event.attachments[].url_available` | `boolean` | Always | Indicates whether url contains an available download URL. |
| `event.attachments[].content_type` | `string` | Always | The media type of the attached file. |
| `event.attachments[].size_bytes` | `integer` | Always | The file size in bytes. |
| `event.attachments[].created_at` | `string (RFC 3339)` | Always | The attachment creation time. |
| `event.attachments[].created_by` | `string` | Always | The identifier of the user who attached the file. |
| `event.source_window_started_at` | `string (RFC 3339) \| null` | Always | The start of the source-data window. A null value means that no window is recorded. |
| `event.source_window_ended_at` | `string (RFC 3339) \| null` | Always | The end of the source-data window. A null value means that no window is recorded. |
| `event.evidence_links` | `object[]` | Always | The evidence references associated with the event. |
| `event.evidence_links[].source` | `string` | Always | The system or provider that supplied the evidence. |
| `event.evidence_links[].url` | `string` | Always | The absolute URL of the evidence. |
| `event.evidence_links[].label` | `string` | Optional | A short display name for the evidence. |
| `event.evidence_links[].summary` | `string` | Optional | A short summary of the evidence. |
| `event.evidence_links[].artifact_id` | `string` | Optional | The identifier of a stored evidence artifact. |
| `event.evidence_links[].artifact_digest` | `string` | Optional | The digest used to make sure that the artifact content did not change. |
| `event.evidence_links[].result_bytes` | `integer` | Optional | The original evidence result size in bytes. |
| `event.evidence_links[].truncated` | `boolean` | Optional | Indicates whether the stored evidence omits part of the original result. |

Returns the updated timeline event.

```json
{
  "event": {
    "id": 100,
    "revision": 4,
    "incident_id": 1,
    "started_at": "2026-08-31T12:10:00Z",
    "at": "2026-08-31T12:10:00Z",
    "ended_at": "2026-08-31T12:18:00Z",
    "is_ongoing": false,
    "group_id": null,
    "is_key": false,
    "source": "api",
    "source_ref": "api:123:deploy-42",
    "type": "change",
    "icon": "🔧",
    "title": "Payment service restart",
    "body": "Restarted the payment service and confirmed recovery.",
    "collaboration_generation": 0,
    "tags": [],
    "ai_context": "",
    "created_by": "api-owner@example.com",
    "comment_count": 0,
    "attachments": [
      {
        "id": 501,
        "timeline_event_id": 100,
        "filename": "restart-log.txt",
        "url": "https://files.example.com/signed/restart-log.txt",
        "url_available": true,
        "content_type": "text/plain",
        "size_bytes": 2048,
        "created_at": "2026-08-31T12:11:00Z",
        "created_by": "oncall@example.com"
      }
    ],
    "source_window_started_at": null,
    "source_window_ended_at": null,
    "evidence_links": []
  }
}
```

#### 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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

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

**Delete timeline event.** Deletes the event and its attachment records. The API removes stored attachment objects asynchronously. Findings promoted from the event return to draft. A repeated request returns 404. Concurrent attachment changes return 409. The request is safe to retry after this conflict.

Required scope: `incidents:write`

Operation ID: `deleteTimelineEvent`
Contract: `supported`
Retry guidance: Do not retry automatically after an uncertain outcome. Read the resource state and follow this operation’s recovery guidance.
Prerequisites: Active responder seat, Incident is not archived

#### 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 timeline-event 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/timeline/{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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

## Services

Manages the service catalog. Create, update, archive, and restore operations require the manage_settings permission. Archived services remain available by ID and retain incident references. They are read-only and absent from default lists.

### `GET /api/v1/services`

**List services.** Returns one page sorted by configured tier rank, normalized service name, and service ID. The first page includes up to 100 historical service values from incidents that do not match a catalog service. These values are sorted by incident count. Later pages return an empty unmanaged_references array. If more than 100 unmatched values exist, unmanaged_references_truncated is true.

Required scope: `services:read`

Operation ID: `listServices`
Contract: `supported`
Retry guidance: This operation is repeat-safe. Respect Retry-After and use bounded retries.
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 services. 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/services' \
  --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 |
| --- | --- | --- | --- |
| `services` | `object[]` | Always | The services in this page. |
| `services[].id` | `integer` | Always | The positive numeric ID of the service. |
| `services[].revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `services[].company_id` | `integer` | Always | The ID of the workspace that owns the service. |
| `services[].name` | `string` | Always | The service name after whitespace is removed from both ends. The length is measured in Unicode code points. Minimum length: 1 characters. Maximum length: 128 characters. |
| `services[].slug` | `string` | Always | A permanent lowercase service key. It starts with a Unicode letter and then contains lowercase Unicode letters, digits, or underscores. The maximum size is 64 UTF-8 bytes. |
| `services[].description` | `string` | Optional | The workspace description of the service. |
| `services[].owner_team_id` | `integer` | Optional | The positive numeric ID of the owning team. A service without an owning team omits this field. |
| `services[].owner_team_name` | `string` | Optional | The display name of the owning team. A service without an owning team omits this field. |
| `services[].owner_team_slug` | `string` | Optional | The permanent key of the owning team. A service without an owning team omits this field. |
| `services[].tier` | `string` | Always | The service-tier key from the workspace configuration. |
| `services[].tags` | `string[]` | Always | The normalized service tags. |
| `services[].external_id` | `string` | Optional | The service identifier from an external system. |
| `services[].observability_names` | `string[]` | Optional | Alternative service names for telemetry systems. The API trims values and removes case-insensitive duplicates. |
| `services[].created_at` | `string (RFC 3339)` | Always | The service creation time. |
| `services[].updated_at` | `string (RFC 3339)` | Always | The time of the latest service change. |
| `services[].archived_at` | `string (RFC 3339)` | Optional | The service archive time. This field is absent for an active service. |
| `unmanaged_references` | `object[]` | Always | Up to 100 historical incident service values that do not match a catalog service. Later pages return an empty array. Maximum items: 100. |
| `unmanaged_references[].value` | `string` | Always | The unmatched service value stored on incidents. |
| `unmanaged_references[].incident_count` | `integer` | Always | The number of incidents that contain this value. Minimum: 1. |
| `unmanaged_references_truncated` | `boolean` | Always | Indicates whether more than 100 unmatched values exist. This value is false on later pages. |
| `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 services, unmanaged incident references, and pagination fields.

```json
{
  "services": [
    {
      "id": 5,
      "revision": 3,
      "company_id": 12,
      "name": "Payment Service",
      "slug": "payment_service",
      "description": "Handles all payment processing",
      "owner_team_id": 42,
      "owner_team_name": "Platform Team",
      "owner_team_slug": "platform_team",
      "tier": "critical",
      "tags": [
        "critical",
        "payments"
      ],
      "external_id": "payments-prod",
      "observability_names": [
        "payments-api",
        "payments-worker"
      ],
      "created_at": "2025-01-15T10:00:00Z",
      "updated_at": "2025-01-15T10:00:00Z"
    }
  ],
  "unmanaged_references": [
    {
      "value": "Legacy Billing",
      "incident_count": 3
    }
  ],
  "unmanaged_references_truncated": false,
  "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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

### `POST /api/v1/services`

**Create service.** Creates a service. If slug is omitted, the API creates a lowercase slug with underscores. A supplied tier must match a key in the workspace tier configuration. If tier is omitted, the API uses the default tier.

Required scope: `services:write`

Operation ID: `createService`
Contract: `supported`
Retry guidance: Retry an identical request with the same Idempotency-Key used on the first attempt, within 24 hours. Never retry an unknown outcome automatically.
Prerequisites: Active responder seat, Manage workspace settings

#### 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/services' \
  --header 'Authorization: Bearer <token>' \
  --header 'Idempotency-Key: <idempotency-key>' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "Payment Service",
  "tier": "critical",
  "tags": [
    "critical",
    "payments"
  ],
  "external_id": "payments-prod",
  "observability_names": [
    "payments-api",
    "payments-worker"
  ]
}'
```

#### 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 service name. The length must be from 1 through 128 Unicode characters. Minimum length: 1 characters. Maximum length: 128 characters. |
| `slug` | `string` | No | A permanent lowercase service key. It must start with a Unicode letter and use at most 64 UTF-8 bytes. Later characters can be lowercase Unicode letters, digits, or underscores. |
| `description` | `string` | No | The workspace description of the service. |
| `owner_team_id` | `integer` | No | The positive ID of an active owning team. Minimum: 1. |
| `tier` | `string` | No | A service-tier key from the workspace configuration. If tier is omitted, the API uses the configured default tier. |
| `tags` | `string[]` | No | The service tags. The API trims each value and removes blank values. |
| `external_id` | `string` | No | The service identifier from an external system. The API removes whitespace from both ends. |
| `observability_names` | `string[]` | No | Alternative service names for telemetry systems. The API trims values and removes case-insensitive duplicates. |

Request JSON:

```json
{
  "name": "Payment Service",
  "tier": "critical",
  "tags": [
    "critical",
    "payments"
  ],
  "external_id": "payments-prod",
  "observability_names": [
    "payments-api",
    "payments-worker"
  ]
}
```

#### 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 |
| --- | --- | --- | --- |
| `service` | `object` | Always | The complete public record for a service. |
| `service.id` | `integer` | Always | The positive numeric ID of the service. |
| `service.revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `service.company_id` | `integer` | Always | The ID of the workspace that owns the service. |
| `service.name` | `string` | Always | The service name after whitespace is removed from both ends. The length is measured in Unicode code points. Minimum length: 1 characters. Maximum length: 128 characters. |
| `service.slug` | `string` | Always | A permanent lowercase service key. It starts with a Unicode letter and then contains lowercase Unicode letters, digits, or underscores. The maximum size is 64 UTF-8 bytes. |
| `service.description` | `string` | Optional | The workspace description of the service. |
| `service.owner_team_id` | `integer` | Optional | The positive numeric ID of the owning team. A service without an owning team omits this field. |
| `service.owner_team_name` | `string` | Optional | The display name of the owning team. A service without an owning team omits this field. |
| `service.owner_team_slug` | `string` | Optional | The permanent key of the owning team. A service without an owning team omits this field. |
| `service.tier` | `string` | Always | The service-tier key from the workspace configuration. |
| `service.tags` | `string[]` | Always | The normalized service tags. |
| `service.external_id` | `string` | Optional | The service identifier from an external system. |
| `service.observability_names` | `string[]` | Optional | Alternative service names for telemetry systems. The API trims values and removes case-insensitive duplicates. |
| `service.created_at` | `string (RFC 3339)` | Always | The service creation time. |
| `service.updated_at` | `string (RFC 3339)` | Always | The time of the latest service change. |
| `service.archived_at` | `string (RFC 3339)` | Optional | The service archive time. This field is absent for an active service. |

Returns the created service.

```json
{
  "service": {
    "id": 5,
    "revision": 3,
    "company_id": 12,
    "name": "Payment Service",
    "slug": "payment_service",
    "description": "Handles all payment processing",
    "owner_team_id": 42,
    "owner_team_name": "Platform Team",
    "owner_team_slug": "platform_team",
    "tier": "critical",
    "tags": [
      "critical",
      "payments"
    ],
    "external_id": "payments-prod",
    "observability_names": [
      "payments-api",
      "payments-worker"
    ],
    "created_at": "2025-01-15T10:00:00Z",
    "updated_at": "2025-01-15T10: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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

#### Related operations

- [List available service tiers](https://tellagen.com/api-reference/configuration#configuration-endpoint-get-api-v1-configuration-service-tiers) (`GET /api/v1/configuration/service-tiers`)

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

**Get service.** Returns one service. The response contains the same service fields as the list operation. A service remains available by ID after it is archived.

Required scope: `services:read`

Operation ID: `getService`
Contract: `supported`
Retry guidance: This operation is repeat-safe. Respect Retry-After and use bounded retries.
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 service 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/services/{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 |
| --- | --- | --- | --- |
| `service` | `object` | Always | The complete public record for a service. |
| `service.id` | `integer` | Always | The positive numeric ID of the service. |
| `service.revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `service.company_id` | `integer` | Always | The ID of the workspace that owns the service. |
| `service.name` | `string` | Always | The service name after whitespace is removed from both ends. The length is measured in Unicode code points. Minimum length: 1 characters. Maximum length: 128 characters. |
| `service.slug` | `string` | Always | A permanent lowercase service key. It starts with a Unicode letter and then contains lowercase Unicode letters, digits, or underscores. The maximum size is 64 UTF-8 bytes. |
| `service.description` | `string` | Optional | The workspace description of the service. |
| `service.owner_team_id` | `integer` | Optional | The positive numeric ID of the owning team. A service without an owning team omits this field. |
| `service.owner_team_name` | `string` | Optional | The display name of the owning team. A service without an owning team omits this field. |
| `service.owner_team_slug` | `string` | Optional | The permanent key of the owning team. A service without an owning team omits this field. |
| `service.tier` | `string` | Always | The service-tier key from the workspace configuration. |
| `service.tags` | `string[]` | Always | The normalized service tags. |
| `service.external_id` | `string` | Optional | The service identifier from an external system. |
| `service.observability_names` | `string[]` | Optional | Alternative service names for telemetry systems. The API trims values and removes case-insensitive duplicates. |
| `service.created_at` | `string (RFC 3339)` | Always | The service creation time. |
| `service.updated_at` | `string (RFC 3339)` | Always | The time of the latest service change. |
| `service.archived_at` | `string (RFC 3339)` | Optional | The service archive time. This field is absent for an active service. |

Returns the service.

```json
{
  "service": {
    "id": 5,
    "revision": 3,
    "company_id": 12,
    "name": "Payment Service",
    "slug": "payment_service",
    "description": "Handles all payment processing",
    "owner_team_id": 42,
    "owner_team_name": "Platform Team",
    "owner_team_slug": "platform_team",
    "tier": "critical",
    "tags": [
      "critical",
      "payments"
    ],
    "external_id": "payments-prod",
    "observability_names": [
      "payments-api",
      "payments-worker"
    ],
    "created_at": "2025-01-15T10:00:00Z",
    "updated_at": "2025-01-15T10: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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

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

**Update service.** Updates an active service. If a field is omitted, its current value remains unchanged. JSON null clears description, owner_team_id, or external_id. An empty tags or observability_names array removes all values from that field. The slug cannot change. An archived service returns 409.

Required scope: `services:write`

Operation ID: `updateService`
Contract: `supported`
Retry guidance: Read the current resource and reconcile changes before retrying with its current version. A stale version returns 412.
Prerequisites: Active responder seat, Manage workspace settings

#### 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 service 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/services/{id}' \
  --header 'Authorization: Bearer <token>' \
  --header 'If-Tellagen-Resource-Version: <resource-version>' \
  --header 'Content-Type: application/json' \
  --data '{
  "tier": "important",
  "tags": [],
  "external_id": null,
  "observability_names": [
    "payments-v2"
  ]
}'
```

#### 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 service name. The length must be from 1 through 128 Unicode characters. Minimum length: 1 characters. Maximum length: 128 characters. |
| `description` | `string \| null` | No | The replacement service description. A null value removes the description. |
| `owner_team_id` | `integer \| null` | No | The positive ID of an active owning team. A null value removes team ownership. Minimum: 1. |
| `tier` | `string` | No | A service-tier key from the workspace configuration. |
| `tags` | `string[]` | No | The complete replacement list of tags. An empty array removes all tags. |
| `external_id` | `string \| null` | No | The replacement external identifier. A null or blank value removes the identifier. |
| `observability_names` | `string[]` | No | The complete replacement list of telemetry names. An empty array removes all telemetry names. |

Request JSON:

```json
{
  "tier": "important",
  "tags": [],
  "external_id": null,
  "observability_names": [
    "payments-v2"
  ]
}
```

#### 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 |
| --- | --- | --- | --- |
| `service` | `object` | Always | The complete public record for a service. |
| `service.id` | `integer` | Always | The positive numeric ID of the service. |
| `service.revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `service.company_id` | `integer` | Always | The ID of the workspace that owns the service. |
| `service.name` | `string` | Always | The service name after whitespace is removed from both ends. The length is measured in Unicode code points. Minimum length: 1 characters. Maximum length: 128 characters. |
| `service.slug` | `string` | Always | A permanent lowercase service key. It starts with a Unicode letter and then contains lowercase Unicode letters, digits, or underscores. The maximum size is 64 UTF-8 bytes. |
| `service.description` | `string` | Optional | The workspace description of the service. |
| `service.owner_team_id` | `integer` | Optional | The positive numeric ID of the owning team. A service without an owning team omits this field. |
| `service.owner_team_name` | `string` | Optional | The display name of the owning team. A service without an owning team omits this field. |
| `service.owner_team_slug` | `string` | Optional | The permanent key of the owning team. A service without an owning team omits this field. |
| `service.tier` | `string` | Always | The service-tier key from the workspace configuration. |
| `service.tags` | `string[]` | Always | The normalized service tags. |
| `service.external_id` | `string` | Optional | The service identifier from an external system. |
| `service.observability_names` | `string[]` | Optional | Alternative service names for telemetry systems. The API trims values and removes case-insensitive duplicates. |
| `service.created_at` | `string (RFC 3339)` | Always | The service creation time. |
| `service.updated_at` | `string (RFC 3339)` | Always | The time of the latest service change. |
| `service.archived_at` | `string (RFC 3339)` | Optional | The service archive time. This field is absent for an active service. |

Returns the updated service.

```json
{
  "service": {
    "id": 5,
    "revision": 3,
    "company_id": 12,
    "name": "Payment Service",
    "slug": "payment_service",
    "description": "Handles all payment processing",
    "owner_team_id": 42,
    "owner_team_name": "Platform Team",
    "owner_team_slug": "platform_team",
    "tier": "important",
    "tags": [],
    "external_id": "payments-prod",
    "observability_names": [
      "payments-v2"
    ],
    "created_at": "2025-01-15T10: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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

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

**Archive service.** Archives a service. A repeated request returns 204 without changing the archive time or resource version. By default, list operations exclude archived services. The include_archived=true parameter includes them. Archived services remain available by ID and retain incident references. They cannot be updated.

Required scope: `services:write`

Operation ID: `archiveService`
Contract: `supported`
Retry guidance: Do not retry automatically after an uncertain outcome. Read the resource state and follow this operation’s recovery guidance.
Prerequisites: Active responder seat, Manage workspace settings

#### 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 service 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/services/{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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

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

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

Required scope: `services:write`

Operation ID: `restoreService`
Contract: `supported`
Retry guidance: Do not retry automatically after an uncertain outcome. Read the resource state and follow this operation’s recovery guidance.
Prerequisites: Active responder seat, Manage workspace settings

#### 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 service 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/services/{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 |
| --- | --- | --- | --- |
| `service` | `object` | Always | The complete public record for a service. |
| `service.id` | `integer` | Always | The positive numeric ID of the service. |
| `service.revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `service.company_id` | `integer` | Always | The ID of the workspace that owns the service. |
| `service.name` | `string` | Always | The service name after whitespace is removed from both ends. The length is measured in Unicode code points. Minimum length: 1 characters. Maximum length: 128 characters. |
| `service.slug` | `string` | Always | A permanent lowercase service key. It starts with a Unicode letter and then contains lowercase Unicode letters, digits, or underscores. The maximum size is 64 UTF-8 bytes. |
| `service.description` | `string` | Optional | The workspace description of the service. |
| `service.owner_team_id` | `integer` | Optional | The positive numeric ID of the owning team. A service without an owning team omits this field. |
| `service.owner_team_name` | `string` | Optional | The display name of the owning team. A service without an owning team omits this field. |
| `service.owner_team_slug` | `string` | Optional | The permanent key of the owning team. A service without an owning team omits this field. |
| `service.tier` | `string` | Always | The service-tier key from the workspace configuration. |
| `service.tags` | `string[]` | Always | The normalized service tags. |
| `service.external_id` | `string` | Optional | The service identifier from an external system. |
| `service.observability_names` | `string[]` | Optional | Alternative service names for telemetry systems. The API trims values and removes case-insensitive duplicates. |
| `service.created_at` | `string (RFC 3339)` | Always | The service creation time. |
| `service.updated_at` | `string (RFC 3339)` | Always | The time of the latest service change. |
| `service.archived_at` | `string (RFC 3339)` | Optional | The service archive time. This field is absent for an active service. |

Returns the complete active service.

```json
{
  "service": {
    "id": 5,
    "revision": 3,
    "company_id": 12,
    "name": "Payment Service",
    "slug": "payment_service",
    "description": "Handles all payment processing",
    "owner_team_id": 42,
    "owner_team_name": "Platform Team",
    "owner_team_slug": "platform_team",
    "tier": "critical",
    "tags": [
      "critical",
      "payments"
    ],
    "external_id": "payments-prod",
    "observability_names": [
      "payments-api",
      "payments-worker"
    ],
    "created_at": "2025-01-15T10:00:00Z",
    "updated_at": "2025-01-15T10: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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

## 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: This operation is repeat-safe. Respect Retry-After and use bounded retries.
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 workspace exceeded an active request limit. |
| `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: Retry an identical request with the same Idempotency-Key used on the first attempt, within 24 hours. Never retry an unknown outcome automatically.
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 workspace exceeded an active request limit. |
| `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: This operation is repeat-safe. Respect Retry-After and use bounded retries.
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 workspace exceeded an active request limit. |
| `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: Read the current resource and reconcile changes before retrying with its current version. A stale version returns 412.
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 workspace exceeded an active request limit. |
| `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: Do not retry automatically after an uncertain outcome. Read the resource state and follow this operation’s recovery guidance.
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 workspace exceeded an active request limit. |
| `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: Do not retry automatically after an uncertain outcome. Read the resource state and follow this operation’s recovery guidance.
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 workspace exceeded an active request limit. |
| `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: This operation is repeat-safe. Respect Retry-After and use bounded retries.
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 workspace exceeded an active request limit. |
| `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: Do not retry automatically after an uncertain outcome. Read the resource state and follow this operation’s recovery guidance.
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 workspace exceeded an active request limit. |
| `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: Do not retry automatically after an uncertain outcome. Read the resource state and follow this operation’s recovery guidance.
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 workspace exceeded an active request limit. |
| `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: Do not retry automatically after an uncertain outcome. Read the resource state and follow this operation’s recovery guidance.
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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

## Custom Fields

Manages custom-field definitions and their values on incidents.

### `GET /api/v1/custom-fields`

**List custom fields.** Returns one page sorted by sort_order, creation time, and custom-field ID. By default, the response excludes archived definitions.

Required scope: `custom_fields:read`

Operation ID: `listCustomFields`
Contract: `supported`
Retry guidance: This operation is repeat-safe. Respect Retry-After and use bounded retries.
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 custom fields. 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/custom-fields' \
  --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 |
| --- | --- | --- | --- |
| `fields` | `object[]` | Always | The custom-field definitions in this page. |
| `fields[].id` | `integer` | Always | The positive numeric ID of the custom-field definition. Minimum: 1. |
| `fields[].revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `fields[].company_id` | `integer` | Always | The ID of the workspace that owns the definition. Minimum: 1. |
| `fields[].name` | `string` | Always | The display name of the custom field. |
| `fields[].slug` | `string` | Always | The permanent key used to identify the custom field. |
| `fields[].field_type` | `string` | Always | The value type for this custom field. Possible values: `"string"` \| `"number"` \| `"boolean"` \| `"date"` \| `"time"` \| `"datetime"` \| `"monetary"` \| `"json"` \| `"select"` \| `"multi_select"` |
| `fields[].description` | `string` | Optional | The workspace description of the custom field. |
| `fields[].default_value` | `any` | Optional | The default field value. Its type depends on field_type. |
| `fields[].config` | `object` | Optional | The validation and option configuration for field_type. |
| `fields[].required` | `boolean` | Always | Indicates whether each incident must retain a value for this field. |
| `fields[].sort_order` | `integer` | Always | The display position of the custom field. |
| `fields[].category` | `string` | Always | The workspace category for the custom field. Possible values: `"context"` \| `"impact"` \| `"analysis"` \| `"external"` \| `"custom"` |
| `fields[].visibility_level` | `string` | Always | The configured display prominence for the custom field. Possible values: `"always"` \| `"prominent"` \| `"standard"` \| `"collapsed"` |
| `fields[].show_on_creation` | `boolean` | Always | Indicates whether the incident creation form shows this field. |
| `fields[].required_on_creation` | `boolean` | Always | Indicates whether a create-incident request must supply this field. |
| `fields[].auto_add_to_layout` | `boolean` | Always | Indicates whether Tellagen adds this field to new incident layouts. |
| `fields[].external_source` | `string` | Optional | The source identifier for a custom field in the external category. |
| `fields[].archived_at` | `string (RFC 3339)` | Optional | The definition archive time. This field is absent for an active definition. |
| `fields[].created_at` | `string (RFC 3339)` | Always | The definition creation time. |
| `fields[].updated_at` | `string (RFC 3339)` | Always | The time of the latest definition change. |
| `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 custom-field definitions and pagination fields.

```json
{
  "fields": [
    {
      "id": 50,
      "revision": 2,
      "company_id": 7,
      "name": "Affected Users",
      "slug": "affected_users",
      "field_type": "number",
      "description": "Number of users impacted",
      "default_value": 0,
      "config": {
        "min": 0,
        "precision": 0
      },
      "required": false,
      "sort_order": 1,
      "category": "impact",
      "visibility_level": "prominent",
      "show_on_creation": true,
      "required_on_creation": false,
      "auto_add_to_layout": true,
      "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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

### `POST /api/v1/custom-fields`

**Create custom field.** Creates a custom-field definition. If slug is omitted, the API creates it from name. Before creation, the API validates config and default_value against field_type.

Required scope: `custom_fields:write`

Operation ID: `createCustomField`
Contract: `supported`
Retry guidance: Do not retry automatically after an uncertain outcome. Read the resource state and follow this operation’s recovery guidance.
Prerequisites: Active responder seat, Manage workspace settings

#### Example request

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

```bash
curl --request POST \
  --url 'https://{company}.api.tellagen.com/api/v1/custom-fields' \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "Affected Users",
  "field_type": "number",
  "description": "Number of users impacted",
  "category": "impact",
  "visibility_level": "prominent",
  "show_on_creation": true,
  "config": {
    "min": 0,
    "precision": 0
  }
}'
```

#### 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 field name. The length must be from 1 through 64 characters. Minimum length: 1 characters. Maximum length: 64 characters. |
| `slug` | `string` | No | A permanent URL-safe key. If the request omits slug, the API creates it from name. |
| `field_type` | `string` | Yes | The value type. Allowed values: `"string"` \| `"number"` \| `"boolean"` \| `"date"` \| `"time"` \| `"datetime"` \| `"monetary"` \| `"json"` \| `"select"` \| `"multi_select"` |
| `description` | `string` | No | The workspace description of the custom field. |
| `default_value` | `any` | No | The default field value. Its type must match field_type. |
| `config` | `object` | Conditional | Validation options for field_type. String fields accept max_length. Number fields accept min, max, and precision. Monetary fields also accept currency or currencies. Select fields accept options and allow_custom. The API rejects unsupported keys. Required when: The accepted keys and value shapes depend on field_type. |
| `required` | `boolean` | No | Controls whether each incident must retain a value. If this value is true, the API rejects JSON null and requests to unset the value. |
| `category` | `string` | No | The workspace category. Allowed values: `"context"` \| `"impact"` \| `"analysis"` \| `"external"` \| `"custom"` |
| `visibility_level` | `string` | No | The display prominence. Allowed values: `"always"` \| `"prominent"` \| `"standard"` \| `"collapsed"` |
| `show_on_creation` | `boolean` | No | Controls whether the incident creation form shows this field. |
| `required_on_creation` | `boolean` | No | Controls whether a create-incident request must supply this field. A true value requires show_on_creation to be true. |
| `auto_add_to_layout` | `boolean` | No | Controls whether Tellagen adds this field to new incident layouts. |
| `external_source` | `string` | Conditional | The source identifier for category external. This field is required for that category and is not valid for other categories. Required when: Required when category is "external". |

Request JSON:

```json
{
  "name": "Affected Users",
  "field_type": "number",
  "description": "Number of users impacted",
  "category": "impact",
  "visibility_level": "prominent",
  "show_on_creation": true,
  "config": {
    "min": 0,
    "precision": 0
  }
}
```

#### 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 |
| --- | --- | --- | --- |
| `field` | `object` | Always | The complete public definition of a custom field. |
| `field.id` | `integer` | Always | The positive numeric ID of the custom-field definition. Minimum: 1. |
| `field.revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `field.company_id` | `integer` | Always | The ID of the workspace that owns the definition. Minimum: 1. |
| `field.name` | `string` | Always | The display name of the custom field. |
| `field.slug` | `string` | Always | The permanent key used to identify the custom field. |
| `field.field_type` | `string` | Always | The value type for this custom field. Possible values: `"string"` \| `"number"` \| `"boolean"` \| `"date"` \| `"time"` \| `"datetime"` \| `"monetary"` \| `"json"` \| `"select"` \| `"multi_select"` |
| `field.description` | `string` | Optional | The workspace description of the custom field. |
| `field.default_value` | `any` | Optional | The default field value. Its type depends on field_type. |
| `field.config` | `object` | Optional | The validation and option configuration for field_type. |
| `field.required` | `boolean` | Always | Indicates whether each incident must retain a value for this field. |
| `field.sort_order` | `integer` | Always | The display position of the custom field. |
| `field.category` | `string` | Always | The workspace category for the custom field. Possible values: `"context"` \| `"impact"` \| `"analysis"` \| `"external"` \| `"custom"` |
| `field.visibility_level` | `string` | Always | The configured display prominence for the custom field. Possible values: `"always"` \| `"prominent"` \| `"standard"` \| `"collapsed"` |
| `field.show_on_creation` | `boolean` | Always | Indicates whether the incident creation form shows this field. |
| `field.required_on_creation` | `boolean` | Always | Indicates whether a create-incident request must supply this field. |
| `field.auto_add_to_layout` | `boolean` | Always | Indicates whether Tellagen adds this field to new incident layouts. |
| `field.external_source` | `string` | Optional | The source identifier for a custom field in the external category. |
| `field.archived_at` | `string (RFC 3339)` | Optional | The definition archive time. This field is absent for an active definition. |
| `field.created_at` | `string (RFC 3339)` | Always | The definition creation time. |
| `field.updated_at` | `string (RFC 3339)` | Always | The time of the latest definition change. |

Returns the created custom-field definition.

```json
{
  "field": {
    "id": 50,
    "revision": 1,
    "company_id": 7,
    "name": "Affected Users",
    "slug": "affected_users",
    "field_type": "number",
    "description": "Number of users impacted",
    "default_value": null,
    "config": {
      "min": 0,
      "precision": 0
    },
    "required": false,
    "sort_order": 0,
    "category": "impact",
    "visibility_level": "prominent",
    "show_on_creation": true,
    "required_on_creation": false,
    "auto_add_to_layout": false,
    "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

| 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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

### `GET /api/v1/custom-fields/{id}`

**Get custom field.** Returns one custom-field definition by its positive numeric ID. A definition remains available by ID after it is archived.

Required scope: `custom_fields:read`

Operation ID: `getCustomField`
Contract: `supported`
Retry guidance: This operation is repeat-safe. Respect Retry-After and use bounded retries.
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 custom-field 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/custom-fields/{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 |
| --- | --- | --- | --- |
| `field` | `object` | Always | The complete public definition of a custom field. |
| `field.id` | `integer` | Always | The positive numeric ID of the custom-field definition. Minimum: 1. |
| `field.revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `field.company_id` | `integer` | Always | The ID of the workspace that owns the definition. Minimum: 1. |
| `field.name` | `string` | Always | The display name of the custom field. |
| `field.slug` | `string` | Always | The permanent key used to identify the custom field. |
| `field.field_type` | `string` | Always | The value type for this custom field. Possible values: `"string"` \| `"number"` \| `"boolean"` \| `"date"` \| `"time"` \| `"datetime"` \| `"monetary"` \| `"json"` \| `"select"` \| `"multi_select"` |
| `field.description` | `string` | Optional | The workspace description of the custom field. |
| `field.default_value` | `any` | Optional | The default field value. Its type depends on field_type. |
| `field.config` | `object` | Optional | The validation and option configuration for field_type. |
| `field.required` | `boolean` | Always | Indicates whether each incident must retain a value for this field. |
| `field.sort_order` | `integer` | Always | The display position of the custom field. |
| `field.category` | `string` | Always | The workspace category for the custom field. Possible values: `"context"` \| `"impact"` \| `"analysis"` \| `"external"` \| `"custom"` |
| `field.visibility_level` | `string` | Always | The configured display prominence for the custom field. Possible values: `"always"` \| `"prominent"` \| `"standard"` \| `"collapsed"` |
| `field.show_on_creation` | `boolean` | Always | Indicates whether the incident creation form shows this field. |
| `field.required_on_creation` | `boolean` | Always | Indicates whether a create-incident request must supply this field. |
| `field.auto_add_to_layout` | `boolean` | Always | Indicates whether Tellagen adds this field to new incident layouts. |
| `field.external_source` | `string` | Optional | The source identifier for a custom field in the external category. |
| `field.archived_at` | `string (RFC 3339)` | Optional | The definition archive time. This field is absent for an active definition. |
| `field.created_at` | `string (RFC 3339)` | Always | The definition creation time. |
| `field.updated_at` | `string (RFC 3339)` | Always | The time of the latest definition change. |

Returns the custom-field definition.

```json
{
  "field": {
    "id": 50,
    "revision": 2,
    "company_id": 7,
    "name": "Affected Users",
    "slug": "affected_users",
    "field_type": "number",
    "description": "Number of users impacted",
    "default_value": 0,
    "config": {
      "min": 0,
      "precision": 0
    },
    "required": false,
    "sort_order": 1,
    "category": "impact",
    "visibility_level": "prominent",
    "show_on_creation": true,
    "required_on_creation": false,
    "auto_add_to_layout": true,
    "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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

### `PATCH /api/v1/custom-fields/{id}`

**Update custom field.** Updates an active custom-field definition. The API combines supplied and stored values before validation. This validation includes the stored default_value. Omitted fields stay unchanged.

Required scope: `custom_fields:write`

Operation ID: `updateCustomField`
Contract: `supported`
Retry guidance: Read the current resource and reconcile changes before retrying with its current version. A stale version returns 412.
Prerequisites: Active responder seat, Manage workspace settings

#### 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 custom-field 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/custom-fields/{id}' \
  --header 'Authorization: Bearer <token>' \
  --header 'If-Tellagen-Resource-Version: <resource-version>' \
  --header 'Content-Type: application/json' \
  --data '{
  "required": true,
  "sort_order": 2
}'
```

#### 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 field name. The length must be from 1 through 64 characters. Minimum length: 1 characters. Maximum length: 64 characters. |
| `description` | `string` | No | The replacement field description. |
| `default_value` | `any` | No | The replacement default value. Its type must match field_type. |
| `config` | `object` | No | The replacement validation and option configuration for field_type. |
| `required` | `boolean` | No | Controls whether each incident must retain a value for this field. |
| `sort_order` | `integer` | No | The new display position of the custom field. |
| `category` | `string` | No | The workspace category. Allowed values: `"context"` \| `"impact"` \| `"analysis"` \| `"external"` \| `"custom"` |
| `visibility_level` | `string` | No | The display prominence. Allowed values: `"always"` \| `"prominent"` \| `"standard"` \| `"collapsed"` |
| `show_on_creation` | `boolean` | No | Controls whether the incident creation form shows this field. |
| `required_on_creation` | `boolean` | No | Controls whether a create-incident request must supply this field. |
| `auto_add_to_layout` | `boolean` | No | Controls whether Tellagen adds this field to new incident layouts. |
| `external_source` | `string` | No | The replacement source identifier for category external. |

Request JSON:

```json
{
  "required": true,
  "sort_order": 2
}
```

#### 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 |
| --- | --- | --- | --- |
| `field` | `object` | Always | The complete public definition of a custom field. |
| `field.id` | `integer` | Always | The positive numeric ID of the custom-field definition. Minimum: 1. |
| `field.revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `field.company_id` | `integer` | Always | The ID of the workspace that owns the definition. Minimum: 1. |
| `field.name` | `string` | Always | The display name of the custom field. |
| `field.slug` | `string` | Always | The permanent key used to identify the custom field. |
| `field.field_type` | `string` | Always | The value type for this custom field. Possible values: `"string"` \| `"number"` \| `"boolean"` \| `"date"` \| `"time"` \| `"datetime"` \| `"monetary"` \| `"json"` \| `"select"` \| `"multi_select"` |
| `field.description` | `string` | Optional | The workspace description of the custom field. |
| `field.default_value` | `any` | Optional | The default field value. Its type depends on field_type. |
| `field.config` | `object` | Optional | The validation and option configuration for field_type. |
| `field.required` | `boolean` | Always | Indicates whether each incident must retain a value for this field. |
| `field.sort_order` | `integer` | Always | The display position of the custom field. |
| `field.category` | `string` | Always | The workspace category for the custom field. Possible values: `"context"` \| `"impact"` \| `"analysis"` \| `"external"` \| `"custom"` |
| `field.visibility_level` | `string` | Always | The configured display prominence for the custom field. Possible values: `"always"` \| `"prominent"` \| `"standard"` \| `"collapsed"` |
| `field.show_on_creation` | `boolean` | Always | Indicates whether the incident creation form shows this field. |
| `field.required_on_creation` | `boolean` | Always | Indicates whether a create-incident request must supply this field. |
| `field.auto_add_to_layout` | `boolean` | Always | Indicates whether Tellagen adds this field to new incident layouts. |
| `field.external_source` | `string` | Optional | The source identifier for a custom field in the external category. |
| `field.archived_at` | `string (RFC 3339)` | Optional | The definition archive time. This field is absent for an active definition. |
| `field.created_at` | `string (RFC 3339)` | Always | The definition creation time. |
| `field.updated_at` | `string (RFC 3339)` | Always | The time of the latest definition change. |

Returns the updated custom-field definition.

```json
{
  "field": {
    "id": 50,
    "revision": 3,
    "company_id": 7,
    "name": "Affected Users",
    "slug": "affected_users",
    "field_type": "number",
    "description": "Number of users impacted",
    "config": {
      "min": 0,
      "precision": 0
    },
    "required": true,
    "sort_order": 2,
    "category": "impact",
    "visibility_level": "prominent",
    "show_on_creation": true,
    "required_on_creation": false,
    "auto_add_to_layout": true,
    "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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

### `DELETE /api/v1/custom-fields/{id}`

**Archive custom field.** Archives a custom-field definition. A repeated request returns 204 without changing the archive time or resource version. Existing incident values remain available as historical data. Values for an archived definition cannot be added, changed, or removed.

Required scope: `custom_fields:write`

Operation ID: `archiveCustomField`
Contract: `supported`
Retry guidance: Do not retry automatically after an uncertain outcome. Read the resource state and follow this operation’s recovery guidance.
Prerequisites: Active responder seat, Manage workspace settings

#### 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 custom-field 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/custom-fields/{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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

### `POST /api/v1/custom-fields/{id}/restore`

**Restore custom field.** Restores an archived custom-field definition and returns the active definition. A repeated request returns the active definition without changing its resource version. The API retains existing incident values.

Required scope: `custom_fields:write`

Operation ID: `restoreCustomField`
Contract: `supported`
Retry guidance: Do not retry automatically after an uncertain outcome. Read the resource state and follow this operation’s recovery guidance.
Prerequisites: Active responder seat, Manage workspace settings

#### 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 custom-field 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/custom-fields/{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 |
| --- | --- | --- | --- |
| `field` | `object` | Always | The complete public definition of a custom field. |
| `field.id` | `integer` | Always | The positive numeric ID of the custom-field definition. Minimum: 1. |
| `field.revision` | `integer` | Always | The resource version for concurrency control. Minimum: 1. |
| `field.company_id` | `integer` | Always | The ID of the workspace that owns the definition. Minimum: 1. |
| `field.name` | `string` | Always | The display name of the custom field. |
| `field.slug` | `string` | Always | The permanent key used to identify the custom field. |
| `field.field_type` | `string` | Always | The value type for this custom field. Possible values: `"string"` \| `"number"` \| `"boolean"` \| `"date"` \| `"time"` \| `"datetime"` \| `"monetary"` \| `"json"` \| `"select"` \| `"multi_select"` |
| `field.description` | `string` | Optional | The workspace description of the custom field. |
| `field.default_value` | `any` | Optional | The default field value. Its type depends on field_type. |
| `field.config` | `object` | Optional | The validation and option configuration for field_type. |
| `field.required` | `boolean` | Always | Indicates whether each incident must retain a value for this field. |
| `field.sort_order` | `integer` | Always | The display position of the custom field. |
| `field.category` | `string` | Always | The workspace category for the custom field. Possible values: `"context"` \| `"impact"` \| `"analysis"` \| `"external"` \| `"custom"` |
| `field.visibility_level` | `string` | Always | The configured display prominence for the custom field. Possible values: `"always"` \| `"prominent"` \| `"standard"` \| `"collapsed"` |
| `field.show_on_creation` | `boolean` | Always | Indicates whether the incident creation form shows this field. |
| `field.required_on_creation` | `boolean` | Always | Indicates whether a create-incident request must supply this field. |
| `field.auto_add_to_layout` | `boolean` | Always | Indicates whether Tellagen adds this field to new incident layouts. |
| `field.external_source` | `string` | Optional | The source identifier for a custom field in the external category. |
| `field.archived_at` | `string (RFC 3339)` | Optional | The definition archive time. This field is absent for an active definition. |
| `field.created_at` | `string (RFC 3339)` | Always | The definition creation time. |
| `field.updated_at` | `string (RFC 3339)` | Always | The time of the latest definition change. |

Returns the complete active custom-field definition.

```json
{
  "field": {
    "id": 50,
    "revision": 3,
    "company_id": 7,
    "name": "Affected Users",
    "slug": "affected_users",
    "field_type": "number",
    "description": "Number of users impacted",
    "config": {
      "min": 0,
      "precision": 0
    },
    "required": true,
    "sort_order": 2,
    "category": "impact",
    "visibility_level": "prominent",
    "show_on_creation": true,
    "required_on_creation": false,
    "auto_add_to_layout": true,
    "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. |
| `429` | The workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

### `GET /api/v1/incidents/{id}/custom-fields`

**List incident custom-field values.** Returns one page sorted by field sort_order, custom-field ID, and value-row ID. Values from archived definitions remain available as historical incident data.

Required scope: `custom_fields:read`

Operation ID: `listIncidentCustomFieldValues`
Contract: `supported`
Retry guidance: This operation is repeat-safe. Respect Retry-After and use bounded retries.
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 incident ID returned by this API. The permanent incident reference is suitable for stored references and external links. 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/incidents/{id}/custom-fields' \
  --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 |
| --- | --- | --- | --- |
| `values` | `object[]` | Always | The custom-field values in this page. |
| `values[].id` | `integer` | Always | The positive numeric ID of the stored value. Minimum: 1. |
| `values[].incident_id` | `integer` | Always | The positive numeric ID of the incident. Minimum: 1. |
| `values[].custom_field_id` | `integer` | Always | The positive numeric ID of the custom-field definition. Minimum: 1. |
| `values[].value` | `any` | Optional | The stored field value. Its type depends on field_type. |
| `values[].created_at` | `string (RFC 3339)` | Always | The initial value storage time. |
| `values[].updated_at` | `string (RFC 3339)` | Always | The time of the latest value change. |
| `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 custom-field values and pagination fields.

```json
{
  "values": [
    {
      "id": 1,
      "incident_id": 1,
      "custom_field_id": 50,
      "value": 1500,
      "created_at": "2025-01-30T12:05:00Z",
      "updated_at": "2025-01-30T12:05:00Z"
    }
  ],
  "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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

### `PATCH /api/v1/incidents/{id}/custom-fields`

**Partially update incident custom-field values.** Updates one or more custom-field values for an incident. The request must contain at least one { field_id, value } item. Each value must match its field type and configured limits or options. A required field rejects JSON null. Omitted fields stay unchanged. An unchanged value does not update timestamps or create severity, event, or changelog changes. API v1 still accepts deprecated PUT requests for this path. The next major API version will not accept PUT.

Required scope: `custom_fields:write`

Operation ID: `updateIncidentCustomFieldValues`
Contract: `supported`
Retry guidance: Do not retry automatically after an uncertain outcome. Read the resource state and follow this operation’s recovery guidance.
Prerequisites: Active responder seat, Incident is not archived

#### 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 incident ID returned by this API. The permanent incident reference is suitable for stored references and external links. 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/incidents/{id}/custom-fields' \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '{
  "values": [
    {
      "field_id": 50,
      "value": 1500
    },
    {
      "field_id": 51,
      "value": "enterprise"
    }
  ]
}'
```

#### Request body

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

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `values` | `object[]` | Yes | A non-empty array of { field_id, value } objects. Each field_id must be unique. The API validates each value against field_type and its configuration. Supported types include strings, numbers, booleans, dates, times, monetary objects, JSON values, and configured selections. Minimum items: 1. |
| `values[].field_id` | `integer` | Yes | The positive ID of the custom-field definition. Minimum: 1. |
| `values[].value` | `any` | Yes | The new field value. Its type must match field_type. |

Request JSON:

```json
{
  "values": [
    {
      "field_id": 50,
      "value": 1500
    },
    {
      "field_id": 51,
      "value": "enterprise"
    }
  ]
}
```

#### 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 |
| --- | --- | --- | --- |
| `values` | `object[]` | Always | All current custom-field values for the incident. |
| `values[].id` | `integer` | Always | The positive numeric ID of the stored value. Minimum: 1. |
| `values[].incident_id` | `integer` | Always | The positive numeric ID of the incident. Minimum: 1. |
| `values[].custom_field_id` | `integer` | Always | The positive numeric ID of the custom-field definition. Minimum: 1. |
| `values[].value` | `any` | Optional | The stored field value. Its type depends on field_type. |
| `values[].created_at` | `string (RFC 3339)` | Always | The initial value storage time. |
| `values[].updated_at` | `string (RFC 3339)` | Always | The time of the latest value change. |

Returns all custom-field values for the incident after the update.

```json
{
  "values": [
    {
      "id": 1,
      "incident_id": 1,
      "custom_field_id": 50,
      "value": 1500,
      "created_at": "2025-01-30T12:05:00Z",
      "updated_at": "2025-01-30T12:10:00Z"
    },
    {
      "id": 2,
      "incident_id": 1,
      "custom_field_id": 51,
      "value": "enterprise",
      "created_at": "2025-01-30T12:10:00Z",
      "updated_at": "2025-01-30T12:10: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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |

### `DELETE /api/v1/incidents/{id}/custom-fields/{fieldId}`

**Unset one incident custom-field value.** Removes one stored custom-field value from an incident. A required field cannot be unset. If the incident has no stored value, the request still succeeds without a change.

Required scope: `custom_fields:write`

Operation ID: `unsetIncidentCustomFieldValue`
Contract: `supported`
Retry guidance: Do not retry automatically after an uncertain outcome. Read the resource state and follow this operation’s recovery guidance.
Prerequisites: Active responder seat, Incident is not archived

#### 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 incident ID returned by this API. The permanent incident reference is suitable for stored references and external links. Minimum: 1. |
| `fieldId` | `integer` | Yes | A positive numeric custom-field 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/incidents/{id}/custom-fields/{fieldId}' \
  --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 |
| --- | --- | --- | --- |
| `values` | `object[]` | Always | All current custom-field values for the incident. |
| `values[].id` | `integer` | Always | The positive numeric ID of the stored value. Minimum: 1. |
| `values[].incident_id` | `integer` | Always | The positive numeric ID of the incident. Minimum: 1. |
| `values[].custom_field_id` | `integer` | Always | The positive numeric ID of the custom-field definition. Minimum: 1. |
| `values[].value` | `any` | Optional | The stored field value. Its type depends on field_type. |
| `values[].created_at` | `string (RFC 3339)` | Always | The initial value storage time. |
| `values[].updated_at` | `string (RFC 3339)` | Always | The time of the latest value change. |

Returns all remaining custom-field values for the incident.

```json
{
  "values": [
    {
      "id": 2,
      "incident_id": 1,
      "custom_field_id": 51,
      "value": "enterprise",
      "created_at": "2025-01-30T12:10:00Z",
      "updated_at": "2025-01-30T12:10: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 workspace exceeded an active request limit. |
| `500` | An internal server error prevented completion of the request. |
