Projects are addressed by key in paths (a lowercase slug such as web), not by UUID. Workspace users see every project; a client user sees a project only when elevated on it or when their client is its billing or a collaborating client. Agent accounts are members of every project. Tokens may only read projects; every change is browser-session only.
GET /api/w/{slug}/projects
The projects the caller may see. Token: yes, read.
| Query parameter | Type | Meaning |
|---|---|---|
archived |
true |
Include archived projects |
Request
curl https://saq.no/api/w/acme/projects \
-H 'Authorization: Bearer saq_YOUR_TOKEN'
Response 200 { "projects": ProjectSummary[] }
{
"projects": [
{
"id": "01924c3e-6b2a-7d11-9d0e-3a1f4b5c6d7e",
"key": "web",
"name": "Website",
"description": "Public website and login saq.",
"billingClient": {
"id": "01924c3e-0005-7000-8000-000000000001",
"name": "Acme AS"
},
"collaboratingClients": [],
"billingMode": "period",
"memberCount": 4,
"openTickets": 12,
"myRole": "member",
"archived": false
}
]
}
| Field | Type | Meaning |
|---|---|---|
id, key, name, description |
Identity and description | |
billingClient |
{ id, name } or null |
Null means internal work |
collaboratingClients[] |
{ id, name } |
Clients whose users may be elevated and to whom tickets may be billed |
billingMode |
period or on_close |
Default billing mode for tickets on the project |
memberCount |
integer | Project members |
openTickets |
integer | Tickets in an open state |
myRole |
member, manager, or null |
The caller's project role |
archived |
boolean | Archived |
GET /api/w/{slug}/projects/{key}
One project with its members and what the caller may do. Token: yes, read.
curl https://saq.no/api/w/acme/projects/web \
-H 'Authorization: Bearer saq_YOUR_TOKEN'
Response 200 (ProjectDetail): every ProjectSummary field plus:
| Field | Type | Meaning |
|---|---|---|
members[] |
ProjectMember | id, identityId, name, email, kind (workspace or client), clientId, clientName, role (member or manager), categoryId (consultant category override), hasFullLogin |
permissions |
object | edit, members, collaborators, billing (booleans) |
| Error | Status | When |
|---|---|---|
not_found |
404 | Unknown key or not visible |
POST /api/w/{slug}/projects
Create a project. Token: no (browser session only). Owner or Admin.
Body field (CreateProject) |
Type | Required | Validation |
|---|---|---|---|
key |
string | yes | 1–32 lowercase letters, digits, or hyphens; unique; fixed after creation |
name |
string | yes | 1–120 characters |
description |
string | no | At most 2 000 characters |
billingClientId |
uuid or null | no | Null is internal work |
billingMode |
period or on_close |
no | Default period |
collaboratingClientIds |
uuid[] | no | At most 20 |
Response 201 (ProjectDetail).
| Error | Status | details.reason |
|---|---|---|
forbidden |
403 | admin_only |
invalid |
400 | project_key_format, name_length, unknown_client |
conflict |
409 | project_key_exists |
PATCH /api/w/{slug}/projects/{key}
Change a project. Token: no (browser session only). Owner, Admin, or a workspace-user Project manager; billingClientId and billingMode are Owner or Admin only; collaboratingClientIds replaces the set and needs Owner, Admin, or a workspace-user Project manager.
Body (UpdateProject): any of name, description, billingClientId, billingMode, collaboratingClientIds, with the validation above. Response 200 (ProjectDetail).
| Error | Status | details.reason |
|---|---|---|
forbidden |
403 | Not allowed to edit; admin_only for billing fields; manager_only for collaborators |
invalid |
400 | name_length, unknown_client |
not_found |
404 | Unknown key |
DELETE /api/w/{slug}/projects/{key}
Archive a project (projects are never deleted). Token: no (browser session only). Owner or Admin. Response 200 { "ok": true }. Errors: forbidden 403, not_found 404.
PUT /api/w/{slug}/projects/{key}/members
Add a member or change their role. For a client user this is the elevation. Token: no (browser session only). Needs a re-authentication within the last ten minutes. Owner, Admin, or a workspace-user Project manager may add workspace users; a client Project manager may elevate only users of their own client, and that client must be on the project.
Body (ProjectMemberInput): { "identityId": "…", "role": "member" } where role is member or manager. Response 200 (ProjectDetail).
| Error | Status | details.reason |
|---|---|---|
reauth_required |
403 | No recent re-authentication |
forbidden |
403 | manager_only, elevate_own_client |
invalid |
400 | client_not_on_project |
not_found |
404 | unknown_person, or unknown project |
DELETE /api/w/{slug}/projects/{key}/members/{identityId}
Remove a member (for a client user: remove the elevation). Token: no (browser session only). Recent re-authentication required; same roles as adding. Response 200 { "ok": true }. Errors: reauth_required 403, forbidden 403, not_found 404.