workspaces
The methods of pd.workspaces. Inputs and outputs are described in Conventions; every method can also fail as described in Errors.
Methods
pd.workspaces.list(init?)
List the workspaces the caller belongs to
GET /api/workspaces
Newest first, each with the caller's own memberId. Needs a signed-in user, or an API key created by one; no permission is checked since the list only holds workspaces the user is a member of.
Input
| Name | In | Type | Required | Description |
|---|---|---|---|---|
init |
init | { signal?: AbortSignal; timeoutMs?: number } |
no | The second argument: signal aborts the call, timeoutMs overrides the client's per-attempt timeout. |
Returns 200 OK
| Field | Type | Required | Description |
|---|---|---|---|
workspaces |
object[] |
yes | The workspaces the caller is a member of, newest first. |
workspaces[].id |
string (uuid) |
yes | Id of the workspace, used in every /api/workspaces/{workspaceId} route. |
workspaces[].name |
string |
yes | Display name of the workspace. |
workspaces[].slug |
string |
yes | URL-safe identifier, unique across all workspaces. |
workspaces[].iconUrl |
string | null |
yes | Address of the workspace's icon, which the first website set up in it provides; null until then. |
workspaces[].memberId |
string (uuid) |
yes | Id of the caller's membership in this workspace (memberId in GET .../members). |
workspaces[].createdAt |
string (date-time) |
yes | When the workspace was created, as an ISO 8601 timestamp. |
Errors 401 missing or invalid key, 403 the key's roles lack the permission, 404 not found, 429 rate limited or out of quota. See Errors.
Example
import { PrismDraft } from "@prism-draft/sdk";
const pd = new PrismDraft({ apiKey: process.env.PRISMDRAFT_API_KEY! });
const result = await pd.workspaces.list();
console.log(result);
pd.workspaces.create(input, init?)
Create a workspace
POST /api/workspaces
Any signed-in user may create a workspace; no permission is needed. In one transaction it provisions the six system roles (Owner, Admin, Editor, Writer, SEO Manager, Viewer), makes the caller its Owner, creates an empty credit wallet and applies the free plan's quotas. The caller's welcome credits are granted with it, once per user and only when their email address is verified. Records workspace.created (and credits.granted) audit entries and answers 201.
Input
| Name | In | Type | Required | Description |
|---|---|---|---|---|
body |
body | object |
yes | |
body.name |
body | string |
yes | Display name of the workspace, 1 to 120 characters. |
body.slug |
body | string |
no | Wanted URL slug, 1 to 120 characters, normalised to lowercase words joined by hyphens. Taken from name when omitted. Slugs are unique across all workspaces: when it is taken, a short random suffix is appended rather than the call failing. Cannot be changed afterwards. |
init |
init | { signal?: AbortSignal; timeoutMs?: number } |
no | The second argument: signal aborts the call, timeoutMs overrides the client's per-attempt timeout. |
Returns 201 Created
| Field | Type | Required | Description |
|---|---|---|---|
workspace |
object |
yes | The workspace as created, with the ids of what was provisioned alongside it. |
workspace.id |
string (uuid) |
yes | Id of the new workspace. |
workspace.name |
string |
yes | Display name of the workspace, as sent. |
workspace.slug |
string |
yes | The slug the workspace got: the one sent or the one derived from name, with a short random suffix when that was taken. |
workspace.ownerMemberId |
string (uuid) |
yes | The creator's membership, holding the Owner role. |
workspace.ownerRoleId |
string (uuid) |
yes | Id of the workspace's Owner role, which the creator's membership holds. |
workspace.walletId |
string (uuid) |
yes | Id of the workspace's credit wallet, created empty and topped up with the welcome credits when the creator qualifies for them. |
Errors 400 invalid input, 401 missing or invalid key, 403 the key's roles lack the permission, 404 not found, 429 rate limited or out of quota. See Errors.
Example
import { PrismDraft } from "@prism-draft/sdk";
const pd = new PrismDraft({ apiKey: process.env.PRISMDRAFT_API_KEY! });
const result = await pd.workspaces.create({
body: {
name: "example",
},
});
console.log(result);
pd.workspaces.get(input, init?)
Read a workspace, the caller's permissions in it, and the server's features
GET /api/workspaces/{workspaceId}
Needs workspace.read. permissions lists what the caller can do here, so a client can decide what to offer, and features lists the server's enabled feature flags: a client must not offer a capability whose flag is absent. A caller who is not a member gets 404 RESOURCE_NOT_FOUND.
Input
| Name | In | Type | Required | Description |
|---|---|---|---|---|
workspaceId |
path | string (uuid) |
yes | Id of the workspace the request works in. The caller must be a member of it, or hold an API key issued by it. For anyone else, an unknown id and an id they do not belong to both answer 404 RESOURCE_NOT_FOUND, so membership cannot be probed. |
init |
init | { signal?: AbortSignal; timeoutMs?: number } |
no | The second argument: signal aborts the call, timeoutMs overrides the client's per-attempt timeout. |
Returns 200 OK
| Field | Type | Required | Description |
|---|---|---|---|
workspace |
object |
yes | The workspace record: id, name, slug, free-form settings, icon and timestamps. The caller's permissions and the server's feature flags are alongside it. |
workspace.id |
string (uuid) |
yes | Id of the workspace. |
workspace.name |
string |
yes | Display name of the workspace, 1 to 120 characters. |
workspace.slug |
string |
yes | URL-safe identifier, unique across all workspaces; fixed at creation. |
workspace.settings |
Record<string, unknown> |
yes | Free-form object stored as sent through PATCH /api/workspaces/{workspaceId}; {} until then. The server does not read any key of it. |
workspace.iconUrl |
string | null |
yes | Address of the workspace's icon, which the first website set up in it provides; null until then. |
workspace.rbacVersion |
integer |
yes | Version of the platform's default roles and permissions the workspace was provisioned with. Workspaces created before the platform revised them keep their older number until their system roles are migrated. Read-only: no request sets it. |
workspace.createdAt |
string (date-time) |
yes | When the workspace was created, as an ISO 8601 timestamp. |
workspace.updatedAt |
string (date-time) |
yes | When the workspace row last changed, as an ISO 8601 timestamp. |
permissions |
string[] |
yes | The caller's effective permission keys in this workspace (resource.action, as listed by GET /api/permissions), sorted: the union of their roles'. For an API key, those of its role. |
features |
string[] |
yes | The feature flags switched on in the server, sorted; a client must not claim others. |
Errors 400 invalid input, 401 missing or invalid key, 403 the key's roles lack the permission, 404 not found, 429 rate limited or out of quota. See Errors.
Example
import { PrismDraft } from "@prism-draft/sdk";
const pd = new PrismDraft({ apiKey: process.env.PRISMDRAFT_API_KEY! });
const result = await pd.workspaces.get({
workspaceId: "3f0c5a1e-8b2d-4c6e-9a7f-1d2e3f4a5b6c",
});
console.log(result);
pd.workspaces.update(input, init?)
Update a workspace
PATCH /api/workspaces/{workspaceId}
Needs workspace.update. Only the fields sent change; the slug cannot be changed. settings replaces the stored object as a whole rather than merging. Records a workspace.updated audit entry listing the fields sent.
Input
| Name | In | Type | Required | Description |
|---|---|---|---|---|
workspaceId |
path | string (uuid) |
yes | Id of the workspace the request works in. The caller must be a member of it, or hold an API key issued by it. For anyone else, an unknown id and an id they do not belong to both answer 404 RESOURCE_NOT_FOUND, so membership cannot be probed. |
body |
body | object |
no | |
body.name |
body | string |
no | New display name, 1 to 120 characters. Unchanged when omitted. |
body.settings |
body | Record<string, unknown> |
no | Free-form settings object. It replaces the stored object as a whole, so send every key you want to keep: unlike a project's settings, keys are not merged. The server stores it as sent and does not read any key from it. Unchanged when omitted. |
init |
init | { signal?: AbortSignal; timeoutMs?: number } |
no | The second argument: signal aborts the call, timeoutMs overrides the client's per-attempt timeout. |
Returns 200 OK
| Field | Type | Required | Description |
|---|---|---|---|
workspace |
object |
yes | The workspace record as stored once the update is applied, so it shows the new name and settings and the new updatedAt. |
workspace.id |
string (uuid) |
yes | Id of the workspace. |
workspace.name |
string |
yes | Display name of the workspace, 1 to 120 characters. |
workspace.slug |
string |
yes | URL-safe identifier, unique across all workspaces; fixed at creation. |
workspace.settings |
Record<string, unknown> |
yes | Free-form object stored as sent through PATCH /api/workspaces/{workspaceId}; {} until then. The server does not read any key of it. |
workspace.iconUrl |
string | null |
yes | Address of the workspace's icon, which the first website set up in it provides; null until then. |
workspace.rbacVersion |
integer |
yes | Version of the platform's default roles and permissions the workspace was provisioned with. Workspaces created before the platform revised them keep their older number until their system roles are migrated. Read-only: no request sets it. |
workspace.createdAt |
string (date-time) |
yes | When the workspace was created, as an ISO 8601 timestamp. |
workspace.updatedAt |
string (date-time) |
yes | When the workspace row last changed, as an ISO 8601 timestamp. |
Errors 400 invalid input, 401 missing or invalid key, 403 the key's roles lack the permission, 404 not found, 429 rate limited or out of quota. See Errors.
Example
import { PrismDraft } from "@prism-draft/sdk";
const pd = new PrismDraft({ apiKey: process.env.PRISMDRAFT_API_KEY! });
const result = await pd.workspaces.update({
workspaceId: "3f0c5a1e-8b2d-4c6e-9a7f-1d2e3f4a5b6c",
});
console.log(result);
pd.workspaces.listMembers(input, init?)
List a workspace's members
GET /api/workspaces/{workspaceId}/members
Needs members.read. Members have no status: a person either holds a membership here or does not. Invited addresses that have not accepted yet are not members; they are listed by GET /api/workspaces/{workspaceId}/invitations.
Input
| Name | In | Type | Required | Description |
|---|---|---|---|---|
workspaceId |
path | string (uuid) |
yes | Id of the workspace the request works in. The caller must be a member of it, or hold an API key issued by it. For anyone else, an unknown id and an id they do not belong to both answer 404 RESOURCE_NOT_FOUND, so membership cannot be probed. |
init |
init | { signal?: AbortSignal; timeoutMs?: number } |
no | The second argument: signal aborts the call, timeoutMs overrides the client's per-attempt timeout. |
Returns 200 OK
| Field | Type | Required | Description |
|---|---|---|---|
members |
object[] |
yes | Every member of the workspace, in no guaranteed order. |
members[].memberId |
string (uuid) |
yes | Id of the membership, used in /members/{memberId} routes and as workspaceMemberId when adding the member to a project. Not the user id. |
members[].userId |
string |
yes | Id of the member's user account. |
members[].name |
string |
yes | The member's display name. |
members[].email |
string |
yes | The member's email address. |
members[].roles |
object[] |
yes | The roles the member holds; their permissions are the union of the roles' permissions. Empty when every role was taken off. |
members[].roles[].id |
string (uuid) |
yes | Id of the role (GET .../roles). |
members[].roles[].name |
string |
yes | Name of the role. |
members[].createdAt |
string (date-time) |
yes | When the member joined the workspace, as an ISO 8601 timestamp. |
Errors 400 invalid input, 401 missing or invalid key, 403 the key's roles lack the permission, 404 not found, 429 rate limited or out of quota. See Errors.
Example
import { PrismDraft } from "@prism-draft/sdk";
const pd = new PrismDraft({ apiKey: process.env.PRISMDRAFT_API_KEY! });
const result = await pd.workspaces.listMembers({
workspaceId: "3f0c5a1e-8b2d-4c6e-9a7f-1d2e3f4a5b6c",
});
console.log(result);
pd.workspaces.addMember(input, init?)
Add a member, or invite an address by email
POST /api/workspaces/{workspaceId}/members
Needs members.invite. An address with an account joins at once, is told so by email, and the call answers { memberId, member }; one already in the workspace answers 409 CONFLICT. Any other address is invited: it gets an email with a link valid for seven days, and the call answers { invitation }. The roles given (the default role when none is sent) may only hold permissions the caller holds: 403 FORBIDDEN, with those permissions in details.permissions. Inviting an address that already has an open invitation renews it (new link, roles and expiry; the old link stops working), and answers 429 RATE_LIMITED with Retry-After within a minute of the last send. Both paths count against the users quota (429 QUOTA_EXCEEDED at the limit), and an open invitation holds a seat until it is accepted, revoked or expires. Either email is queued with the change and sent in the background, in the member's own language for an account and in locale for an invitation. Records a members.invited audit entry and answers 201.
Input
| Name | In | Type | Required | Description |
|---|---|---|---|---|
workspaceId |
path | string (uuid) |
yes | Id of the workspace the request works in. The caller must be a member of it, or hold an API key issued by it. For anyone else, an unknown id and an id they do not belong to both answer 404 RESOURCE_NOT_FOUND, so membership cannot be probed. |
body |
body | object |
yes | |
body.email |
body | string (email) |
yes | Email address to add. Matched case-insensitively against existing accounts: one with an account joins at once, any other is invited by email. |
body.roleIds |
body | string (uuid)[] |
no | Ids of the roles to give (GET .../roles); every one must belong to this workspace, else 400 INVALID_REQUEST, and none may carry a permission the caller lacks, else 403 FORBIDDEN. Empty or omitted: the workspace's default role (isDefault) is given. For an invitation the roles are applied when it is accepted. Default: []. |
body.locale |
body | "en" | "fr" |
no | Language of the invitation email, en or fr. Only used when the address has no account. Omitted: the first language of the request's Accept-Language header when it is French or English, else en. |
init |
init | { signal?: AbortSignal; timeoutMs?: number } |
no | The second argument: signal aborts the call, timeoutMs overrides the client's per-attempt timeout. |
Returns 201 Created
| Field | Type | Required | Description |
|---|---|---|---|
(response) |
object<variant 1> | object<variant 2> |
yes | |
<variant 1>.memberId |
string (uuid) |
yes | Kept for clients written before invitations existed. |
<variant 1>.member |
object |
yes | The new member, as GET .../members lists them. |
<variant 1>.member.memberId |
string (uuid) |
yes | Id of the membership, used in /members/{memberId} routes and as workspaceMemberId when adding the member to a project. Not the user id. |
<variant 1>.member.userId |
string |
yes | Id of the member's user account. |
<variant 1>.member.name |
string |
yes | The member's display name. |
<variant 1>.member.email |
string |
yes | The member's email address. |
<variant 1>.member.roles |
object[] |
yes | The roles the member holds; their permissions are the union of the roles' permissions. Empty when every role was taken off. |
<variant 1>.member.roles[].id |
string (uuid) |
yes | Id of the role (GET .../roles). |
<variant 1>.member.roles[].name |
string |
yes | Name of the role. |
<variant 1>.member.createdAt |
string (date-time) |
yes | When the member joined the workspace, as an ISO 8601 timestamp. |
<variant 2>.invitation |
object |
yes | The invitation that was created, or renewed when the address already had an open one. |
<variant 2>.invitation.id |
string (uuid) |
yes | Id of the invitation, used to resend or revoke it. |
<variant 2>.invitation.email |
string |
yes | The invited address, lowercased. The invitation can only be accepted by it. |
<variant 2>.invitation.roleIds |
string (uuid)[] |
yes | The roles the invitee receives on accepting; any that no longer exist by then are skipped, and when none is left the workspace's default roles apply. |
<variant 2>.invitation.invitedBy |
object | null |
yes | The inviter; null when it was an API key without a creator, or the user is gone. |
<variant 2>.invitation.invitedBy.id |
string |
yes | Id of the inviting user. |
<variant 2>.invitation.invitedBy.name |
string |
yes | Display name of the inviting user. |
<variant 2>.invitation.expiresAt |
string (date-time) |
yes | When the emailed link stops working, as an ISO 8601 timestamp: seven days after the last send. Past it the invitation is expired but stays listed, so it can be resent. |
<variant 2>.invitation.createdAt |
string (date-time) |
yes | When the invitation was first created, as an ISO 8601 timestamp. |
<variant 2>.invitation.lastSentAt |
string (date-time) |
yes | When the link was last emailed, as an ISO 8601 timestamp; resending is refused for a minute after. |
Errors 400 invalid input, 401 missing or invalid key, 403 the key's roles lack the permission, 404 not found, 429 rate limited or out of quota. See Errors.
Example
import { PrismDraft } from "@prism-draft/sdk";
const pd = new PrismDraft({ apiKey: process.env.PRISMDRAFT_API_KEY! });
const result = await pd.workspaces.addMember({
workspaceId: "3f0c5a1e-8b2d-4c6e-9a7f-1d2e3f4a5b6c",
body: {
email: "ada@example.com",
},
});
console.log(result);
pd.workspaces.setMemberRoles(input, init?)
Replace a member's roles
PATCH /api/workspaces/{workspaceId}/members/{memberId}
Needs roles.assign. Replaces all of the member's roles with the ones sent, in effect immediately. 403 FORBIDDEN when a role sent, or a role the member holds now, carries a permission the caller lacks (details.permissions lists them), so nobody can raise a member above themselves or demote someone more powerful than themselves. 403 FORBIDDEN also when it would take the Owner role from the workspace's last owner. 404 RESOURCE_NOT_FOUND when the member is not in the workspace. Records a members.roles_updated audit entry.
Input
| Name | In | Type | Required | Description |
|---|---|---|---|---|
workspaceId |
path | string (uuid) |
yes | Id of the workspace the request works in. The caller must be a member of it, or hold an API key issued by it. For anyone else, an unknown id and an id they do not belong to both answer 404 RESOURCE_NOT_FOUND, so membership cannot be probed. |
memberId |
path | string (uuid) |
yes | Id of a membership of this workspace, as memberId in GET .../members (not the user id). A membership of another workspace answers 404 RESOURCE_NOT_FOUND. |
body |
body | object |
yes | |
body.roleIds |
body | string (uuid)[] |
yes | Ids of the roles the member should hold from now on; every one must belong to this workspace, else 400 INVALID_REQUEST, and none may carry a permission the caller lacks, else 403 FORBIDDEN. They replace the current roles entirely. An empty array leaves the member with no role and so no permissions; no default role is applied. |
init |
init | { signal?: AbortSignal; timeoutMs?: number } |
no | The second argument: signal aborts the call, timeoutMs overrides the client's per-attempt timeout. |
Returns 200 OK
| Field | Type | Required | Description |
|---|---|---|---|
ok |
true |
yes | Always true: the change was made. A failure answers an error. |
Errors 400 invalid input, 401 missing or invalid key, 403 the key's roles lack the permission, 404 not found, 429 rate limited or out of quota. See Errors.
Example
import { PrismDraft } from "@prism-draft/sdk";
const pd = new PrismDraft({ apiKey: process.env.PRISMDRAFT_API_KEY! });
const result = await pd.workspaces.setMemberRoles({
workspaceId: "3f0c5a1e-8b2d-4c6e-9a7f-1d2e3f4a5b6c",
memberId: "3f0c5a1e-8b2d-4c6e-9a7f-1d2e3f4a5b6c",
body: {
roleIds: ["3f0c5a1e-8b2d-4c6e-9a7f-1d2e3f4a5b6c"],
},
});
console.log(result);
pd.workspaces.removeMember(input, init?)
Remove a member from a workspace
DELETE /api/workspaces/{workspaceId}/members/{memberId}
Needs members.remove. Deletes the membership, and with it the member's roles and project memberships; their user account is untouched. 403 FORBIDDEN when the member holds a permission the caller lacks (details.permissions lists them), so nobody can remove someone more powerful than themselves; removing yourself is always allowed. 403 FORBIDDEN also for the workspace's last owner: give someone else the Owner role first. 404 RESOURCE_NOT_FOUND when the member is not in the workspace. Records a members.removed audit entry.
Input
| Name | In | Type | Required | Description |
|---|---|---|---|---|
workspaceId |
path | string (uuid) |
yes | Id of the workspace the request works in. The caller must be a member of it, or hold an API key issued by it. For anyone else, an unknown id and an id they do not belong to both answer 404 RESOURCE_NOT_FOUND, so membership cannot be probed. |
memberId |
path | string (uuid) |
yes | Id of a membership of this workspace, as memberId in GET .../members (not the user id). A membership of another workspace answers 404 RESOURCE_NOT_FOUND. |
init |
init | { signal?: AbortSignal; timeoutMs?: number } |
no | The second argument: signal aborts the call, timeoutMs overrides the client's per-attempt timeout. |
Returns 200 OK
| Field | Type | Required | Description |
|---|---|---|---|
ok |
true |
yes | Always true: the change was made. A failure answers an error. |
Errors 400 invalid input, 401 missing or invalid key, 403 the key's roles lack the permission, 404 not found, 429 rate limited or out of quota. See Errors.
Example
import { PrismDraft } from "@prism-draft/sdk";
const pd = new PrismDraft({ apiKey: process.env.PRISMDRAFT_API_KEY! });
const result = await pd.workspaces.removeMember({
workspaceId: "3f0c5a1e-8b2d-4c6e-9a7f-1d2e3f4a5b6c",
memberId: "3f0c5a1e-8b2d-4c6e-9a7f-1d2e3f4a5b6c",
});
console.log(result);