Skip to content
docivault

API reference

docivault API reference

Which endpoints does the docivault API have?

43 endpoints across 7 areas: documents, clients and contacts, categories, requests, activity, dashboard, client portal. Each entry lists the method and path, who may call it, its parameters and body, and its responses. The same data is available as an OpenAPI 3.1 document.

Basics

How requests are authenticated and answered

Send a personal API token as `Authorization: Bearer <token>`. A token acts as the person who created it and never reaches further than that person can in the app.

Requests and responses are JSON unless a row says otherwise. Errors are returned as { "error": "message" } with a standard status code.

Every list on this page that returns many rows is paged; the page parameters are listed per endpoint. Responses for signed-in data are never cached.

Documents

GET

/api/files

List one client's documents in one space (keyset paged); used by the portal and by pickers. Org Documents tab uses /api/files/workspace instead.

Authentication
Signed-in session (team member or client contact)
Who can call it
admin, member, portal contact

Query parameters

NameTypeRequiredDetails
client_iduuidYes
spaceenumYesone of private, shared, client
archivedbool-stringNodefault false
category_iduuid|'__unassigned__'No
sortenumNoone of newest, oldest, az, za; default newest
limitintNomin 1; max 100; default 50
cursorbase64url JSON {k:string 1..1000 chars, id:uuid}Nomax 2000
qstring trimmedNomax 200

Responses

  • 200 {documents:[{id,name,display_name,sort_name,file_size,mime_type,uploaded_by,created_at,archived_at,category_ids,space,client_id,processing_status,processing_retry_after,rendition_mime,description,profiles:{full_name}|null,failure_reason (team only),request_count}],has_more,next_cursor}
  • 400 Invalid input.
  • 401 Not signed in.
  • 404 Not found.
  • 403 Not allowed.
  • 500 Something went wrong; safe to retry.
POST

/api/files/bulk

Archive / restore / delete / move space (optionally attach a client) / add or remove categories for up to 100 documents in one request, with per-id verdicts and a dry_run mode.

Authentication
Signed-in session with two-factor verified
Who can call it
admin, member
Rate limit
300 per 5 min per user (shared with edit)

JSON body

NameTypeRequiredDetails
actionenumYesone of archive, restore, delete, move, add_categories, remove_categories
idsuuid[]Yesmin 1; max 100
dry_runboolNo
spaceenum private|shared|clientYes
client_iduuidNoonly with space=client
category_idsuuid[]Yesmin 1; max 100

Responses

  • 200 {results:[{id,ok,reason?}],counts:{ok,skipped,failed},contacts?,comments?(dry_run move to Shared)}
  • 400 Invalid input.
  • 401 Not signed in.
  • 403 Not allowed.
  • 413 Too large.
  • 429 Too many requests; retry after the delay in the response.
  • 500 Something went wrong; safe to retry.
GET

/api/files/workspace

Org Documents tab: one flat, role-scoped, keyset-paged list across spaces/contacts/categories/uploaders with search, doc_id deep-link lookup and exact_name upload-conflict lookup.

Authentication
Signed-in session with two-factor verified
Who can call it
admin, member, staff_admin(fallback)

Query parameters

NameTypeRequiredDetails
org_iduuidYes
spaceenumNoone of private, shared, client
spaces[]enum[]Nomax 3
client_ids[]uuid[]Nomax 200
no_contactbool-stringNodefault false
category_iduuid|'__unassigned__'Nolegacy
category_ids[]uuid[]Nomax 50
uploaded_by[]uuid[]Nomax 100
uncategorizedbool-stringNo
archived'true'|'only'|absentNodefault hide archived
processingenumNoone of failed
sortenumNoone of newest, oldest, az, za; default newest
cursorbase64url JSON {k,id uuid}; k must be ISO datetime for newest/oldestNomax 2000
searchstring trimmedNomax 200
limitintNomin 1; max 25; default 25
doc_iduuidNolookup mode
exact_namestringNomin 1; max 500; lookup mode, requires exactly one space

Responses

  • 200 {documents:[{id,name,display_name,description,file_size,mime_type,space,created_at,archived_at,category_ids,client_id,client_name,client_archived,client_avatar_url,uploaded_by,uploader_name,uploader_avatar_url,uploader_is_contact,processing_status,rendition_mime,rendition_size,processing_retry_after,request_count,failure_reason}],has_more,next_cursor}
  • 400 Invalid input.
  • 401 Not signed in.
  • 403 Not allowed.
  • 500 Something went wrong; safe to retry.
DELETE

/api/files/{fileId}

Hard-delete a document: row first, then every stored byte; admin or uploader only.

Authentication
Signed-in session with two-factor verified
Who can call it
admin, member (uploader)

Path parameters

NameTypeRequiredDetails
fileIdstringYes

Responses

  • 200 {success:true}
  • 401 Not signed in.
  • 403 Not allowed.
  • 404 Not found.
  • 500 Something went wrong; safe to retry.
PATCH

/api/files/{fileId}

Edit a document: rename, description, notes, move space, archive/restore, set categories, or change its client (with an optional space move).

Authentication
Signed-in session with two-factor verified
Who can call it
admin, member
Rate limit
300 per 5 min per user (shared with bulk)

Path parameters

NameTypeRequiredDetails
fileIdstringYes

JSON body

NameTypeRequiredDetails
display_namestringNomin 1; max 500; not trimmed, no hidden-char check
spaceenumNoone of private, shared, client
notesstring|nullNomax 10000
descriptionstring|nullNodescriptionField, max 500, blank clears
archivedboolNo
category_idsuuid[]Nomax 10
client_iduuid|nullNoonly combined with space

Responses

  • 200 {document:{id,name,display_name,description,space,notes,archived_at,category_ids,file_size,mime_type,uploaded_by,created_at,client_id}}
  • 400 Invalid input.
  • 401 Not signed in.
  • 403 Not allowed.
  • 404 Not found.
  • 429 Too many requests; retry after the delay in the response.
  • 500 Something went wrong; safe to retry.
GET

/api/files/{fileId}/download

The one audited way to get a document as a file: decrypts server-side and streams the original, an Office PDF rendition, or a PDF built from image pages.

Authentication
Signed-in session (team member or client contact)
Who can call it
admin, member (granted), portal contact (granted)
Rate limit
60 / 1h per user_id

Path parameters

NameTypeRequiredDetails
fileIdstringYes

Query parameters

NameTypeRequiredDetails
formatstringNoone of original, pdf; max 16; default first offered format

Responses

  • 200 binary stream; Content-Disposition attachment, Content-Length, Cache-Control private,no-store, nosniff
  • 401 Not signed in.
  • 403 Not allowed.
  • 404 Not found.
  • 409 Conflict with the current state of the resource.
  • 422 Invalid input.
  • 429 Too many requests; retry after the delay in the response.
  • 503 Temporarily unavailable.
  • 500 Something went wrong; safe to retry.
PUT

/api/files/{fileId}/download-access

Set who may download a document (team flag, contacts flag, named people). Admin only; replaces grants atomically.

Authentication
Signed-in session with two-factor verified
Who can call it
admin

Path parameters

NameTypeRequiredDetails
fileIdstringYes

JSON body

NameTypeRequiredDetails
teamboolYes
contactsboolYes
user_idsuuid[]Yesmax 50

Responses

  • 200 {team,contacts,user_ids}
  • 400 Invalid input.
  • 401 Not signed in.
  • 403 Not allowed.
  • 404 Not found.
  • 422 Invalid input.
  • 500 Something went wrong; safe to retry.
GET

/api/files/{fileId}/download-log

Keyset-paged log of who downloaded a document (admin: all; member: own; portal: 404).

Authentication
Signed-in session with two-factor verified
Who can call it
admin, member (own rows)

Path parameters

NameTypeRequiredDetails
fileIdstringYes

Query parameters

NameTypeRequiredDetails
cursorbase64url JSON [ISO datetime with offset, uuid]No
limitintNomin 1; max 50; default 25

Responses

  • 200 {items:[{id,at,event:'download',who:{kind:'member'|'contact',name,avatar?}}],nextCursor,total(first page only),scope:'own'|'all'}
  • 400 Invalid input.
  • 401 Not signed in.
  • 404 Not found.
  • 500 Something went wrong; safe to retry.
GET

/api/files/{fileId}/requests

Document requests this document is attached to (for the details panel and archive/move confirms).

Authentication
Signed-in session (team member or client contact)
Who can call it
admin, member, portal contact

Path parameters

NameTypeRequiredDetails
fileIdstringYes

Responses

  • 200 {requests:[...],total}
  • 401 Not signed in.
  • 404 Not found.
  • 500 Something went wrong; safe to retry.

Clients and contacts

GET

/api/clients

Unpaged list of the workspace's non-archived clients (id, name, email, company, status, assigned team) for the assign-clients and invite-team pickers.

Authentication
Signed-in session with two-factor verified
Who can call it
admin, member

Query parameters

NameTypeRequiredDetails
org_idstring (NOT uuid-validated)Yes

Responses

  • 200 {clients:[{id,name,email,company,status,created_at,client_assignments:[{user_id,profiles:{full_name}}]}]} ordered created_at desc; archived excluded
  • 400 Invalid input.
  • 401 Not signed in.
  • 403 Not allowed.
  • 500 Something went wrong; safe to retry.
POST

/api/clients

Admin creates a client with 1-100 contacts (one or more primary), optional photo and team assignees; every contact is invited (inline, or deferred to the per-contact invite route). Invitations are sent afterwards, one request per contact (never inside this request).

Authentication
Signed-in session with two-factor verified
Who can call it
admin
Rate limit
30/min

JSON body

NameTypeRequiredDetails
org_iduuidYes
namestring trimYesmin 1; max 120
companystring trim (alias of name)Nomin 1; max 120
contactsarray<{email: trimmed lower-cased email, name?: string trim max 120 nullable}>Yesmin 1; max 100
primaryIndexintNomin 0; default 0
primaryIndexesint[]Nomin 1; max 100; wins over primaryIndex; each must be < contacts.length
assignedTeamUserIdsuuid[]NoNO max length
deferInvitesbooleanNo
avatarKeystringNomax 500; must equal pending-avatars/<orgId>/<digits>-<12hex>.(png|jpg|webp)

Responses

  • 201 {client:{...clients row}, inviteFailures:string[] emails, invitesSkipped:number}; with deferInvites: {client, contacts:[{id,email}], inviteFailures:[], invitesSkipped:0, invitesDeferred:true}
  • 400 Invalid input.
  • 401 Not signed in.
  • 403 Not allowed.
  • 409 Conflict with the current state of the resource.
  • 422 Invalid input.
  • 429 Too many requests; retry after the delay in the response.
  • 500 Something went wrong; safe to retry.
  • 503 Temporarily unavailable.
POST

/api/clients/bulk/archive

Admin archives up to 25 clients at once and signs every linked portal contact out globally.

Authentication
Signed-in session with two-factor verified
Who can call it
admin
Rate limit
10/min

JSON body

NameTypeRequiredDetails
clientIdsuuid[]Yesmin 1; max 25
org_iduuidYes

Responses

  • 200 {succeeded:string[], failed:[{id, reason:'not_found'|a short reason}]}
  • 400 Invalid input.
  • 401 Not signed in.
  • 403 Not allowed.
  • 429 Too many requests; retry after the delay in the response.
  • 500 Something went wrong; safe to retry.
POST

/api/clients/bulk/delete

Admin hard-deletes up to 25 clients (cascade to contacts, documents rows) after signing their linked portal users out globally.

Authentication
Signed-in session with two-factor verified
Who can call it
admin
Rate limit
10/min

JSON body

NameTypeRequiredDetails
clientIdsuuid[]Yesmin 1; max 25
org_iduuidYes

Responses

  • 200 {succeeded:string[], failed:[{id, reason:'not_found'|a short reason}]}
  • 400 Invalid input.
  • 401 Not signed in.
  • 403 Not allowed.
  • 429 Too many requests; retry after the delay in the response.
  • 500 Something went wrong; safe to retry.
POST

/api/clients/bulk/unarchive

Admin restores up to 25 archived clients at once.

Authentication
Signed-in session with two-factor verified
Who can call it
admin
Rate limit
10/min

JSON body

NameTypeRequiredDetails
clientIdsuuid[]Yesmin 1; max 25
org_iduuidYes

Responses

  • 200 {succeeded:string[], failed:[{id, reason:'not_found'|a short reason}]}
  • 400 Invalid input.
  • 401 Not signed in.
  • 403 Not allowed.
  • 429 Too many requests; retry after the delay in the response.
  • 500 Something went wrong; safe to retry.
POST

/api/clients/contacts/check

Admin pre-check of up to 100 candidate contact emails: ok | invalid | team | pending_team | existing contact (with its client name).

Authentication
Signed-in session with two-factor verified
Who can call it
admin
Rate limit
60/min

JSON body

NameTypeRequiredDetails
org_iduuidYes
emailsstring[] (items have NO max length, not email-validated here)Yesmin 1; max 100

Responses

  • 200 {results:[{email,status:'ok'|'invalid'|'team'|'pending_team'|'contact', clientId?, clientName?}]} same order as input
  • 400 Invalid input.
  • 401 Not signed in.
  • 403 Not allowed.
  • 404 Not found.
  • 429 Too many requests; retry after the delay in the response.
  • 500 Something went wrong; safe to retry.
  • 503 Temporarily unavailable.
GET

/api/clients/list

Paginated, searchable, sortable, filterable enriched Clients table (contacts, team, doc/category counts, derived status, signed avatars); ids_only=true returns the Select-all id set (admin only, max 100).

Authentication
Signed-in session with two-factor verified
Who can call it
admin, member

Query parameters

NameTypeRequiredDetails
org_idstring (NOT uuid)Yesmin 1
pageintNomin 1; default 1
pageSizeintNomin 1; max 100; default 25
searchstringNono max length; escapeIlike then or() with ,()" stripped; matches client name/company and contact name/email
statusenumNoone of archived, active, invited, inactive
sortenumNoone of recently_added, oldest_added, name_asc, name_desc, company_asc, company_desc, last_active, last_active_desc, last_active_asc; default recently_added
assignedUserIdscomma list of uuidNono max count
clientIdscomma list of uuidNomax 100
categoryIdscomma list of uuid or '__uncategorized__'Nomax 100
ids_onlyenumNoone of true, false

Responses

  • 200 {rows:[EnrichedClient{id,name,email,company,status,rawStatus,created_at,last_active,avatarUrl(signed),primaryContactUserId,contacts[top4],totalContacts,assignedTeam[],pendingTeamCount,orgAdminCount,teamCount,docCount,categoryCount}], total, page, pageSize}; ids_only: {ids:[{id,name,status}], total, capped}
  • 400 Invalid input.
  • 401 Not signed in.
  • 403 Not allowed.
  • 500 Something went wrong; safe to retry.
GET

/api/clients/picker

Lightweight name-ordered, offset-paged {id,name} client list for pickers and filter drawers; scope=mine limits a member to assigned clients.

Authentication
Signed-in session with two-factor verified
Who can call it
admin, member

Query parameters

NameTypeRequiredDetails
org_iduuidYes
scopeenumYesone of all, mine
searchstringNomax 200; ilike name with escapeLike
limitintNomin 1; max 200; default 50
offsetintNomin 0; max 100000; default 0

Responses

  • 200 {clients:[{id,name}], hasMore:boolean}, ordered by name
  • 400 Invalid input.
  • 401 Not signed in.
  • 403 Not allowed.
  • 500 Something went wrong; safe to retry.
DELETE

/api/clients/{clientId}

Hard-delete a client (typed name confirmation), signing out its linked portal users

Authentication
Signed-in session
Who can call it
admin

Path parameters

NameTypeRequiredDetails
clientIdstringYes

JSON body

NameTypeRequiredDetails
confirmNamestringYestrimmed, exact match; empty/invalid JSON body tolerated -> 400 NAME_MISMATCH; no Zod

Responses

  • 200 {ok:true}
  • 400 Invalid input.
  • 401 Not signed in.
  • 403 Not allowed.
  • 404 Not found.
  • 500 Something went wrong; safe to retry.
PATCH

/api/clients/{clientId}

Edit client name/company/photo and replace its assigned team (admin only)

Authentication
Signed-in session
Who can call it
admin

Path parameters

NameTypeRequiredDetails
clientIdstringYes

JSON body

NameTypeRequiredDetails
namestringNomin 1; max 120; trimmed
companystring|''Nomax 120; '' clears
assignedTeamUserIdsuuid[]NoNO max length
avatarR2Keystring|nullNomax 500; null/'' clears; else must be pending-avatars/<orgId>/<ms>-<12hex>.(png|jpg|webp)

Responses

  • 200 {client:<clients row>}
  • 400 Invalid input.
  • 401 Not signed in.
  • 403 Not allowed.
  • 404 Not found.
  • 409 Conflict with the current state of the resource.
  • 422 Invalid input.
  • 500 Something went wrong; safe to retry.
POST

/api/clients/{clientId}/archive

Archive a client (status archived) and sign out all linked portal users

Authentication
Signed-in session
Who can call it
admin

Path parameters

NameTypeRequiredDetails
clientIdstringYes

Responses

  • 200 {client:<clients.* + client_contacts[{user_id}]>}
  • 401 Not signed in.
  • 403 Not allowed.
  • 404 Not found.
  • 500 Something went wrong; safe to retry.
GET

/api/clients/{clientId}/contacts

List non-archived contacts of a client with derived status and primary flag

Authentication
Signed-in session
Who can call it
admin, member (assigned only)

Path parameters

NameTypeRequiredDetails
clientIdstringYes

Responses

  • 200 {contacts:[{id,email,name,userId,avatarUrl,lastActiveAt,invited_at,portal_enabled,createdAt,isPrimary,status}]}
  • 401 Not signed in.
  • 404 Not found.
  • 500 Something went wrong; safe to retry.
POST

/api/clients/{clientId}/contacts

Add a contact to a client and send the portal invite (admin, or a PRIMARY portal contact for their own client)

Authentication
Signed-in session
Who can call it
admin, portal contact (primary only)
Rate limit
30/min

Path parameters

NameTypeRequiredDetails
clientIdstringYes

JSON body

NameTypeRequiredDetails
emailstringYestrim+lowercase+email
namestring|nullNomax 120
isPrimarybooleanNo

Responses

  • 201 {contact:{id,email,name,user_id,invited_at,portal_enabled}} plus inviteFailed:true when no mail went out (no reason given)
  • 400 Invalid input.
  • 401 Not signed in.
  • 403 Not allowed.
  • 404 Not found.
  • 409 Conflict with the current state of the resource.
  • 422 Invalid input.
  • 429 Too many requests; retry after the delay in the response.
  • 500 Something went wrong; safe to retry.
  • 503 Temporarily unavailable.
DELETE

/api/clients/{clientId}/contacts/{contactId}

Remove a contact (admin), remove a colleague (primary portal contact) or leave (any portal contact)

Authentication
Signed-in session
Who can call it
admin, portal contact (primary for colleagues; any for self/leave)

Path parameters

NameTypeRequiredDetails
clientIdstringYes
contactIdstringYes

Responses

  • 200 {ok:true} (admin) | {ok:true,selfLeft:true} (self leave)
  • 204 empty (primary removed a colleague)
  • 401 Not signed in.
  • 403 Not allowed.
  • 404 Not found.
  • 409 Conflict with the current state of the resource.
  • 500 Something went wrong; safe to retry.
  • 503 Temporarily unavailable.
PATCH

/api/clients/{clientId}/contacts/{contactId}

Change a contact's email, make/unmake primary, or archive/restore it (name is never editable)

Authentication
Signed-in session
Who can call it
admin, portal contact (own row; primary for colleagues/primary flag)
Rate limit
10/h

Path parameters

NameTypeRequiredDetails
clientIdstringYes
contactIdstringYes

JSON body

NameTypeRequiredDetails
emailstringNotrim+lowercase+email
isPrimarybooleanNoalone
archivedbooleanNoalone; admin only
nameneverNorefine: any non-undefined name (incl null) => 400

Responses

  • 200 {ok:true,contact:{id,name,email[,isPrimary|archived]}}
  • 400 Invalid input.
  • 401 Not signed in.
  • 403 Not allowed.
  • 404 Not found.
  • 409 Conflict with the current state of the resource.
  • 422 Invalid input.
  • 429 Too many requests; retry after the delay in the response.
  • 500 Something went wrong; safe to retry.
  • 503 Temporarily unavailable.
POST

/api/clients/{clientId}/contacts/{contactId}/invite

Send the FIRST invite to a just-added contact (deferInvites flow)

Authentication
Signed-in session
Who can call it
admin
Rate limit
1200/h

Path parameters

NameTypeRequiredDetails
clientIdstringYes
contactIdstringYes

Responses

  • 200 {ok:true} | {ok:true,already:true}
  • 401 Not signed in.
  • 404 Not found.
  • 409 Conflict with the current state of the resource.
  • 429 Too many requests; retry after the delay in the response.
  • 500 Something went wrong; safe to retry.
  • 502 Temporarily unavailable.
  • 503 Temporarily unavailable.
POST

/api/clients/{clientId}/contacts/{contactId}/resend-invite

Resend a pending portal invite (admin, or a primary portal contact for a colleague)

Authentication
Signed-in session
Who can call it
admin, portal contact (primary for colleagues)
Rate limit
1/min and 5/24h

Path parameters

NameTypeRequiredDetails
clientIdstringYes
contactIdstringYes

Responses

  • 200 {ok:true,email}
  • 401 Not signed in.
  • 403 Not allowed.
  • 404 Not found.
  • 409 Conflict with the current state of the resource.
  • 429 Too many requests; retry after the delay in the response.
  • 500 Something went wrong; safe to retry.
  • 502 Temporarily unavailable.
  • 503 Temporarily unavailable.
POST

/api/clients/{clientId}/unarchive

Restore an archived client to active

Authentication
Signed-in session
Who can call it
admin

Path parameters

NameTypeRequiredDetails
clientIdstringYes

Responses

  • 200 {client:<clients.* + client_contacts[{user_id}]>}
  • 401 Not signed in.
  • 403 Not allowed.
  • 404 Not found.
  • 500 Something went wrong; safe to retry.
GET

/api/members/contacts

Org-wide client-contacts directory (one row per contact with client name/status, derived portal status, last active, primary flags), paged/sorted/filtered; ids_only returns the select-all set.

Authentication
Signed-in session with two-factor verified
Who can call it
admin, member

Query parameters

NameTypeRequiredDetails
org_iduuidYes
ids_onlyenumNoone of true, 1
searchstring (no max)No
pageintNomin 1; default 1
pageSizeintNomin 1; max 100; default 25
statusenumNoone of active, inactive, archived, invited
clientIdscomma list of uuid (no max count)No
includeArchivedenumNoone of true, false
sortByenumNoone of name_asc, name_desc, status_asc, status_desc, invited_desc, invited_asc, added_desc, added_asc; default name_asc
contactTypeenumNoone of all, primary, regular; default all

Responses

  • 200 {rows:[ContactRow{id,client_id,client_name,client_status,name,email,user_id,invited_at,portal_enabled,created_at,avatar_url,pending_avatar_url,avatar_updated_at,last_active_at,status,is_self,is_primary,client_primary_count,contact_archived}], total, totalPages}; ids_only: {ids:[{id,client_id}], totalSelectable}
  • 400 Invalid input.
  • 401 Not signed in.
  • 403 Not allowed.
  • 500 Something went wrong; safe to retry.

Categories

GET

/api/categories

Paged, filterable category list (admin / member / portal contact variants), select-all ids, and upload-picker mode.

Authentication
Signed-in session (team member or client contact)
Who can call it
admin, member, portal contact

Query parameters

NameTypeRequiredDetails
org_iduuidYes
pageintNomin 1; default 1
pageSizeintNomin 1; max 100; default 15
searchstringNomax 100
filterTypeenumNoone of all, workspace_wide, specific, unassigned; default all
sortByenumNoone of name_asc, name_desc, created_asc, created_desc, updated_desc; default name_asc
filterClientIdsstring (comma list)NoNOT uuid-validated, no length cap
createdBystringNomax 2000; comma list of 'me' or uuids, refined
isDefaultenumNoone of true, false
ids_onlyenumNoone of true, 1
purposeenumNoone of upload
_tstringNocache buster

Responses

  • 200 {categories:[{id,name,is_default,assign_to_all_clients,created_at,updated_at,created_by,assignments,assignment_summary,created_by_profile,document_count}],availableClients:[{id,name}],pagination:{page,pageSize,total,totalPages},totalSelectable}; ids_only: {ids,totalSelectable}; portal rows lack summary fields. no-store headers
  • 400 Invalid input.
  • 401 Not signed in.
  • 403 Not allowed.
  • 500 Something went wrong; safe to retry.
POST

/api/categories

Create one category (legacy scope body) or one/many categories (flat bulk body, up to 50 names).

Authentication
Signed-in session
Who can call it
admin, member (own clients only)
Rate limit
30 per 10 min per user

JSON body

NameTypeRequiredDetails
org_iduuidYes
namestringNomin 1; max 100
namesstring[]Nomax 50
assign_to_all_clientsbooleanNodefault false; flat body only; admin only
client_idsuuid[]Nomax 500
scope{kind:"all"}|{kind:"clients",client_ids:uuid[<=500]}Nolegacy body; presence of key selects this schema

Responses

  • 201 legacy: {category:<enrichOne>} ; flat: {created:[{id,name,created_at,updated_at,created_by}],conflicts:[names]}
  • 400 Invalid input.
  • 401 Not signed in.
  • 403 Not allowed.
  • 409 Conflict with the current state of the resource.
  • 429 Too many requests; retry after the delay in the response.
  • 500 Something went wrong; safe to retry.
POST

/api/categories/bulk

Bulk soft-delete or bulk reassign (workspace-wide flag / client set) up to 50 categories.

Authentication
Signed-in session
Who can call it
admin, member (own categories)

JSON body

NameTypeRequiredDetails
actionenumYesone of delete, reassign
idsuuid[]Yesmin 1; max 50
org_iduuidYes
assign_to_all_clientsbooleanNoreassign only
client_idsuuid[]Nomax 500; reassign only; [] = unassign all

Responses

  • 200 delete: {deleted:[ids],skipped:[{id,reason}],not_found:[ids]} ; reassign: {updated:[ids],skipped:[{id,reason}]}
  • 400 Invalid input.
  • 401 Not signed in.
  • 403 Not allowed.
  • 500 Something went wrong; safe to retry.
DELETE

/api/categories/{id}

Soft-delete a category and strip it from every document.

Authentication
Signed-in session
Who can call it
admin, member (own non-workspace-wide categories only)

Path parameters

NameTypeRequiredDetails
idstringYes

Responses

  • 200 {ok:true}
  • 401 Not signed in.
  • 403 Not allowed.
  • 404 Not found.
  • 500 Something went wrong; safe to retry.
PATCH

/api/categories/{id}

Rename a category, toggle workspace-wide, replace its client assignments, or transfer ownership (admin).

Authentication
Signed-in session
Who can call it
admin, member (own non-workspace-wide categories only)

Path parameters

NameTypeRequiredDetails
idstringYes

JSON body

NameTypeRequiredDetails
namestringNomin 1; max 100
assign_to_all_clientsbooleanNo
client_idsuuid[]Nomax 500
owner_iduuidNoadmin only; must be active member of org

Responses

  • 200 {ok:true,category:<enrichOne payload>}
  • 400 Invalid input.
  • 401 Not signed in.
  • 403 Not allowed.
  • 404 Not found.
  • 409 Conflict with the current state of the resource.
  • 500 Something went wrong; safe to retry.
GET

/api/categories/{id}/clients

Preview of the client names behind a category count (up to limit, plus true total).

Authentication
Signed-in session
Who can call it
admin, member

Path parameters

NameTypeRequiredDetails
idstringYes

Query parameters

NameTypeRequiredDetails
org_iduuidYes
limitintNomin 1; max 50; default 8

Responses

  • 200 {workspace_wide:boolean,clients:[{id,name}],total:number}
  • 400 Invalid input.
  • 401 Not signed in.
  • 403 Not allowed.
  • 404 Not found.
  • 500 Something went wrong; safe to retry.

Requests

GET

/api/document-requests

List all requests of ONE client (portal list + per-client firm view)

Authentication
Signed-in session
Who can call it
admin, member (assigned), portal contact

Query parameters

NameTypeRequiredDetails
client_iduuidYes

Responses

  • 200 {requests:RequestRow[], truncated?:true} newest first, cap 5000 paged by 1000
  • 400 Invalid input.
  • 401 Not signed in.
  • 403 Not allowed.
  • 404 Not found.
  • 500 Something went wrong; safe to retry.
POST

/api/document-requests

Create a document request (firm->client or client->firm) with optional opening attachments

Authentication
Signed-in session
Who can call it
admin, member (assigned), portal contact
Rate limit
30/h

JSON body

NameTypeRequiredDetails
org_iduuidYes
client_iduuidYes
items{name}[]Yesmin 1; max 50; name trim 1..200, strict
due_dateYYYY-MM-DD|""Nonot past (isPastDueDate), not too far (isTooFarDueDate)
notestringNomax 1000
directionenumNoone of firm_to_client, client_to_firm; default firm_to_client
document_idsuuid[]Nomax 10; default
category_idsuuid[]Nomax 10

Responses

  • 201 {request:RequestRow, attachments_failed?:true}
  • 400 Invalid input.
  • 401 Not signed in.
  • 403 Not allowed.
  • 404 Not found.
  • 422 Invalid input.
  • 429 Too many requests; retry after the delay in the response.
  • 500 Something went wrong; safe to retry.
GET

/api/document-requests/org

Org-wide request list for the Requests tab (admin all, member assigned clients)

Authentication
Signed-in session
Who can call it
admin, member (assigned clients)

Query parameters

NameTypeRequiredDetails
org_iduuidYes

Responses

  • 200 {requests:RequestRow[], truncated?:true, closed_truncated?:true}; open rows to 5000, closed/cancelled newest 500
  • 400 Invalid input.
  • 401 Not signed in.
  • 403 Not allowed.
  • 500 Something went wrong; safe to retry.
GET

/api/document-requests/{requestId}

Request thread: request, opening attachments, messages, allowed actions for the viewer

Authentication
Signed-in session
Who can call it
admin, member (assigned), portal contact

Path parameters

NameTypeRequiredDetails
requestIdstringYes

Responses

  • 200 {request:RequestRow, attachments, messages, allowed_actions:string[]}
  • 400 Invalid input.
  • 401 Not signed in.
  • 404 Not found.
  • 500 Something went wrong; safe to retry.
POST

/api/document-requests/{requestId}/messages

Post a reply/comment/approve/reopen/cancel on a request thread (state machine in RPC)

Authentication
Signed-in session
Who can call it
admin, member (assigned), portal contact
Rate limit
120/h

Path parameters

NameTypeRequiredDetails
requestIdstringYes

JSON body

NameTypeRequiredDetails
kindenumYesone of reply, comment, reopen, approve, cancel
bodystringNomax 2000; default ; trim; reopen requires text; reply/comment require text or a file
document_idsuuid[]Nomax 10; default ; not allowed with approve/cancel
category_idsuuid[]Nomax 10

Responses

  • 201 {ok:true, message?, request?} (always 201 after the RPC commits; thin {ok:true} if re-read fails)
  • 400 Invalid input.
  • 401 Not signed in.
  • 403 Not allowed.
  • 404 Not found.
  • 409 Conflict with the current state of the resource.
  • 422 Invalid input.
  • 429 Too many requests; retry after the delay in the response.
  • 500 Something went wrong; safe to retry.

Activity

GET

/api/activity

Admin-only paged workspace activity feed (audit_logs catalogue) with search, actor, category and date filters.

Authentication
Signed-in session
Who can call it
admin

Query parameters

NameTypeRequiredDetails
org_iduuidYes
pageintNomin 1; default 1
pageSizeintNomin 1; max 50; default 25
searchstringNomax 200; sanitizeSearch strips structural chars, min 2 chars, ILIKE over 9 metadata keys + actor name match (<=50 actors)
actoruuidNo
categoryenumNoone of all, <ACTIVITY_CATEGORIES>; default all
fromYYYY-MM-DDNo
toYYYY-MM-DDNorefine from<=to

Responses

  • 200 {entries:[{id,action,category,tone,actor,verb,subject,subjectState,href,clientName?,createdAt}],pagination:{page,pageSize,total,totalPages}}
  • 400 Invalid input.
  • 401 Not signed in.
  • 403 Not allowed.
  • 500 Something went wrong; safe to retry.

Dashboard

GET

/api/dashboard

Firm-side dashboard payload (admin or member summary) for a workspace.

Authentication
Signed-in session
Who can call it
admin, member

Query parameters

NameTypeRequiredDetails
org_iduuidYes
todayYYYY-MM-DD (valid calendar date)Yes

Responses

  • 200 AdminDashboard {role:'admin',kpis,attention:{overdue,failed,stale},recentDocuments,activity} | MemberDashboard {role:'member',kpis,requests,myClients,recentDocuments,myUploads,feed}; no-store
  • 400 Invalid input.
  • 401 Not signed in.
  • 403 Not allowed.
  • 500 Something went wrong; safe to retry.

Client portal

GET

/api/portal/dashboard

Client-portal dashboard (documents, needs-action requests, shared docs, firm team, feed) for one client.

Authentication
Signed-in session (team member or client contact)
Who can call it
portal contact

Query parameters

NameTypeRequiredDetails
client_iduuidYes
todayYYYY-MM-DDYes

Responses

  • 200 PortalDashboard {role:'client',kpis:{documents,needsAction,overdue},needsAction,sharedWithYou,firmTeam,feed}; no-store
  • 400 Invalid input.
  • 401 Not signed in.
  • 403 Not allowed.
  • 500 Something went wrong; safe to retry.
GET

/api/portal/people

Portal Members page data: 'firm' tab (admins + assigned team incl. pending) or 'colleagues' tab (contacts of the caller's client), filtered/sorted/paged in memory.

Authentication
Signed-in session (team member or client contact)
Who can call it
portal contact

Query parameters

NameTypeRequiredDetails
client_iduuidYes
tabenumYesone of firm, colleagues
sortByenumNoone of name_asc, name_desc, activity_desc, activity_asc, status_asc, status_desc; default name_asc
statusenumNoone of all, active, inactive, invited, archived; default all
contactTypeenumNoone of all, primary, regular; default all
roleenumNoone of all, admin, member; default all
searchstringNomax 200
pageintNomin 1; default 1
pageSizeintNomin 1; max 100; default 25

Responses

  • 200 firm: {rows:[FirmRow],total,totalPages} ; colleagues: {rows:[PortalColleagueRow],total,totalPages,viewerIsPrimary,primaryCount}
  • 400 Invalid input.
  • 401 Not signed in.
  • 403 Not allowed.
  • 500 Something went wrong; safe to retry.

Try it on your own workspace

Start a free workspace and open Settings to find the developer tools.

  • No credit card
  • Free plan, no expiry
  • The people you exchange with are free