# teem API

Base URL: `https://teem.so/api/v1`. Authenticate every request with
`Authorization: Bearer teem_pat_...`. Create an agent in Settings → API & MCP.
Its access key is shown once. Keep it out of source control, browser code and shared logs.

Each agent acts as one person in one organization. Its permissions follow that membership's
current role. Revoking the agent or removing the membership ends access on the next request.
The public organization key and the signing secret are not API credentials.

## Endpoints

| Method | Endpoint | Operation |
| --- | --- | --- |
| GET | `/api/v1/me` | `whoami` |
| PATCH | `/api/v1/me` | `update_profile` |
| GET | `/api/v1/notifications` | `read_notification_settings` |
| PATCH | `/api/v1/notifications` | `update_notification_settings` |
| GET | `/api/v1/organization` | `read_organization` |
| GET | `/api/v1/organization/members` | `list_members` |
| GET | `/api/v1/organization/signing-secret` | `read_signing_secret` |
| GET | `/api/v1/conversations` | `list_conversations` |
| GET | `/api/v1/search` | `search` |
| GET | `/api/v1/conversations/:id` | `get_conversation` |
| POST | `/api/v1/conversations/:id/reply` | `reply_to_conversation` |
| PATCH | `/api/v1/conversations/:id/status` | `set_conversation_status` |
| DELETE | `/api/v1/conversations/:id` | `delete_conversation` |
| PATCH | `/api/v1/conversations/:id/assignment` | `assign_conversation` |
| GET | `/api/v1/slack` | `read_slack_status` |
| PATCH | `/api/v1/slack` | `select_slack_channel` |
| DELETE | `/api/v1/slack` | `disconnect_slack` |
| POST | `/api/v1/conversations/:id/slack/retry` | `retry_slack_delivery` |
| GET | `/api/v1/posts` | `list_posts` |
| GET | `/api/v1/posts/:id` | `get_post` |
| POST | `/api/v1/posts` | `create_post` |
| PATCH | `/api/v1/posts/:id` | `update_post_status` |
| PATCH | `/api/v1/posts/:id/content` | `update_post` |
| DELETE | `/api/v1/posts/:id` | `delete_post` |
| GET | `/api/v1/changelog` | `list_changelog` |
| POST | `/api/v1/changelog` | `create_changelog_entry` |
| POST | `/api/v1/changelog/:id/publish` | `publish_changelog_entry` |
| POST | `/api/v1/changelog/import` | `import_changelog_entry` |
| PATCH | `/api/v1/changelog/:id` | `update_changelog_entry` |
| DELETE | `/api/v1/changelog/:id` | `delete_changelog_entry` |
| GET | `/api/v1/help/topics` | `list_help_topics` |
| POST | `/api/v1/help/topics` | `create_help_topic` |
| PATCH | `/api/v1/help/topics/:id` | `update_help_topic` |
| DELETE | `/api/v1/help/topics/:id` | `delete_help_topic` |
| POST | `/api/v1/help/topics/reorder` | `reorder_help_topics` |
| POST | `/api/v1/help/topics/:id/articles/reorder` | `reorder_help_articles` |
| GET | `/api/v1/help/articles` | `list_help_articles` |
| GET | `/api/v1/help/articles/:id` | `get_help_article` |
| POST | `/api/v1/help/articles` | `create_help_article` |
| PATCH | `/api/v1/help/articles/:id` | `update_help_article` |
| POST | `/api/v1/help/articles/preview` | `preview_help_article` |
| POST | `/api/v1/help/articles/:id/publish` | `publish_help_article` |
| POST | `/api/v1/help/articles/:id/unpublish` | `unpublish_help_article` |
| POST | `/api/v1/help/articles/:id/discard` | `discard_help_article_changes` |
| DELETE | `/api/v1/help/articles/:id` | `delete_help_article` |
| PATCH | `/api/v1/organization` | `update_organization` |
| POST | `/api/v1/organization/public-key/rotate` | `rotate_public_key` |
| PUT | `/api/v1/organization/domain` | `connect_domain` |
| POST | `/api/v1/organization/domain/check` | `check_domain` |
| DELETE | `/api/v1/organization/domain` | `remove_domain` |
| DELETE | `/api/v1/organization` | `delete_organization` |

`GET /me` returns `user`, `organization`, `role`, and agent metadata in the existing `token`
field. User metadata includes
`theme_preference` (`system`, `light`, or `dark`) and `avatar_url` (a same-origin path, or null).
Organization metadata includes `id`, `slug`, `name`, `key` / `public_key`, `board_url`,
`installed_at`, `last_seen_at`, `accent`, `visitor_theme`, and `logo_url` (a same-origin path, or null). Installation timestamps are
null until the widget records them. The signing secret is excluded from these responses.

Settings updates accept a JSON `organization` object with `name`, `accent`, and/or
`visitor_theme` (`system`, `light`, `dark`). Members can update these settings. Public-key
rotation, reading the signing secret, and organization deletion require an owner.
Deleting an organization permanently removes its memberships, agents and access keys.

`PATCH /organization` also accepts `logo_base64` or `remove_logo: true` inside the
`organization` object. Send standard base64 of a JPG, PNG or WebP file up to 2 MB and
40 million pixels. Upload and removal cannot be combined. The logo is stored as a WebP
within 256 × 256 pixels, preserving its proportions and transparency and removing metadata.
Invalid changes leave the saved logo and settings intact. The response includes `logo_url`;
the same logo appears in the organization switcher, public pages and hosted widget.

`PATCH /me` accepts a JSON `user` object with any of `name`, `theme_preference`,
`avatar_base64`, and `remove_avatar`. The name must have 1–100 characters after trimming.
For a photo, send standard base64 of a JPG, PNG or WebP file up to 2 MB and 40 million pixels;
use `remove_avatar: true` to remove it. Upload and removal cannot be combined. Teem stores a
256px square WebP with image metadata removed. The response includes `avatar_url`.
These personal changes apply across the user's organizations. The theme controls the dashboard;
the organization's visitor theme is separate. The sign-in email cannot be changed here.

`GET /notifications` returns `email`, `slack`, and `can_manage_slack`. Both channel objects use
`new_conversation`, `bug_report`, and `feature_request` boolean fields. `PATCH /notifications`
accepts a `notification_settings` object containing either channel. Email choices belong to the
current membership. Slack choices apply to the organization and require an owner.

Success: `{ "data": ... }`.
Error: `{ "error": { "code": "...", "message": "...", "details": {} } }`.
Validation errors include field errors in `details`.

401 means the bearer is invalid; 403 means the role cannot perform the action; 404 means
the resource is absent from the agent's organization; 422 means invalid input.
There is no organization selector: create a separate agent for another organization.

Member lists accept `after` (the previous `meta.next_cursor`) and `limit` (default 25,
maximum 100). Responses contain `data` and `meta.next_cursor`; null ends the list.
Cursors are signed and valid only for their original organization and collection.

Conversation lists accept `status=open|resolved` and use the same pagination fields. A
conversation summary includes the visitor, assignment, latest message, captured page context,
waiting state and timestamps. `GET /conversations/:id` also includes the ordered thread.
Reply bodies are 1–4,000 characters after trimming. Send an `Idempotency-Key` header when you
retry a reply; teem writes and mails it once per key. Set `membership_id` to a current member's
numeric id to assign a conversation, or `null` to clear it. Status accepts `open` or `resolved`.
Conversation ids are opaque `cv_...` values; a valid id from another organization still returns
404.

`PATCH /posts/:id` sets a feedback request's `status` (`open`, `planned`, `in_progress`,
`shipped` or `declined`) and an optional public `note`. teem emails the request's voters when its
status changes. Send `"notify": false` to skip the email.

`GET /slack` returns connection state plus the selected workspace and channel without Slack credentials.
Owners may request the joined channel list, select one with `PATCH /slack`, disconnect with
`DELETE /slack`, and retry a conversation with `POST /conversations/:id/slack/retry`. Members can
read status but cannot change it. OAuth remains a signed browser redirect in Settings and has no
agent-authenticated twin.

REST and MCP share 120 requests per minute per agent. A 429 includes `Retry-After: 60`.
When an organization's trial or subscription has ended, every endpoint except `GET /me` answers
402 with the code `subscription_required`, a message saying when it ended, and
`details.billing_url` for an owner to subscribe. `GET /me` includes a `billing` object.
Agent creation/revocation, membership changes and signing-secret rotation require a browser
session. Switching the browser's organization changes that session only.

## Example

```sh
curl https://teem.so/api/v1/me \
  -H "Authorization: Bearer $TEEM_AGENT_KEY"
```

```sh
curl "https://teem.so/api/v1/conversations?status=open&limit=25" \
  -H "Authorization: Bearer $TEEM_AGENT_KEY"

curl https://teem.so/api/v1/conversations/cv_example/reply \
  -H "Authorization: Bearer $TEEM_AGENT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"body":"We found it and are working on a fix."}'
```

Help Center publishing is explicit. Create or update a draft, keep the returned editable
`revision`, then pass that revision to publish:

```sh
curl https://teem.so/api/v1/help/articles/ha_example \
  -X PATCH \
  -H "Authorization: Bearer $TEEM_AGENT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"help_article":{"expected_revision":3,"body":"## Updated answer\n\nNew steps."}}'

curl https://teem.so/api/v1/help/articles/ha_example/publish \
  -X POST \
  -H "Authorization: Bearer $TEEM_AGENT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"help_article":{"expected_revision":4}}'
```

The [MCP reference](/mcp.md) describes agent connections.
