Workspaces
Copy page
Without X-Workspace-ID, a workspace-scoped portal-session request from the account owner runs against the main workspace. A team member uses the main workspace if permitted, otherwise their allowed workspace with the lowest id. For endpoints that accept API keys, the request runs in the workspace the key belongs to. If you have a single workspace, nothing here concerns you.
The model
Section titled “The model”One portal account can own several workspaces. Each workspace binds to one
SMS gateway product; exactly one workspace is main (the account owner’s default when a portal-session request has no X-Workspace-ID).
Workspaces are created by your administrator. The account owner can rename them (see Rename a workspace).
API keys: no header needed
Section titled “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.
GET /user/workspaces and other portal-session-only routes refuse API keys. The key is still authenticated first, in this order, so you can receive:
| Status | Code | When | Retry-After |
|---|---|---|---|
| 400 | AMBIGUOUS_AUTHENTICATION |
API key sent together with a session cookie | – |
| 503 | API_KEY_VALIDATION_UNAVAILABLE |
The key could not be checked | 1 |
| 503 | AUTH_PROTECTION_UNAVAILABLE |
Unknown key and the failure counter is unavailable | 1 |
| 429 | TOO_MANY_ATTEMPTS |
Too many unknown-key failures from your IP | seconds |
| 401 | INVALID_API_KEY, API_KEY_EXPIRED, API_KEY_INACTIVE, API_KEY_OWNER_INACTIVE |
Key rejected; see API keys | – |
| 401 | CROSS_TENANT_WORKSPACE |
The key’s workspace is missing or deleted (description invalid_api_key); create a key in a live workspace |
– |
| 401 | SIGNATURE_REQUIRED, INVALID_SIGNATURE |
The key requires a signature | – |
| 403 | FORBIDDEN |
Integration SMS key: “integration SMS keys can access only the strict integration send endpoint” | – |
| 403 | FORBIDDEN |
Any other key: “API keys cannot access this endpoint” | – |
Portal sessions: the X-Workspace-ID header
Section titled “Portal sessions: the X-Workspace-ID header”Log in as described in Authentication; the session is the auth_token cookie. Scope a request with:
X-Workspace-ID: 18| Header value | Account owner | Team member |
|---|---|---|
| omitted | Main workspace | Allowed main workspace, else the lowest allowed id |
| ID of a live workspace you may use | Your workspace | A workspace you were granted |
| Not a positive integer | INVALID_WORKSPACE_HEADER (400) |
INVALID_WORKSPACE_HEADER (400) |
| Foreign, deleted or not granted | CROSS_TENANT_WORKSPACE (403) |
CROSS_TENANT_WORKSPACE (403) |
| No workspace access | – | NO_ACCESSIBLE_WORKSPACE (403) — ask the account owner |
| Omitted, no live main workspace | NO_MAIN_WORKSPACE (403) — contact support |
– |
| Workspace check unavailable | WORKSPACE_ACCESS_UNAVAILABLE, Retry-After: 1 (503) |
same |
List your workspaces (portal session only)
Section titled “List your workspaces (portal session only)”GET /user/workspaces returns the live workspaces you may use: main first, then by id. Team members see only granted workspaces (all of them with all workspaces access). API keys cannot call it. GET /user/profile embeds the same list.
curl -b cookies.txt https://restlink23telecom.com/api/v1/user/workspaces[ { "id": 16, "name": "Main", "is_main": true, "account_id": 279, "product_id": 1430, "crypto_enabled": false }, { "id": 18, "name": "test-space", "is_main": false, "color": "#5298D9", "account_id": 279, "product_id": 1406, "crypto_enabled": true }]Fields
Section titled “Fields”| Field | Description |
|---|---|
id |
Workspace ID — use this in X-Workspace-ID |
name |
Label set by your administrator; the account owner can rename it |
is_main |
Whether this is the main workspace |
color |
UI tint; present only when set, never null |
account_id |
Bound SMS gateway account |
product_id |
Bound SMS gateway product |
crypto_enabled |
Whether crypto top-ups are enabled for this workspace |
Errors
Section titled “Errors”| Status | Code | Description | Retry-After |
|---|---|---|---|
| 400 | INVALID_WORKSPACE_HEADER |
X-Workspace-ID must be a positive integer | – |
| 400 | AMBIGUOUS_AUTHENTICATION |
do not combine API key and session cookie | – |
| 401 | CROSS_TENANT_WORKSPACE |
invalid_api_key | – |
| 403 | CROSS_TENANT_WORKSPACE |
workspace is not available | – |
| 403 | NO_ACCESSIBLE_WORKSPACE |
no workspace access; ask the account owner | – |
| 403 | NO_MAIN_WORKSPACE |
workspace not found | – |
| 403 | FORBIDDEN |
API keys cannot access this endpoint | – |
| 403 | FORBIDDEN |
integration SMS keys can access only the strict integration send endpoint | – |
| 429 | RATE_LIMIT_EXCEEDED |
rate limit exceeded; retry after 1 second | 1 |
| 429 | RATE_LIMIT_EXCEEDED |
sending is suspended for this account or team member; contact support | 60 |
| 429 | TOO_MANY_ATTEMPTS |
too many API key authentication failures | seconds |
| 500 | DB_ERROR |
An internal error occurred. Reference: X-Request-ID |
– |
| 500 | INTERNAL_ERROR |
An internal error occurred. Reference: X-Request-ID |
– |
| 503 | API_KEY_VALIDATION_UNAVAILABLE |
An internal error occurred. Reference: X-Request-ID |
1 |
| 503 | AUTH_PROTECTION_UNAVAILABLE |
An internal error occurred. Reference: X-Request-ID |
1 |
| 503 | RATE_LIMIT_UNAVAILABLE |
An internal error occurred. Reference: X-Request-ID |
– |
| 503 | WORKSPACE_ACCESS_UNAVAILABLE |
An internal error occurred. Reference: X-Request-ID |
1 |
Other 401 codes: see Authentication and API keys.
Example: send from a specific workspace (portal session)
Section titled “Example: send from a specific workspace (portal session)”curl -X POST https://restlink23telecom.com/api/v1/sms/send \ -b cookies.txt \ -H "X-CSRF-Token: $CSRF_TOKEN" \ -H "X-Workspace-ID: 18" \ -H "Content-Type: application/json" \ -d '{"to":["+14155551234"],"message":"hi","sender_id":"MyApp"}'$CSRF_TOKEN is the value of the csrf_token cookie; without it the request returns 403 CSRF_FAILED.
Rename a workspace (owner only)
Section titled “Rename a workspace (owner only)”The account owner can rename any of their live workspaces, including the main workspace. Authenticate as in the send example above: the auth_token cookie plus X-CSRF-Token.
curl -X PATCH https://restlink23telecom.com/api/v1/user/workspaces/18 \ -b cookies.txt \ -H "X-CSRF-Token: $CSRF_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"EU traffic"}'{ "status": true }- The path selects the workspace.
X-Workspace-IDis optional; if you send it, it is checked as in the header table above. - Surrounding whitespace is trimmed; the trimmed name must be 1–64 characters. Other body fields are ignored. A body that cannot be decoded returns
400 INVALID_BODYwith the parser’s message. - Team members cannot rename workspaces. A member whose Settings section is hidden, or who lacks the Settings update permission, receives
403 SECTION_ACCESS_DENIEDwith"section": "settings". A member who passes both checks reaches the rename and receives404 NOT_FOUND, because only the owner’s own workspaces match. - API keys cannot call it and get the outcomes in the API-key table above. A signed API-key request can also receive
503 REPLAY_PROTECTION_UNAVAILABLE(Retry-After: 1), because replay protection covers every method exceptGET,HEADandOPTIONS.
Rename errors
Section titled “Rename errors”These outcomes cover ordinary account sessions and API keys.
| Status | Code | Description | Retry-After |
|---|---|---|---|
| 400 | INVALID_WORKSPACE_HEADER |
X-Workspace-ID must be a positive integer | – |
| 400 | AMBIGUOUS_AUTHENTICATION |
do not combine API key and session cookie | – |
| 400 | INVALID_BODY |
invalid workspace id | – |
| 400 | INVALID_BODY |
workspace name cannot be empty | – |
| 400 | INVALID_BODY |
workspace name must be 64 characters or fewer | – |
| 401 | UNAUTHORIZED |
authorization_failed | – |
| 401 | INVALID_TOKEN |
invalid_token | – |
| 401 | TOKEN_EXPIRED |
token_expired | – |
| 401 | INVALID_API_KEY |
invalid_api_key | – |
| 401 | CROSS_TENANT_WORKSPACE |
invalid_api_key | – |
| 403 | CSRF_FAILED |
CSRF token missing or invalid | – |
| 403 | SECTION_ACCESS_DENIED |
This section is not available with your access | – |
| 403 | CROSS_TENANT_WORKSPACE |
workspace is not available | – |
| 403 | NO_ACCESSIBLE_WORKSPACE |
no workspace access; ask the account owner | – |
| 403 | NO_MAIN_WORKSPACE |
workspace not found | – |
| 403 | FORBIDDEN |
API keys cannot access this endpoint | – |
| 403 | FORBIDDEN |
integration SMS keys can access only the strict integration send endpoint | – |
| 404 | NOT_FOUND |
workspace not found | – |
| 429 | RATE_LIMIT_EXCEEDED |
rate limit exceeded; retry after 1 second | 1 |
| 429 | RATE_LIMIT_EXCEEDED |
sending is suspended for this account or team member; contact support | 60 |
| 429 | TOO_MANY_ATTEMPTS |
too many API key authentication failures | seconds |
| 500 | DB_ERROR |
An internal error occurred. Reference: X-Request-ID |
– |
| 500 | PERMISSION_CHECK_FAILED |
An internal error occurred. Reference: X-Request-ID |
– |
| 500 | INTERNAL_ERROR |
An internal error occurred. Reference: X-Request-ID |
– |
| 503 | API_KEY_VALIDATION_UNAVAILABLE |
An internal error occurred. Reference: X-Request-ID |
1 |
| 503 | AUTH_PROTECTION_UNAVAILABLE |
An internal error occurred. Reference: X-Request-ID |
1 |
| 503 | REPLAY_PROTECTION_UNAVAILABLE |
An internal error occurred. Reference: X-Request-ID |
1 |
| 503 | RATE_LIMIT_UNAVAILABLE |
An internal error occurred. Reference: X-Request-ID |
– |
| 503 | SECTION_ACCESS_UNAVAILABLE |
An internal error occurred. Reference: X-Request-ID |
– |
| 503 | WORKSPACE_ACCESS_UNAVAILABLE |
An internal error occurred. Reference: X-Request-ID |
1 |
Other 401 codes: see Authentication and API keys.
Webhooks are per-workspace
Section titled “Webhooks are per-workspace”Webhook URLs, field selections and signing secrets are configured per workspace — each workspace routes its events independently. See webhooks.
Team members and passkeys
Section titled “Team members and passkeys”When several people share one portal account, each person can add their own passkey for passwordless sign-in, instead of sharing the account password.
