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

# Workspaces, organizations and API keys

> How workspaces keep customers apart, and API key management.

## Workspaces

A workspace is the boundary between one customer and another. Agents, tools, integrations, knowledge, calls, keys and webhooks belong to exactly one. `GET /v1/workspaces` lists the workspaces the credential can see (an API key sees exactly one). `GET /v1/workspaces/{id}` adds `your_role` and the agents in scope. `PATCH /v1/workspaces/{id}` renames (owner).

## Organizations

An organization is the membership and billing umbrella over workspaces. `GET /v1/organizations` lists the caller's workspace organizations. `GET /v1/organizations/{organization_id}` reads one (`organizations:read`); an id the credential cannot see answers `404 not_found`, the same as an id that never existed. Membership and invitations are managed in the Sempera App.

### Reseller-funded child organizations

`POST /v1/organizations` creates a child workspace under an existing reseller. It requires a signed-in reseller owner or admin, the session's CSRF token and an allowed Sempera App origin. Workspace API keys do not carry reseller authority and are refused, even with `organizations:write`.

The JSON body contains `parent_organization_id` (the reseller partner id), `name`, `owner_email`, `external_ref` and optional `support_responsibility` (`partner` or `sempera`, default `partner`). Other fields are refused. The customer's owner must already have an active Sempera account with a verified email and cannot be reseller staff. An established customer receives an ownership invitation through the existing invitation flow. The parent needs an approved entitlement and an available customer slot.

The child is always wholesale-funded: the reseller, not the child, is responsible for funding. Workspace creation and its parent link commit together. A new child returns 201. Reusing an `external_ref` under the same parent returns the existing child with 200, including when the parent's customer slots are now full. Keep the reference stable after a lost response; use a different reference for a different customer. The App generates a reference for retries within the open form when none is supplied.

The response's `id` is the partner customer id; `workspace_id` is the child's workspace id. Manage existing children in the signed-in partner console: list and read customers, suspend or resume service, and read attributed usage for a date range. Those session-authenticated management operations are not part of this canonical API's published route contract. Recorded amounts are not credit balances.

Each action checks the caller's current reseller role and the parent-child relationship. Creating a child gives reseller staff no membership in it and no access to its calls or recordings.

Credit purchases, credit transfers and reseller price configuration are not enabled by this endpoint. The published credit offers remain catalogue-only. A usage allowance is not a purchased balance, and credits are not converted into minutes or cash. Credit settlement, reservation/consumption, reversals and price-floor enforcement must be implemented before these operations can be offered.

## Doug text Assistant

These routes manage the workspace owner's text channel from their signed-in Sempera App session. API keys, including owner and CI keys, cannot use them. Reads require `workspaces:read`; writes require `workspaces:write`, owner authority and CSRF proof. New work requires an active workspace and Assistant entitlement. The channel is default off and is available only on a deployment configured to deliver messages; these routes do not activate a provider.

| Route | Contract |
| - | - |
| `GET /v1/text-assistant/settings` | Returns `name`, `available`, `enabled`, `linked`, masked `mobile` or null, and `pending`. |
| `PATCH /v1/text-assistant/settings` | Takes a boolean `enabled` and returns the settings. Disabling remains available without entitlement. |
| `POST /v1/text-assistant/link` | Takes `mobile` in international E.164 format and sends a linking code. Returns `sent: true`, not a verified link. |
| `POST /v1/text-assistant/link/confirm` | Takes the received `code` in the authenticated App session and returns settings after successful verification. |
| `DELETE /v1/text-assistant/link` | Removes the owner's link and its messages, then returns settings. Unlinking remains available without entitlement. |
| `GET /v1/text-assistant/thread` | Returns only this owner's messages in the current workspace, never another owner's thread. |

The thread response contains `name`, `messages`, `has_more` and `next_before`. Each message contains `id`, `body`, nullable `answer`, `state` and timestamp `at`. A page contains up to 50 messages, oldest first within that page. Pass `before=<next_before>` to fetch the next older page. The cursor must be a canonical UUID in this owner's workspace thread; an unknown or foreign cursor returns no messages. An invalid cursor answers `400 validation_failed`.

Linking codes expire after ten minutes and allow at most five verification attempts. Requests are throttled, and an already linked number must be unlinked before changing it. Settings expose only the mobile number's last four digits. A disabled or unavailable channel does not imply a successful send.

## Service version

A workspace pins the Sempera runtime release its new sessions start on, independent of any agent's published version. Only releases installed on this deployment are listed; today that is one.

* `GET /v1/org/service-versions` (`workspaces:read`) answers `{"versions": [...]}`, each with `id`, `name` and `available`.
* `GET /v1/org/service-version` (`workspaces:read`) answers the pin: `{"version": "...", "applies_to": "new_sessions"}`. A pin that cannot be read answers `503 service_unavailable`.
* `PUT /v1/org/service-version` (`workspaces:write`, owner only) with `{"version": "...", "expected_version": "..."}` changes it. A stale `expected_version` or a release that is not installed answers `409 conflict`; a paused workspace answers `403 forbidden` on every route here.

Sessions already running keep the release they started on. A call, preview or test whose workspace pin cannot be read, or names a release that is not installed, is refused before it starts rather than run on another release.

## Platform

`GET /v1/platform` (scope `deploy:read`, the CI and certification credential) reports the deployment's build identity, its production posture and the current call-capacity snapshot. It never names a machine or a path.

## API keys

`GET /v1/api-keys`, `POST /v1/api-keys`, `GET /v1/api-keys/{id}`, `PATCH /v1/api-keys/{id}`, `DELETE /v1/api-keys/{id}`. See [Authentication and credentials](https://docs.sempera.org/authentication.md) for kinds, roles and scopes. Creating, renaming and revoking need the owner role and `api_keys:write`; from the App they also carry the session's CSRF token. `PATCH` takes `{"name": "..."}` (1 to 120 characters) and changes only the label: the secret, scopes and role stay. A revoked key and a connection key (named `mcp:`, `oauth:` or `rest:`) answer `409 conflict`, and a new name cannot start with one of those prefixes.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.