# Workspaces

> Use 23 Telecom multi-workspace accounts — route SMS through multiple gateway products from one portal account, scope requests with X-Workspace-ID or per-workspace API keys.
> Source: https://docs.23telecom.co.uk/account/workspaces/

Instructions for LLMs: This is one page of the 23 Telecom messaging API docs
(SMS today; more channels planned). Base URL: https://restlink23telecom.com/api/v1,
auth via the X-API-Key header. Match errors on the error_code field, never on
description text. Full docs: https://docs.23telecom.co.uk/llms-full.txt · Schemas: https://docs.23telecom.co.uk/openapi.yaml

By default every request runs against your **main workspace** — if you have a
single workspace, nothing here concerns you. Read on only if your account
routes traffic through more than one SMS gateway product.

## The model

One portal account can own several workspaces. Each workspace binds to one
SMS gateway product; exactly one workspace is **main** (the default fallback).
Workspaces are created by your administrator.

## API keys: no header needed

Each API key is **server-side bound to one workspace** — the key itself
decides where its traffic routes. The `X-Workspace-ID` header is ignored on
API-key requests.

  To send from a different workspace with API-key auth, create an API key
  **inside that workspace** (portal → switch workspace → Settings → API Keys).
  One key per workspace is the cleanest integration pattern.

## JWT: the X-Workspace-ID header

When authenticating with JWT, scope any request to a workspace you own:

```
X-Workspace-ID: 18
```

| Header value | Behavior |
| --- | --- |
| *omitted* | Request runs against your main workspace |
| Valid ID of a live workspace you own | Request scoped to that workspace |
| Not a positive integer | `400 INVALID_WORKSPACE_HEADER` |
| Workspace you don't own, or deleted | `403 CROSS_TENANT_WORKSPACE` |
| No live main workspace on the account | `403 NO_MAIN_WORKSPACE` — contact support |

## List your workspaces (JWT only)

`GET /user/workspaces` returns your live workspaces. It is **not** available
to API-key requests; `GET /user/profile` already embeds the same list.

```bash
curl -H "Authorization: Bearer $TOKEN" \
     https://restlink23telecom.com/api/v1/user/workspaces
```

```json title="200 OK"
[
  { "id": 16, "name": "Main", "is_main": true, "account_id": 279, "product_id": 1430 },
  { "id": 18, "name": "test-space", "is_main": false, "color": "#5298D9", "account_id": 279, "product_id": 1406 }
]
```

| Field | Description |
| --- | --- |
| `id` | Workspace ID — use this in `X-Workspace-ID` |
| `name` | Label chosen by your admin |
| `is_main` | `true` for exactly one workspace |
| `color` | Optional UI tint |
| `account_id` / `product_id` | Bound SMS gateway product |

## Example: send from a specific workspace (JWT)

```bash
curl -X POST https://restlink23telecom.com/api/v1/sms/send \
     -H "Authorization: Bearer $TOKEN" \
     -H "X-Workspace-ID: 18" \
     -H "Content-Type: application/json" \
     -d '{"to":["+14155551234"],"message":"hi","sender_id":"MyApp"}'
```

## Webhooks are per-workspace

Webhook URLs, field selections and signing secrets are configured **per
workspace** — each workspace routes its events independently. See
[webhooks](/webhooks/overview).