Skip to main content

Workspaces (organizations)

A workspace is the tenancy boundary of Orunbase — every project, member, secret, and audit entry belongs to exactly one. The API canonically calls a workspace an organization and serves it at /v1/organizations/*; /v1/workspaces/* is an accepted alias for the same handlers. Accounts are parent organizations whose account-scoped roles cascade to their child workspaces. For the model, see Workspaces.

Endpoints

MethodPathPermissionDescription
POST/v1/organizationsauthenticated actorCreate a workspace; the creator becomes its first member
GET/v1/organizationsauthenticated actorList the workspaces the caller belongs to
GET/v1/organizations/{orgId}organization.readGet one workspace
GET/v1/organizations/{orgId}/workspacesorganization.member.listList an Account's child workspaces
GET/v1/organizations/{orgId}/effective-accessnone (self); organization.member.list (another subject)Effective permissions on the workspace, with via provenance

Workspace references in paths

The {orgId} segment accepts three spellings, all resolved at the edge before routing:

FormExampleStability
org_…org_1f6a3c9eLegacy opaque id — permanent
ws_…ws_a1b2c3d4Immutable public Workspace ID — safe to commit and automate
slugacmeMutable vanity label — can be renamed

An unresolvable ws_/slug reference returns 404 not_found. Requests to /v1/workspaces/* are rewritten to /v1/organizations/* and their JSON responses additionally carry workspaceId next to every orgId — the two surfaces are otherwise identical.

Create a workspace

curl -X POST "https://api.orunbase.com/v1/organizations" \
-H "Authorization: Bearer $ORUN_CLOUD_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 8b6f1c2e-create-acme" \
-d '{ "name": "Acme", "slug": "acme" }'

slug is optional — one is derived from name when omitted.

{
"data": {
"organization": {
"id": "org_1f6a3c9e",
"name": "Acme",
"slug": "acme",
"workspaceRef": "ws_a1b2c3d4",
"accountId": "ws_a1b2c3d4",
"kind": "account",
"isAccountRoot": true,
"createdAt": "2026-07-02T09:15:00.000Z"
},
"membership": { "role": "owner", "joinedAt": "2026-07-02T09:15:00.000Z" }
},
"meta": { "requestId": "req_7a6b5c4d3e2f", "cursor": null }
}

accountId is the owning Account's ws_… id; it equals the workspace's own workspaceRef exactly when the workspace is an Account root.

The Account a new workspace is created under is your own: the earliest-created Account root you hold the owner role on. A workspace someone shared with you is never a candidate, no matter how long ago it was created — so if every workspace you can see belongs to somebody else's account, your next one starts a fresh Account of your own.

Get a workspace by any reference

curl "https://api.orunbase.com/v1/organizations/ws_a1b2c3d4" \
-H "Authorization: Bearer $ORUN_CLOUD_TOKEN"
{
"data": {
"organization": {
"id": "org_1f6a3c9e",
"name": "Acme",
"slug": "acme",
"workspaceRef": "ws_a1b2c3d4",
"accountId": "ws_a1b2c3d4",
"kind": "account",
"isAccountRoot": true,
"createdAt": "2026-07-02T09:15:00.000Z"
}
},
"meta": { "requestId": "req_9c8d7e6f5a4b", "cursor": null }
}

To enumerate the child workspaces under an Account:

curl "https://api.orunbase.com/v1/organizations/ws_a1b2c3d4/workspaces" \
-H "Authorization: Bearer $ORUN_CLOUD_TOKEN"
{
"data": {
"workspaces": [
{ "orgId": "org_44e0b7a1", "workspaceRef": "ws_f9e8d7c6", "name": "Acme Staging" }
]
},
"meta": { "requestId": "req_2b3c4d5e6f7a", "cursor": null }
}

Inspect effective access

GET /v1/organizations/{orgId}/effective-access returns the caller's permitted actions, each carrying via provenance (direct, team, or account_cascade). Optional query parameters: projectId narrows the resource scope; subjectId inspects another subject's access (requires organization.member.list).

import { OrunCloud } from "@saas/sdk";

const client = new OrunCloud({
baseUrl: "https://api.orunbase.com",
auth: { kind: "bearer", token: process.env.ORUN_CLOUD_TOKEN! },
});

// List my workspaces, then check what I can do in the first one.
const { organizations } = await client.organizations.list();
const access = await client.teams.effectiveAccess(organizations[0]!.id);
for (const p of access.permissions) {
if (p.allow) console.log(p.action, "via", p.via?.kind ?? "direct");
}