Workspaces
Read and rename your workspaces and choose how long their data is kept; with workspace sharing, create shared workspaces, manage members and roles, and invite people by email.
A workspace groups accounts. Every workspace works the same way: it has members, each with one role, a plan, and data retention settings. One of your workspaces is your private workspace (private: true): it is created with your account, only you belong to it, and it is yours alone. It cannot be shared, left, transferred, or deleted, and it is deleted only with your account. A new private workspace is named "My workspace"; you can rename it. Every other workspace is shared and can have other members and invitations. Your recordings, contacts, and other data belong to your account and stay private to it; joining a shared workspace does not share them.
Workspace sharing
Sharing is available only to accounts with workspace sharing turned on; GET /me reports it as sharing_enabled. Without it, an account can list and read its own workspaces, rename its private workspace and change its settings, and list its members, and can still leave a shared workspace, transfer its ownership, and delete one it owns, so it is never stuck in a workspace it can no longer use. Every other sharing capability returns 403 WORKSPACE_SHARING_REQUIRED: creating a workspace, renaming a shared one, listing a shared workspace's members, changing roles, removing other members, and every invitation operation, including listing, accepting, and declining invitations addressed to you. The account making the request needs workspace sharing; accepting an invitation needs it on the invitee's account. Optima staff turn workspace sharing on for an account.
The routes below require an Optima bearer session, use X-API-Version: 1, and return the normal success or error envelope. See the API reference for exact schemas.
Roles and scopes
| Role | Can |
|---|---|
member | See the workspace and its members; leave |
admin | Everything a member can, plus invite people, revoke and resend invitations, change roles, and remove members. Admins cannot grant, change, or remove owner |
owner | Everything an admin can, plus grant and remove owner, rename, change data retention, transfer ownership, and delete the workspace |
A shared workspace always keeps at least one owner: the last owner cannot be demoted, removed, or leave (409 WORKSPACE_OWNER_REQUIRED). Transfer ownership or add another owner first.
Each role corresponds to a permission: workspace:read (member), workspace:members (admin), and workspace:manage (owner). A request needs both the permission in its credential and a role that holds it in the target workspace. An app session carries all three; an OAuth token carries only the scopes it was granted; a personal access token carries none. A missing scope or a lower role returns 403 FORBIDDEN. A workspace you do not belong to returns 404 NOT_FOUND, the same as one that does not exist.
Workspaces
| Operation | Request | Needs |
|---|---|---|
| List your workspaces | GET /workspaces | workspace:read |
| Read a workspace | GET /workspaces/{id} | member |
| Rename your private workspace | PATCH /workspaces/{id} with { "name": "Studio" } | owner |
| Create a workspace | POST /workspaces with { "name": "Acme" } | workspace:manage; workspace sharing |
| Rename a shared workspace | PATCH /workspaces/{id} with { "name": "Acme Labs" } | owner; workspace sharing |
| Delete a shared workspace | DELETE /workspaces/{id} returns 204 | owner |
| Transfer ownership | POST /workspaces/{id}/transfer-ownership with { "user_id": "..." } | owner |
| Leave a shared workspace | POST /workspaces/{id}/leave returns 204 | member |
A workspace has id, name, private (true only for your private workspace), your role, its plan, and created_at. The list returns your private workspace first, then the others by name, as { items, cursor } with every workspace and a null cursor. GET /me includes the same list without plan and created_at.
Names are trimmed and must contain 1–80 characters. Creating a workspace makes you its owner; it is shared, never private. An account can create at most 10 workspaces a day, counting workspaces it has since deleted (429 WORKSPACE_LIMIT_REACHED). Transferring ownership makes the named member an owner and makes you an admin. Deleting a workspace is permanent: it ends every membership, revokes its pending invitations, and its data is erased shortly after the response. A deleted workspace is never restored, and its ID is never reused. Shared workspaces hold no recordings or other data today, since data belongs to accounts, so erasing one removes nothing from its members' accounts. Your private workspace cannot be left, transferred, or deleted, and takes no other members or invitations (409 PRIVATE_WORKSPACE); it is deleted only when your deleted account is erased, 7 days after the account deletion.
curl https://api.getoptima.com/workspaces \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
-H 'X-API-Version: 1' \
-H 'Content-Type: application/json' \
-d '{"name":"Acme"}'{
"success": true,
"data": {
"id": "WORKSPACE_ID",
"name": "Acme",
"private": false,
"role": "owner",
"plan": "basic",
"created_at": "2026-09-28T12:00:00.000000+00:00"
}
}Data retention
A workspace's settings say how long Optima keeps its data. Each kind of data is a retention bucket that is either on, keeping data for a number of days, or off:
| Bucket | Data | Default | Off means |
|---|---|---|---|
audio | Recordings | On, 7 days | Recordings are deleted once they are transcribed |
transcripts | Transcripts | On, 30 days | Transcripts are deleted once insights are made from them |
insights | Memories and suggestions | On, 90 days | Insights are not kept |
voice | Voice recognition (speaker voice profiles) | On, 365 days | Voice profiles are not kept. Contacts are never deleted by retention |
| Operation | Request | Needs |
|---|---|---|
| Read settings | GET /workspaces/{id}/settings | member; workspace sharing for a shared workspace |
| Change settings | PATCH /workspaces/{id}/settings | owner; workspace sharing for a shared workspace |
Reading needs workspace:read and changing needs workspace:manage, like the other workspace operations. Your private workspace's settings need no workspace sharing. The response lists every bucket, in the order above, with enabled, days (how long data is kept while the bucket is on), max_days (the longest your plan allows), default_enabled and default_days, and updated_at and updated_by (when and by which account it last changed; both null until the bucket is first changed). Buckets can be added later; show an unrecognized bucket generically.
A change names only the buckets and fields it changes; the rest keep their values. Durations are whole days from 1 to the bucket's max_days. A longer duration returns 422 RETENTION_LIMIT_EXCEEDED, whose details name the field and the limit. Turning a bucket off keeps its days for when it is turned back on. If your plan's limit drops below a chosen duration, days reports the limit and data is kept for that long.
Shortening a duration or turning a bucket off also applies to data you already have: at the next cleanup, data older than the new duration, or all data in a bucket that is off, is deleted and cannot be recovered. Ask for confirmation before sending such a change. How removed data appears in the API, with expired states and 410 CONTENT_EXPIRED, is described in Removed content.
curl -X PATCH https://api.getoptima.com/workspaces/WORKSPACE_ID/settings \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
-H 'X-API-Version: 1' \
-H 'Content-Type: application/json' \
-d '{"retention":{"audio":{"days":30},"voice":{"enabled":false}}}'{
"success": true,
"data": {
"workspace_id": "WORKSPACE_ID",
"retention": [
{
"bucket": "audio",
"enabled": true,
"days": 30,
"max_days": 365,
"default_enabled": true,
"default_days": 7,
"updated_at": "2026-09-29T12:00:00.000000+00:00",
"updated_by": "ACCOUNT_ID"
},
{
"bucket": "transcripts",
"enabled": true,
"days": 30,
"max_days": 365,
"default_enabled": true,
"default_days": 30,
"updated_at": null,
"updated_by": null
},
{
"bucket": "insights",
"enabled": true,
"days": 90,
"max_days": 365,
"default_enabled": true,
"default_days": 90,
"updated_at": null,
"updated_by": null
},
{
"bucket": "voice",
"enabled": false,
"days": 365,
"max_days": 365,
"default_enabled": true,
"default_days": 365,
"updated_at": "2026-09-29T12:00:00.000000+00:00",
"updated_by": "ACCOUNT_ID"
}
]
}
}A change emits workspace.updated to each current member, naming the changed buckets.
Members
| Operation | Request | Needs |
|---|---|---|
| List members | GET /workspaces/{id}/members | member; workspace sharing for a shared workspace |
| Change a role | PATCH /workspaces/{id}/members/{user_id} with { "role": "admin" } | admin, or owner to grant or change owner; workspace sharing |
| Remove a member | DELETE /workspaces/{id}/members/{user_id} returns 204 | admin, or owner to remove an owner; workspace sharing. Removing yourself needs only member |
A member has user_id, email, name, role, and joined_at (when the current membership started). Members are listed with owners first, then admins, then members, as { items, cursor } with every current member and a null cursor. Removing yourself is the same as leaving and needs the same workspace:read scope. Someone who left or was removed can be invited again and joins as a new member.
Invitations
Admins and owners of a shared workspace invite people by email; a private workspace takes no invitations (409 PRIVATE_WORKSPACE). The invitee receives a link and joins after signing in with that email address. Every invitation operation needs workspace sharing on the account making the request (403 WORKSPACE_SHARING_REQUIRED).
| Operation | Request | Needs |
|---|---|---|
| Invite | POST /workspaces/{id}/invites with { "email": "...", "role": "member" } | admin |
| List pending invitations | GET /workspaces/{id}/invites | admin |
| Revoke | DELETE /workspaces/{id}/invites/{invite_id} returns 204 | admin |
| Resend | POST /workspaces/{id}/invites/{invite_id}/resend | admin |
| List invitations for you | GET /workspace-invites | workspace:read |
| Accept from a link | POST /workspace-invites/accept with { "token": "..." } | workspace:read |
| Accept from your list | POST /workspace-invites/{id}/accept | workspace:read |
| Decline | POST /workspace-invites/{id}/decline returns 204 | workspace:read |
An invitation offers member (the default) or admin. To make someone an owner, invite them and then change their role. Emails are trimmed and compared without regard to case. An email can have one pending invitation per workspace (409 INVITE_PENDING); an email that already belongs to a member cannot be invited (409 ALREADY_MEMBER).
Invitations are limited so a workspace cannot be used to send mass email. Each limit returns 429 INVITE_LIMIT_REACHED:
| Limit | Value |
|---|---|
| Invitations you create, across all workspaces | 20 an hour |
| Invitations created in one workspace, by all its admins | 50 a day |
| Pending invitations one workspace holds, expired ones included | 100 |
The hourly and daily limits count every invitation created, including revoked ones, so revoking and inviting again does not reset them. Revoke pending invitations you no longer need to make room under the pending limit.
curl https://api.getoptima.com/workspaces/WORKSPACE_ID/invites \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
-H 'X-API-Version: 1' \
-H 'Content-Type: application/json' \
-d '{"email":"sam@example.com","role":"member"}'{
"success": true,
"data": {
"id": "INVITE_ID",
"workspace_id": "WORKSPACE_ID",
"email": "sam@example.com",
"role": "member",
"invited_by": "ACCOUNT_ID",
"created_at": "2026-09-28T12:00:00.000000+00:00",
"sent_at": "2026-09-28T12:00:00.000000+00:00",
"expires_at": "2026-10-05T12:00:00.000000+00:00"
}
}Optima emails the invitee the workspace name, who invited them, the offered role, the expiry, and a link to the Optima web app, https://getoptima.com/invites/accept#token=TOKEN. Long workspace and inviter names are shortened in the email. The token is in the URL fragment, so it is not sent to servers or in referrers. Optima's policy is that invitation emails carry no tracking: the email adds no tracking pixel or redirect, and click and open tracking are turned off for Optima's sending domain at its email provider, so the link goes straight to the web app. Optima stores only a hash of the token and never returns it through the API. The email is queued with the invitation and sent shortly after; the response does not report delivery. If the invitation is revoked, resent, or answered, or its workspace is deleted, before the email goes out, that email is not sent. If the invitee does not receive it, resend the invitation.
An invitation expires 7 days after it is sent. Resending sends a new link with a new 7-day expiry, and the previous link stops working; an invitation can be resent at most once a minute (429 TOO_MANY_REQUESTS). The pending list includes expired invitations so you can resend or revoke them.
Accept an invitation
The invitee's app signs them in, then calls POST /workspace-invites/accept with the token from the link, or lists GET /workspace-invites and accepts one by ID. The signed-in account's confirmed email must match the invited email:
| Response | Meaning |
|---|---|
| 200 with the workspace | Joined with the offered role. Accepting again while still a member returns the workspace |
403 INVITE_EMAIL_MISMATCH | The link is for a different email address; sign in with the invited email |
403 WORKSPACE_SHARING_REQUIRED | Workspace sharing is not turned on for the signed-in account |
404 NOT_FOUND | The invitation was revoked, declined, replaced by a resend, or already used, or its workspace was deleted |
410 INVITE_EXPIRED | Ask for a new invitation |
GET /workspace-invites lists only unexpired pending invitations addressed to your account's email, each with its id, role, workspace (id and name), invited_by (name and email), created_at, and expires_at. An existing member who accepts keeps their current role. An invitation to an email whose account is deleted stays pending: the invitee signs in, restores the account, and then accepts it. Deleting an account revokes the pending invitations to the shared workspaces it is alone in.
Events
Joining or leaving a shared workspace, a role change there, or renaming any workspace, your private workspace included, emits account.updated with workspaces in changed_fields to each affected member. Turning workspace sharing on or off emits account.updated with sharing_enabled. Renaming a workspace also emits workspace.updated to each current member with name in changed_fields, and changing data retention emits workspace.updated with the changed buckets as retention.<bucket>. Deleting a shared workspace also emits workspace.deleted to each member it removes. Invitations emit no events; accepting one emits the new member's account.updated.