# Workspace

Part of AppEngine API Documentation. Full index: https://appengine.appmint.io/documentation.md
## GET /workspace/home

**Home**

`operationId: WorkspaceController_home`

Everything the caller has joined, in one call: `workspaces` and `conversations` (each with `unread`), `unreadTotal`, `myTasks` (open tasks assigned to the caller, soonest due first, up to 20), `upcoming` (meetings from now, up to 10) and `recent` (the latest top-level items, expired ones left out). `me` is the caller as the workspace sees them (email, name, external).

#### Signature

```http
GET /workspace/home () -> { me, workspaces, conversations, unreadTotal, myTasks, upcoming, recent }
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /workspace/home/inbox`
- `GET /workspace/home/tasks`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | { me, workspaces, conversations, unreadTotal, myTasks, upcoming, recent } |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /workspace/home/inbox

**Inbox and mentions**

`operationId: WorkspaceController_inbox`

Items across joined workspaces that are for the caller: `mention` (their email @mentioned), `direct` (messages from others in a direct conversation) and `reply` (replies to their own items). Newest first, up to 100. Each carries `kind`, `workspaceTitle`, `direct` and `unread` (newer than the caller's last read of that workspace).

#### Signature

```http
GET /workspace/home/inbox (kind?: string) -> { data: Item[] }
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `kind` | query | "mentions" \| "direct" \| "replies" | — | Only one kind. Omit for all three. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | { data: Item[] } |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /workspace/home/tasks

**My tasks**

`operationId: WorkspaceController_myTasks`

Tasks in every joined workspace that are assigned to the caller or that the caller created, newest first, in the same shape as a workspace board.

#### Signature

```http
GET /workspace/home/tasks (status?: string) -> Task[]
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `status` | query | string | — | Comma-separated statuses. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Task[] |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /workspace/home/calendar

**My calendar**

`operationId: WorkspaceController_myCalendar`

Events and meetings across joined workspaces. A meeting is listed once, as its reservation; `from`/`to` filter meetings by start time. Meetings are sorted by start.

#### Signature

```http
GET /workspace/home/calendar (from?: string, to?: string) -> { events: Item[], meetings: Meeting[] }
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `from` | query | string | — | ISO date-time. |
| `to` | query | string | — | ISO date-time. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | { events: Item[], meetings: Meeting[] } |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /workspace/home/files

**Recent files**

`operationId: WorkspaceController_recentFiles`

File items, and any item with attachments, across joined workspaces (or one workspace), newest first. Expired and deleted items are left out.

#### Signature

```http
GET /workspace/home/files (workspace?: string, by?: string, limit?: integer) -> Item[]
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | WORKSPACE_NOT_FOUND | Workspace not found | No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). | — |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `workspace` | query | string | — | Only this workspace (must be readable). |
| `by` | query | string | — | Only files posted by this email. |
| `limit` | query | integer | — |  |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Item[] |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Workspace not found — No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /workspace/home/search

**Search everything I have joined**

`operationId: WorkspaceController_searchAll`

Case-insensitive text match on item title, message and summary, and on task title and description, across joined workspaces. Up to 100 of each, newest first. An empty `q` returns nothing.

#### Signature

```http
GET /workspace/home/search (q?: string) -> { items: Item[], tasks }
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `q` | query | string | yes |  |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | { items: Item[], tasks } |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /workspace

**List workspaces**

`operationId: WorkspaceController_list`

Workspaces and conversations the caller can read, each with `unread`. `joined=true` keeps only the ones they are a member of; otherwise public ones they could join are included (never for external callers).

#### Signature

```http
GET /workspace (type?: string, joined?: string) -> Workspace summaries
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `type` | query | "workspace" \| "conversation" | — |  |
| `joined` | query | "true" \| "1" | — | Only workspaces the caller is a member of. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Workspace summaries |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /workspace

**Create a workspace or conversation**

`operationId: WorkspaceController_create`

Creates a workspace (`type` omitted) or a conversation (`type: "conversation"`). The caller becomes its admin. `members` are added as members — people with no account are invited and receive a sign-up email that lands on `redirectUrl`; customers are always external.

**Direct conversations** (`type: "conversation", direct: true`) are private and unique per set of people: if one already exists for exactly these members it comes back with `existing: true` instead of a new one. The title defaults to the other members' names.

#### Signature

```http
POST /workspace (body) -> The workspace summary with `members` (and `existing: true` for a found direct conversation)
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `403` | EXTERNAL_CANNOT_CREATE | External members cannot create workspaces | The caller is a customer, an invited outsider or a user flagged external. | — |
| `400` | NOT_A_PERSON | <email> is not an email address. Members are users or customers, never groups. | A member that is not an email (body `reason: "not-a-person"`). | — |

Plus the standard platform errors: `401`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |

### Request body

```json
{
  "title": "Q4 launch",
  "isPrivate": true,
  "members": [
    "ana@acme.com"
  ]
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The workspace summary with `members` (and `existing: true` for a found direct conversation) |
| `400` | <email> is not an email address. Members are users or customers, never groups. — A member that is not an email (body `reason: "not-a-person"`). |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | External members cannot create workspaces — The caller is a customer, an invited outsider or a user flagged external. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /workspace/digest/run

**Send the away digests now**

`operationId: WorkspaceController_digest`

Runs what the 15-minute job does for this org: emails members the unread activity they missed. `graceMinutes` (default 30) skips activity newer than that; `0` includes everything unread.

#### Signature

```http
POST /workspace/digest/run (graceMinutes?: integer) -> { sent }
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `graceMinutes` | query | integer | — |  |

### Responses

| Status | Meaning |
| --- | --- |
| `201` | { sent } |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /workspace/migrate

**Migrate existing workspaces**

`operationId: WorkspaceController_migrate`

Idempotent backfill: gives workspaces without one a `type` (workspace) and `status` (active), and links each item to its workspace, typing unknown items as `file` or `message`.

#### Signature

```http
POST /workspace/migrate () -> { workspaces, items } — how many were changed
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |

### Responses

| Status | Meaning |
| --- | --- |
| `201` | { workspaces, items } — how many were changed |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /workspace/item/{iid}

**Get an item**

`operationId: WorkspaceController_getItem`

#### Signature

```http
GET /workspace/item/{iid} (iid: string) -> The item
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | ITEM_NOT_FOUND | Item not found | No such item, or it has expired. | — |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `iid` | path | string | yes | workspace_item sk. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The item |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Item not found — No such item, or it has expired. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## DELETE /workspace/item/{iid}

**Delete an item**

`operationId: WorkspaceController_deleteItem`

A tombstone: the card stays in the timeline with `deleted: true`; its message, summary and files are cleared.

#### Signature

```http
DELETE /workspace/item/{iid} (iid: string) -> The tombstoned item
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | ITEM_NOT_FOUND | Item not found | No such item, or it has expired. | — |
| `403` | NOT_AUTHOR | Only the author or an admin can delete this | The caller did not write the item and is not an admin. | — |

Plus the standard platform errors: `401`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `iid` | path | string | yes | workspace_item sk. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The tombstoned item |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | Only the author or an admin can delete this — The caller did not write the item and is not an admin. |
| `404` | Item not found — No such item, or it has expired. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## PATCH /workspace/item/{iid}

**Edit an item**

`operationId: WorkspaceController_patchItem`

The author or a workspace admin edits `title`, `summary`, `message` (mentions are re-read from it), `files`, `expiresAt`, `status`, `room`, `lead` and `dueDate`. The item is marked `edited`.

#### Signature

```http
PATCH /workspace/item/{iid} (iid: string, body) -> The item
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | ITEM_NOT_FOUND | Item not found | No such item, or it has expired. | — |
| `403` | NOT_AUTHOR | Only the author or an admin can edit this | The caller did not write the item and is not an admin. | — |

Plus the standard platform errors: `401`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `iid` | path | string | yes | workspace_item sk. |

### Request body

```json
{
  "message": "Updated: kickoff moves to Tuesday."
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The item |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | Only the author or an admin can edit this — The caller did not write the item and is not an admin. |
| `404` | Item not found — No such item, or it has expired. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /workspace/{id}

**Get a workspace**

`operationId: WorkspaceController_get`

#### Signature

```http
GET /workspace/{id} (id: string) -> Summary plus `members`, `pinnedItems` and `intakeForm`
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | WORKSPACE_NOT_FOUND | Workspace not found | No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). | — |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `id` | path | string | yes | Workspace sk. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Summary plus `members`, `pinnedItems` and `intakeForm` |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Workspace not found — No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## PATCH /workspace/{id}

**Update a workspace**

`operationId: WorkspaceController_patch`

Admins change `title`, `description`, `icon`, `isPrivate`, `expiresAt`, `pinnedItems` and `intakeForm`. Other fields are ignored; an empty body returns the summary unchanged.

#### Signature

```http
PATCH /workspace/{id} (id: string, body) -> The workspace summary
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | WORKSPACE_NOT_FOUND | Workspace not found | No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). | — |
| `403` | ADMIN_REQUIRED | Only an admin of this workspace can do that | The caller is neither the owner nor a member with accessType admin. | — |

Plus the standard platform errors: `401`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `id` | path | string | yes | Workspace sk. |

### Request body

```json
{
  "title": "Q4 launch (final)"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The workspace summary |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | Only an admin of this workspace can do that — The caller is neither the owner nor a member with accessType admin. |
| `404` | Workspace not found — No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /workspace/{id}/archive

**Archive a workspace**

`operationId: WorkspaceController_archive`

Admins only. Sets `status: archived`.

#### Signature

```http
POST /workspace/{id}/archive (id: string) -> The workspace summary
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | WORKSPACE_NOT_FOUND | Workspace not found | No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). | — |
| `403` | ADMIN_REQUIRED | Only an admin of this workspace can do that | The caller is neither the owner nor a member with accessType admin. | — |

Plus the standard platform errors: `401`, `429`, `500`.

#### See also

- `POST /workspace/{id}/restore`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `id` | path | string | yes | Workspace sk. |

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The workspace summary |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | Only an admin of this workspace can do that — The caller is neither the owner nor a member with accessType admin. |
| `404` | Workspace not found — No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /workspace/{id}/restore

**Restore an archived workspace**

`operationId: WorkspaceController_restore`

Admins only. Sets `status: active`.

#### Signature

```http
POST /workspace/{id}/restore (id: string) -> The workspace summary
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | WORKSPACE_NOT_FOUND | Workspace not found | No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). | — |
| `403` | ADMIN_REQUIRED | Only an admin of this workspace can do that | The caller is neither the owner nor a member with accessType admin. | — |

Plus the standard platform errors: `401`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `id` | path | string | yes | Workspace sk. |

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The workspace summary |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | Only an admin of this workspace can do that — The caller is neither the owner nor a member with accessType admin. |
| `404` | Workspace not found — No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /workspace/{id}/members

**List members**

`operationId: WorkspaceController_members`

#### Signature

```http
GET /workspace/{id}/members (id: string) -> Members, each with a computed `expired`
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | WORKSPACE_NOT_FOUND | Workspace not found | No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). | — |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `id` | path | string | yes | Workspace sk. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Members, each with a computed `expired` |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Workspace not found — No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /workspace/{id}/members

**Add members**

`operationId: WorkspaceController_addMembers`

Any member can add people. Each entry is `{ email, name?, accessType?: admin|member|guest, external?, expiresAt? }`; an existing member is updated in place. People with no account are invited (status `invited`) and emailed a sign-up link that lands on `redirectUrl`; others are notified they were added. The body may also be the bare array or a single member object.

#### Signature

```http
POST /workspace/{id}/members (id: string, body) -> All members
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | WORKSPACE_NOT_FOUND | Workspace not found | No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). | — |
| `403` | JOIN_REQUIRED | Join this workspace to post in it | The caller can read the workspace but is not a member. Body also carries `reason: "join-required"`. | POST /workspace/{id}/join (public workspaces) or ask an admin to add you. |
| `400` | NO_MEMBERS | No members given | No entry with an email. | — |

Plus the standard platform errors: `401`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `id` | path | string | yes | Workspace sk. |

### Request body

```json
{
  "members": [
    {
      "email": "lee@partner.io",
      "accessType": "guest",
      "expiresAt": "2026-12-31T00:00:00Z"
    }
  ],
  "redirectUrl": "https://app.acme.com/signup"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | All members |
| `400` | No members given — No entry with an email. |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | Join this workspace to post in it — The caller can read the workspace but is not a member. Body also carries `reason: "join-required"`. |
| `404` | Workspace not found — No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## DELETE /workspace/{id}/members/{email}

**Remove a member**

`operationId: WorkspaceController_removeMember`

Admins remove anyone except the owner; any member may remove themselves (same as leave).

#### Signature

```http
DELETE /workspace/{id}/members/{email} (id: string, email: string) -> All remaining members
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | WORKSPACE_NOT_FOUND | Workspace not found | No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). | — |
| `403` | ADMIN_REQUIRED | Only an admin of this workspace can do that | The caller is neither the owner nor a member with accessType admin. | — |

Plus the standard platform errors: `401`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `id` | path | string | yes | Workspace sk. |
| `email` | path | string | yes | Member email (case-insensitive). |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | All remaining members |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | Only an admin of this workspace can do that — The caller is neither the owner nor a member with accessType admin. |
| `404` | Workspace not found — No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## PATCH /workspace/{id}/members/{email}

**Change a member**

`operationId: WorkspaceController_patchMember`

Admins change `accessType` and `expiresAt` (null clears it); the person is emailed whenever their access changes. Anyone may change their own `notify` and `mutedUntil`.

#### Signature

```http
PATCH /workspace/{id}/members/{email} (id: string, email: string, body) -> All members
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | WORKSPACE_NOT_FOUND | Workspace not found | No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). | — |
| `403` | ADMIN_REQUIRED | Only an admin of this workspace can do that | The caller is neither the owner nor a member with accessType admin. | — |

Plus the standard platform errors: `401`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `id` | path | string | yes | Workspace sk. |
| `email` | path | string | yes | Member email (case-insensitive). |

### Request body

```json
{
  "accessType": "admin"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `200` | All members |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | Only an admin of this workspace can do that — The caller is neither the owner nor a member with accessType admin. |
| `404` | Workspace not found — No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /workspace/{id}/join

**Join a public workspace**

`operationId: WorkspaceController_join`

Adds the caller as a member. Already a member: returns the members unchanged.

#### Signature

```http
POST /workspace/{id}/join (id: string) -> All members
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | WORKSPACE_NOT_FOUND | Workspace not found | No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). | — |
| `403` | INVITE_ONLY | You have to be invited to this one | The workspace is private. | — |

Plus the standard platform errors: `401`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `id` | path | string | yes | Workspace sk. |

### Responses

| Status | Meaning |
| --- | --- |
| `201` | All members |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You have to be invited to this one — The workspace is private. |
| `404` | Workspace not found — No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /workspace/{id}/leave

**Leave a workspace**

`operationId: WorkspaceController_leave`

#### Signature

```http
POST /workspace/{id}/leave (id: string) -> All remaining members
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | WORKSPACE_NOT_FOUND | Workspace not found | No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). | — |
| `403` | JOIN_REQUIRED | Join this workspace to post in it | The caller can read the workspace but is not a member. Body also carries `reason: "join-required"`. | POST /workspace/{id}/join (public workspaces) or ask an admin to add you. |

Plus the standard platform errors: `401`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `id` | path | string | yes | Workspace sk. |

### Responses

| Status | Meaning |
| --- | --- |
| `201` | All remaining members |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | Join this workspace to post in it — The caller can read the workspace but is not a member. Body also carries `reason: "join-required"`. |
| `404` | Workspace not found — No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /workspace/{id}/items

**Read the journal**

`operationId: WorkspaceController_items`

Top-level items newest first (replies are not listed; use `thread`). No filter is the Activity view; `type` limits to one item type; `room` to one room; `before` pages back. `thread` returns one item and its replies. Expired items are hidden unless `includeExpired=true`.

#### Signature

```http
GET /workspace/{id}/items (id: string, type?: string, room?: string, before?: string, limit?: integer, thread?: string, includeExpired?: string) -> { data: Item[], total, hasMore }
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | WORKSPACE_NOT_FOUND | Workspace not found | No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). | — |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `id` | path | string | yes | Workspace sk. |
| `type` | query | string | — | Item type, or `activity` for all. |
| `room` | query | string | — | Room id. |
| `before` | query | string | — | ISO date-time; items created before it. |
| `limit` | query | integer | — | Default 50, max 200. |
| `thread` | query | string | — | Item sk: that item and its replies. |
| `includeExpired` | query | "true" | — |  |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | { data: Item[], total, hasMore } |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Workspace not found — No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /workspace/{id}/item

**Post an item**

`operationId: WorkspaceController_createItem`

Members post any item type in one call: `message` (default), `file`, `task`, `event` / `meeting`, `agenda`, `analytics`, `data`, `team`, `block`.

- **task**: creates the `task` record (title, `description`, `dueDate`, `assignTo`, optional `agenda` project) and links it; assignees are notified. Fields may be flat or under `task`.
- **meeting** (or `event` with `meeting`): creates a reservation on the org's `meeting` definition (`startTime`, `endTime`, `timezone`, `meetingLink`, `meetingInfo`, `invites`).
- **reply**: set `parentItem` to a top-level item in this workspace; replies are one level deep and inherit its room.

@mentions in `message` (and any emails in `mentions`) are notified; direct-conversation members are notified of every message.

#### Signature

```http
POST /workspace/{id}/item (id: string, body) -> The created item
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | WORKSPACE_NOT_FOUND | Workspace not found | No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). | — |
| `403` | JOIN_REQUIRED | Join this workspace to post in it | The caller can read the workspace but is not a member. Body also carries `reason: "join-required"`. | POST /workspace/{id}/join (public workspaces) or ask an admin to add you. |
| `400` | MEMBER_ITEM | Member entries are written by the members routes | `type: "member"`. | — |
| `422` | NO_MEETING_DEFINITION | This org has no "meeting" reservation definition yet (run org initialization) | Posting a meeting before the org has a `meeting` reservation definition. | — |

Plus the standard platform errors: `401`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `x-client-info` | header | string | yes |  |
| `id` | path | string | yes | Workspace sk. |

### Request body

```json
{
  "message": "Kickoff notes are up @ana@acme.com",
  "room": {
    "label": "Design"
  }
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The created item |
| `400` | Member entries are written by the members routes — `type: "member"`. |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | Join this workspace to post in it — The caller can read the workspace but is not a member. Body also carries `reason: "join-required"`. |
| `404` | Workspace not found — No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). |
| `422` | This org has no "meeting" reservation definition yet (run org initialization) — Posting a meeting before the org has a `meeting` reservation definition. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /workspace/{id}/rooms

**List rooms**

`operationId: WorkspaceController_rooms`

Rooms that messages in this workspace were posted to, most recently active first, with message counts.

#### Signature

```http
GET /workspace/{id}/rooms (id: string) -> [{ id, label, lastActivityAt, count }]
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | WORKSPACE_NOT_FOUND | Workspace not found | No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). | — |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `id` | path | string | yes | Workspace sk. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | [{ id, label, lastActivityAt, count }] |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Workspace not found — No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /workspace/{id}/tasks

**Task board**

`operationId: WorkspaceController_tasks`

The workspace's tasks (up to 500) as the board shows them, with assignee names, files and room from the journal card, the agenda (project) and `overdue`.

#### Signature

```http
GET /workspace/{id}/tasks (id: string, status?: string, assignTo?: string, agenda?: string) -> Task[]
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | WORKSPACE_NOT_FOUND | Workspace not found | No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). | — |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `id` | path | string | yes | Workspace sk. |
| `status` | query | string | — | Comma-separated statuses. |
| `assignTo` | query | string | — | Assignee email. |
| `agenda` | query | string | — | Agenda item sk, or `none` for tasks in no project. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Task[] |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Workspace not found — No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /workspace/{id}/meetings/{mid}

**Get a meeting**

`operationId: WorkspaceController_getMeeting`

#### Signature

```http
GET /workspace/{id}/meetings/{mid} (id: string, mid: string) -> The meeting
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | WORKSPACE_NOT_FOUND | Workspace not found | No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). | — |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `id` | path | string | yes | Workspace sk. |
| `mid` | path | string | yes | reservation sk |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The meeting |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Workspace not found — No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## PATCH /workspace/{id}/meetings/{mid}

**Edit or cancel a meeting**

`operationId: WorkspaceController_patchMeeting`

Members change `title`, `startTime`, `endTime`, `timezone`, `meetingLink`, `meetingInfo` and `invites` (existing invitees keep their response); `cancel: true` cancels it. Moving the start past the current end pushes the end to one hour after the start. The journal card follows.

#### Signature

```http
PATCH /workspace/{id}/meetings/{mid} (id: string, mid: string, body) -> The meeting
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | WORKSPACE_NOT_FOUND | Workspace not found | No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). | — |
| `403` | JOIN_REQUIRED | Join this workspace to post in it | The caller can read the workspace but is not a member. Body also carries `reason: "join-required"`. | POST /workspace/{id}/join (public workspaces) or ask an admin to add you. |
| `400` | BAD_TIMES | The meeting has to end after it starts | `endTime` is not after `startTime`. | — |

Plus the standard platform errors: `401`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `id` | path | string | yes | Workspace sk. |
| `mid` | path | string | yes | reservation sk |

### Request body

```json
{
  "startTime": "2026-10-08T15:00:00Z"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The meeting |
| `400` | The meeting has to end after it starts — `endTime` is not after `startTime`. |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | Join this workspace to post in it — The caller can read the workspace but is not a member. Body also carries `reason: "join-required"`. |
| `404` | Workspace not found — No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /workspace/{id}/tasks/{tid}

**Get a task**

`operationId: WorkspaceController_getTask`

#### Signature

```http
GET /workspace/{id}/tasks/{tid} (id: string, tid: string) -> The task
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | WORKSPACE_NOT_FOUND | Workspace not found | No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). | — |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `id` | path | string | yes | Workspace sk. |
| `tid` | path | string | yes | task sk |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The task |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Workspace not found — No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## PATCH /workspace/{id}/tasks/{tid}

**Edit a task**

`operationId: WorkspaceController_patchTask`

Members change `title`, `description`, `status` (done/approved stamps `completedAt`), `dueDate`, `assignTo` and `agenda` (null removes it). Each change is appended to the task history and mirrored on its journal card; newly assigned people are notified. No change returns the task as is.

#### Signature

```http
PATCH /workspace/{id}/tasks/{tid} (id: string, tid: string, body) -> The task
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | WORKSPACE_NOT_FOUND | Workspace not found | No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). | — |
| `403` | JOIN_REQUIRED | Join this workspace to post in it | The caller can read the workspace but is not a member. Body also carries `reason: "join-required"`. | POST /workspace/{id}/join (public workspaces) or ask an admin to add you. |

Plus the standard platform errors: `401`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `id` | path | string | yes | Workspace sk. |
| `tid` | path | string | yes | task sk |

### Request body

```json
{
  "status": "done"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The task |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | Join this workspace to post in it — The caller can read the workspace but is not a member. Body also carries `reason: "join-required"`. |
| `404` | Workspace not found — No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /workspace/{id}/files

**Workspace files**

`operationId: WorkspaceController_files`

The workspace's `file` items, newest first, up to 200.

#### Signature

```http
GET /workspace/{id}/files (id: string) -> Item[]
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | WORKSPACE_NOT_FOUND | Workspace not found | No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). | — |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `id` | path | string | yes | Workspace sk. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Item[] |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Workspace not found — No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /workspace/{id}/calendar

**Workspace calendar**

`operationId: WorkspaceController_calendar`

Event items and meetings (reservations) of this workspace; `from`/`to` filter meetings by start time.

#### Signature

```http
GET /workspace/{id}/calendar (id: string, from?: string, to?: string) -> { events: Item[], meetings: Meeting[] }
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | WORKSPACE_NOT_FOUND | Workspace not found | No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). | — |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `id` | path | string | yes | Workspace sk. |
| `from` | query | string | — |  |
| `to` | query | string | — |  |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | { events: Item[], meetings: Meeting[] } |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Workspace not found — No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /workspace/{id}/agenda

**Agendas (projects)**

`operationId: WorkspaceController_agenda`

The workspace's `agenda` items, each with `leadName` and `progress: { total, done, open, overdue }` counted from the tasks linked to it.

#### Signature

```http
GET /workspace/{id}/agenda (id: string) -> Agenda items with progress
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | WORKSPACE_NOT_FOUND | Workspace not found | No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). | — |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `id` | path | string | yes | Workspace sk. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Agenda items with progress |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Workspace not found — No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /workspace/{id}/analytics

**Workspace analytics**

`operationId: WorkspaceController_analytics`

Counts for the workspace: tasks by status and assignee plus overdue; items by type and per ISO week; members and how many posted in the last 30 days.

#### Signature

```http
GET /workspace/{id}/analytics (id: string) -> { tasks: { total, byStatus, byAssignee, overdue }, items: { total, byType, perWeek }, members: { total, activeLast30Days } }
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | WORKSPACE_NOT_FOUND | Workspace not found | No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). | — |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `id` | path | string | yes | Workspace sk. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | { tasks: { total, byStatus, byAssignee, overdue }, items: { total, byType, perWeek }, members: { total, activeLast30Days } } |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Workspace not found — No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /workspace/{id}/search

**Search a workspace**

`operationId: WorkspaceController_search`

Case-insensitive match on item title/message/summary and task title/description in this workspace. Up to 100 of each.

#### Signature

```http
GET /workspace/{id}/search (id: string, q?: string) -> { items: Item[], tasks }
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | WORKSPACE_NOT_FOUND | Workspace not found | No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). | — |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `id` | path | string | yes | Workspace sk. |
| `q` | query | string | yes |  |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | { items: Item[], tasks } |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Workspace not found — No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /workspace/{id}/read

**Mark as read**

`operationId: WorkspaceController_read`

Stamps the caller's `lastReadAt`, which clears their unread count. A reader who is not a member gets `{ read: false }`.

#### Signature

```http
POST /workspace/{id}/read (id: string) -> { read, at? }
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | WORKSPACE_NOT_FOUND | Workspace not found | No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). | — |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `id` | path | string | yes | Workspace sk. |

### Responses

| Status | Meaning |
| --- | --- |
| `201` | { read, at? } |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Workspace not found — No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /workspace/{id}/pin/{iid}

**Pin an item**

`operationId: WorkspaceController_pin`

Members pin an item to the workspace.

#### Signature

```http
POST /workspace/{id}/pin/{iid} (id: string, iid: string) -> The pinned items
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | WORKSPACE_NOT_FOUND | Workspace not found | No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). | — |
| `403` | JOIN_REQUIRED | Join this workspace to post in it | The caller can read the workspace but is not a member. Body also carries `reason: "join-required"`. | POST /workspace/{id}/join (public workspaces) or ask an admin to add you. |

Plus the standard platform errors: `401`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `id` | path | string | yes | Workspace sk. |
| `iid` | path | string | yes | workspace_item sk. |

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The pinned items |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | Join this workspace to post in it — The caller can read the workspace but is not a member. Body also carries `reason: "join-required"`. |
| `404` | Workspace not found — No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## DELETE /workspace/{id}/pin/{iid}

**Unpin an item**

`operationId: WorkspaceController_unpin`

#### Signature

```http
DELETE /workspace/{id}/pin/{iid} (id: string, iid: string) -> The pinned items
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | WORKSPACE_NOT_FOUND | Workspace not found | No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). | — |
| `403` | JOIN_REQUIRED | Join this workspace to post in it | The caller can read the workspace but is not a member. Body also carries `reason: "join-required"`. | POST /workspace/{id}/join (public workspaces) or ask an admin to add you. |

Plus the standard platform errors: `401`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `id` | path | string | yes | Workspace sk. |
| `iid` | path | string | yes | workspace_item sk. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The pinned items |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | Join this workspace to post in it — The caller can read the workspace but is not a member. Body also carries `reason: "join-required"`. |
| `404` | Workspace not found — No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /workspace/{id}/notify

**My notification setting**

`operationId: WorkspaceController_notify`

The caller's own notification level for this workspace and an optional mute end.

#### Signature

```http
POST /workspace/{id}/notify (id: string, body) -> All members
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | WORKSPACE_NOT_FOUND | Workspace not found | No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). | — |
| `403` | JOIN_REQUIRED | Join this workspace to post in it | The caller can read the workspace but is not a member. Body also carries `reason: "join-required"`. | POST /workspace/{id}/join (public workspaces) or ask an admin to add you. |

Plus the standard platform errors: `401`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `orgid` | header | string | yes |  |
| `id` | path | string | yes | Workspace sk. |

### Request body

```json
{
  "notify": "mentions"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | All members |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | Join this workspace to post in it — The caller can read the workspace but is not a member. Body also carries `reason: "join-required"`. |
| `404` | Workspace not found — No such workspace, it has expired, or the caller cannot read it (private, or external caller on a public one). |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

