Members & identifiers
GET /workspace/members and PUT/DELETE /workspace/members/{user_id}/identities — list workspace members and link your CRM or telephony identifiers to them.
For integrations that know people by their own ids
A CRM knows a manager as user 12345; a PBX knows them as extension 101. Link those
identifiers to workspace members once, and every upload can name the author as
X-Author: external:hubspot:12345 — no mapping table on your side. See
Assigning an author.
Owners only
These endpoints need a key created with Allow managing members, and only a workspace
owner can create such a key — the member list contains emails, and linking identifiers
decides who receives whose calls. Other keys get 403 members_scope_required. See
Authentication → Key permissions.
Workspace owners can also manage the same identifiers in the app: Settings → Members → External identifiers next to each member.
List members
GET /workspace/members — active members of the key's workspace with everything linked to
them. Use it to build a "CRM manager → workspace member" picker in your settings screen and to
see what is already configured.
curl https://app.specala.ai/api/v1/developer/workspace/members \
-H "Authorization: Bearer sk_live_your_key_here"{
"items": [
{
"id": "9c2d4e6f-1a2b-4c3d-8e9f-0a1b2c3d4e5f",
"email": "alex@company.com",
"name": "Alex Morgan",
"role": "user",
"identities": [
{ "provider": "hubspot", "external_id": "12345" },
{ "provider": "aircall", "external_id": "101" }
]
}
],
"total": 1
}| Field | Type | Description |
|---|---|---|
items[].id | UUID | Member id — also valid as X-Author: user:<id> and as {user_id} below. |
items[].email | string | The email they signed up with — also valid as X-Author: email:<address>. |
items[].name | string | null | Display name. |
items[].role | string | owner or user. |
items[].identities[] | array | Linked identifiers as { provider, external_id }. Empty if none. |
Only active members are returned; pending invitations are not. The list is not paginated (workspaces are small) and is capped at 1000 members.
Link an identifier
PUT /workspace/members/{user_id}/identities — link one identifier to a member. Idempotent:
linking the same pair again returns 200 instead of 201.
curl -X PUT https://app.specala.ai/api/v1/developer/workspace/members/9c2d4e6f-1a2b-4c3d-8e9f-0a1b2c3d4e5f/identities \
-H "Authorization: Bearer sk_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"provider": "hubspot", "external_id": "12345"}'import httpx
resp = httpx.put(
"https://app.specala.ai/api/v1/developer/workspace/members/9c2d4e6f-1a2b-4c3d-8e9f-0a1b2c3d4e5f/identities",
headers={"Authorization": "Bearer sk_live_your_key_here"},
json={"provider": "hubspot", "external_id": "12345"},
)
resp.raise_for_status() # 201 created, 200 already linkedconst res = await fetch(
"https://app.specala.ai/api/v1/developer/workspace/members/9c2d4e6f-1a2b-4c3d-8e9f-0a1b2c3d4e5f/identities",
{
method: "PUT",
headers: {
Authorization: "Bearer sk_live_your_key_here",
"Content-Type": "application/json",
},
body: JSON.stringify({ provider: "hubspot", external_id: "12345" }),
},
);
if (!res.ok) throw new Error(`HTTP ${res.status}`);| Field | Rules |
|---|---|
provider | Your label for the external system: 1–32 characters, a-z, 0-9, _, -. Lowercased on save. There is no fixed list — hubspot, salesforce, pipedrive, aircall, ringcentral are just conventions. |
external_id | The person's identifier in that system: 1–128 printable ASCII characters, matched exactly (case-sensitive). |
One member can have any number of identifiers — a CRM id and two extensions, say. But one
provider + external_id pair points to exactly one member per workspace: linking it to
someone else returns 409 identity_conflict until you unlink it first.
Response: 201 (created) or 200 (already linked) with { "provider", "external_id" }.
Unlink an identifier
DELETE /workspace/members/{user_id}/identities/{provider}/{external_id} — 204 on success,
404 if nothing was linked. URL-encode external_id if it contains / or :.
curl -X DELETE https://app.specala.ai/api/v1/developer/workspace/members/9c2d4e6f-1a2b-4c3d-8e9f-0a1b2c3d4e5f/identities/hubspot/12345 \
-H "Authorization: Bearer sk_live_your_key_here"Lifecycle
- When a member is removed from the workspace, their identifiers are deleted with them.
Uploads naming them then fail with
422 author_not_member— re-link after they rejoin. - Identifiers live inside a workspace: the same
hubspot:12345in another workspace is a different person.
Errors
| Status | Code | When |
|---|---|---|
403 | members_scope_required | Key has no member permission. |
403 | api_key_required | Called with an OAuth token instead of an API key. |
404 | member_not_found | user_id is not an active member of the key's workspace. |
404 | identity_not_found | Nothing to unlink. |
409 | identity_conflict | The pair is linked to another member. |
422 | invalid_identity | provider or external_id does not match the rules above. |