Developer docs

Developers

API reference

Every endpoint, generated from the server's router.

310 operations in 28 areas, generated from the server's router. Paths are relative to your server (for example https://procera.example.com); authenticate as described in the API overview. The OpenAPI 3.1 document has the same paths and summaries for tools. Request and response bodies are described in words until typed schemas land.

Sign-in and account

Signing in with a password and a second step, refreshing and ending sessions, two-step verification and passkeys. Browser flows; automation uses API tokens.

  • POST/api/v1/auth/login

    Signs in with email and password. Returns an access token and sets the refresh cookie, or answers mfa_required (with mfa_token) or password_change_required. 429 when rate-limited.

    Audited as login.password_change_required, login.password_ok_mfa_required, login.succeeded, password.reset_requested

  • POST/api/v1/auth/mfa

    Completes sign-in with the mfa_token and exactly one of code (TOTP or recovery code) or credential (passkey assertion); 422 otherwise. Returns an access token and the refresh cookie.

    Audited as login.succeeded

  • POST/api/v1/auth/mfa/webauthn/options

    Passkey challenge for the second sign-in step (after the password).

  • POST/api/v1/auth/refresh

    A new access token from the refresh cookie (rotating it). Requires the X-Requested-With: procera header (403 without it); 401 without a valid cookie.

  • POST/api/v1/auth/logout

    Ends the session behind the refresh cookie and clears it; 204. Requires the X-Requested-With: procera header.

    Audited as logout

  • GET/api/v1/me

    The signed-in user's account and the current token's facts (methods used, auth time, plane, workspace, expiry).

  • POST/api/v1/me/sign-out-everywhere

    Revokes every session of the caller's account and invalidates its outstanding access tokens, then clears the refresh cookie; 204.

    Audited as sessions.revoked_all

  • POST/api/v1/me/mfa/totp

    Starts authenticator-app enrolment and returns the secret to scan; confirm it with POST /me/mfa/totp/confirm.

    Audited as mfa.totp_enrollment_started

  • POST/api/v1/me/mfa/totp/confirm

    Confirms TOTP enrolment with a code; returns the recovery codes (shown once) and an upgraded access token for this session.

    Audited as mfa.totp_enabled

  • GET/api/v1/me/mfa/webauthn

    The caller's registered passkeys.

  • POST/api/v1/me/mfa/webauthn

    Registers a passkey (name, credential) answering the challenge from /me/mfa/webauthn/options; 201. Recovery codes are returned once when it is the first second factor.

    Audited as mfa.webauthn_added

  • POST/api/v1/me/mfa/webauthn/options

    A WebAuthn registration challenge for adding a passkey.

  • DELETE/api/v1/me/mfa/webauthn/{id}

    Removes one of the caller's passkeys; 204. Needs a sign-in within the last 5 minutes (401 step_up_required).

    Audited as mfa.webauthn_removed

Email approvals

The confirm page behind Approve and Reject links in approval emails: read the request, then decide once. The link is the credential; no session.

  • GET/api/v1/email-actions/{token}

    What the confirm page shows (no session; the link is the key): {state: open|used|expired|closed, deciding_as, workspace, step, outcomes, request {display_id, title, type_name, initiator, submitted_at, due_at}, answers [{label, type, value}], expires_at}. Answers marked confidential or restricted are left out. 404 for an unknown link.

  • POST/api/v1/email-actions/{token}

    Decides once through the link: outcome approved, or rejected with a comment. Candidacy, separation of duties and "one decision wins" apply as in My work. 409 when the link was used or expired or the step is already decided. Returns {outcome, display_id, status}.

    Audited as instance.advanced, instance.completed, webhook.created, work.decided_by_email, work_item.decided

Outgoing webhooks

Endpoints that receive signed events, their delivery log, replay, test events and secret rolling, and the event catalogue.

  • GET/api/v1/webhook-events

    The event types a webhook can receive, with what each means.

  • GET/api/v1/webhooks

    The workspace's endpoints with their last delivery (connector:view).

  • POST/api/v1/webhooks

    A new endpoint (url https, events types or ["*"], description; connector:configure; at most 20). 201 with the endpoint and its secret (shown only here). 422 for a URL inside a private network or a port other than 443/80/8443/8080.

  • GET/api/v1/webhooks/{id}

    One endpoint (connector:view), with an ETag of its revision.

  • PATCH/api/v1/webhooks/{id}

    Changes the URL, events, description or on/off (connector:configure; honours If-Match). Turning an endpoint back on clears why it was turned off.

    Audited as webhook.updated

  • DELETE/api/v1/webhooks/{id}

    Removes an endpoint and its delivery log (connector:configure). 204.

    Audited as webhook.deleted

  • POST/api/v1/webhooks/{id}/rotate-secret

    A new signing secret (connector:manage_secrets); the previous one also signs for 24 hours. Returns {secret, previous_valid_until}.

    Audited as webhook.secret_rotated

  • POST/api/v1/webhooks/{id}/test

    Queues a webhook.test event for the endpoint (connector:configure); the next delivery pass sends it. 202 with the delivery.

    Audited as webhook.tested

  • GET/api/v1/webhooks/{id}/deliveries

    The endpoint's deliveries, newest first (at most 200), each with its state, attempts, last status, error and duration, a response excerpt and the event body (connector:view).

  • POST/api/v1/webhooks/deliveries/{id}/replay

    Sends a delivery again from the start of its retry schedule, with the same event id (connector:configure).

    Audited as webhook.replayed

API tokens

Personal API tokens: create, list and revoke your own; security admins list and revoke everyone's.

  • GET/api/v1/me/api-tokens

    My tokens in this workspace, newest first.

  • POST/api/v1/me/api-tokens

    A new token with some of my own permissions; the secret is shown once.

    Audited as api_token.created

  • DELETE/api/v1/me/api-tokens/{id}

    Revoke one of my tokens (repeating it is a no-op).

    Audited as api_token.revoked

  • GET/api/v1/api-tokens

    Every token in the workspace (api_token:manage).

  • DELETE/api/v1/api-tokens/{id}

    Revoke anyone's token (api_token:manage).

    Audited as api_token.revoked

Workspace and members

Your workspaces and permissions, switching workspace and step-up, invitations, members, deactivation and reactivation.

  • GET/api/v1/me/workspaces

    The workspaces I belong to, and which one my current token is for (active).

  • POST/api/v1/session/workspace

    Issues an access token for the chosen workspace (or none, for the home view).

    Audited as session.workspace_selected

  • POST/api/v1/session/step-up

    Re-authenticates within the current sign-in and returns an access token with a fresh auth_time.

    Audited as session.stepped_up

  • POST/api/v1/session/step-up/webauthn/options

    Passkey challenge for step-up.

  • GET/api/v1/invitations/preview

    Public: what the invitation page shows to the holder of the link.

  • POST/api/v1/invitations/accept

    Accepts an invitation (body {token}) for the signed-in user and returns the new workspace_id and membership_id.

    Audited as member.joined

  • POST/api/v1/invitations/register

    Creates an account from an invitation (token, display_name, password) and joins the workspace; 201. Sign in afterwards to get a workspace token.

    Audited as member.joined, user.created

  • GET/api/v1/permission-catalog

    Every permission and the built-in role templates with their permissions.

  • GET/api/v1/me/permissions

    My effective permissions in this workspace, and whether I manage a team (manages: I have direct reports or hold work:view_team on a department).

  • GET/api/v1/members

    Every member of the workspace (member:view).

  • GET/api/v1/members/invitations

    The workspace's invitations (member:invite). Links are never included.

  • POST/api/v1/members/invitations

    Invites an email address with a set of roles (member:invite); 201. The invitation link is sent by email only and is never returned here.

    Audited as member.invited

  • DELETE/api/v1/members/invitations/{id}

    Revokes a pending invitation so its link stops working (member:invite); 204.

    Audited as member.invitation_revoked

  • POST/api/v1/members/invitations/{id}/link

    A fresh one-time link for a pending invitation, for the inviter to hand over when email isn't set up. The earlier link stops working. Only this response carries the token; lists never do.

    Audited as member.invitation_link_copied

  • POST/api/v1/members/{id}/deactivate

    Deactivates a member, ending their access to the workspace (member:deactivate); 204.

    Audited as member.deactivated

  • POST/api/v1/members/{id}/reactivate

    Brings a deactivated member back with the roles they had. Restoring access is treated like granting it: step-up, and no restoring roles the caller could not grant.

    Audited as member.reactivated

  • GET/api/v1/roles

    The workspace's roles with their permissions (role:view).

  • POST/api/v1/roles

    A custom role (key, name, permissions, optional cloned_from template); role:manage plus a recent step-up (401 step_up_required otherwise); 201.

    Audited as role.created

  • PATCH/api/v1/roles/{id}

    Replaces a role's permissions (role:manage plus a recent step-up); 204. The audit entry records what was added, removed and how many members it affects.

    Audited as role.updated

  • POST/api/v1/roles/{id}/preview

    Shows what a role change would do before saving it.

  • GET/api/v1/role-grants

    Role grants in the workspace, optionally for one ?membership_id= (role:view).

  • POST/api/v1/role-grants

    Grants role_key to exactly one of membership_id or group_id (else 422), with an optional scope, expires_at and reason; member:manage_roles plus a recent step-up; 201 {id}.

    Audited as role.granted

  • DELETE/api/v1/role-grants/{id}

    Revokes a role grant (member:manage_roles plus a recent step-up); 204.

    Audited as role.revoked

Directory

Departments, groups, positions, managers and the CSV import that keeps them in step with your HR system.

  • GET/api/v1/departments

    Every department in the workspace (directory:view).

  • POST/api/v1/departments

    A new department (name, optional parent_id); directory:manage; 201.

  • PATCH/api/v1/departments/{id}

    Renames a department, moves it (parent_id, null for the root) or sets its head (head_membership_id); directory:manage; 204.

    Audited as department.updated

  • DELETE/api/v1/departments/{id}

    Deletes an empty department (directory:manage); 204. A department that still has sub-departments, members or positions is refused with 409.

    Audited as department.deleted

  • GET/api/v1/departments/{id}/usage

    What refers to a department, shown before deleting it: role grants scoped to it, and active or draft campaigns, announcements and policies whose audience names it. Titles only, and only for people who manage the directory.

  • GET/api/v1/groups

    Every group in the workspace (directory:view).

  • POST/api/v1/groups

    A new group (name, optional description); directory:manage; 201.

    Audited as group.created

  • DELETE/api/v1/groups/{id}

    Deletes a group (directory:manage); 204.

    Audited as group.deleted

  • GET/api/v1/groups/{id}/members

    The membership ids in a group (directory:view).

  • POST/api/v1/groups/{id}/members

    Adds a member (membership_id) to a group (directory:manage); 204.

    Audited as group.member_added

  • DELETE/api/v1/groups/{id}/members/{membership_id}

    Removes a member from a group (directory:manage); 204.

    Audited as group.member_removed

  • GET/api/v1/positions

    Every position in the workspace (directory:view).

  • POST/api/v1/positions

    Positions are off unless directory.positions.enabled.

    Audited as position.created

  • GET/api/v1/locations

    The directory locations in use, with how many active members each has (audience pickers).

  • DELETE/api/v1/positions/{id}

    Deletes a position (directory:manage); 204.

    Audited as position.deleted

  • PATCH/api/v1/members/{id}

    Sets a member's directory fields (title, department_id, manager_membership_id, location; null clears one); directory:manage. Returns {changed}.

    Audited as member.directory_updated

  • GET/api/v1/members/{id}/directory

    A member's directory fields, groups and resolved manager (explicit, department head or none); directory:view.

  • GET/api/v1/members/{id}/holdings

    What a member holds, for the leaving flow (docs/design/ia-competitive-analysis.md §8.3): documents they own, drafts and reviews they started, campaigns they run, groups, direct reports, role grants and open acknowledgements, plus the workspace's hand-over default. Titles and counts only.

  • POST/api/v1/members/import

    CSV directory import. mode=plan (default) reports what would change and rolls back; mode=apply commits.

    Audited as member.directory_imported

Library (SOPs and policies)

Controlled documents: drafts, review and approval, publishing as major or minor versions, retirement, review cadence, verification and change requests.

  • GET/api/v1/artifacts

    The SOPs and policies the caller may view, filtered by kind, state and collection (sub-collections included). Drafts and restricted ones show only to authors of the kind.

  • POST/api/v1/artifacts

    A new SOP or policy (kind, key, title, optional criticality, content) with its first draft (<kind>:create). 201 with the artifact and version; 409 key_taken.

    Audited as artifact.created

  • GET/api/v1/artifacts/{id}

    One document, the versions the caller may see and my_actions (what the caller may do with it, scoped grants included). Returns an ETag of its revision.

  • POST/api/v1/artifacts/{id}/versions

    Starts a new draft from the latest version's content (<kind>:edit_draft). 201; 409 when a draft or review is already open.

    Audited as version.created

  • GET/api/v1/versions/{id}

    One version with its content and review state (<kind>:view; drafts only for authors of the kind). Returns an ETag of its revision.

  • PATCH/api/v1/versions/{id}

    Replaces a draft's content (<kind>:edit_draft). Send If-Match: "<revision>" to guard against overwrites (412 when stale); 409 when the version is not a draft.

    Audited as version.edited

  • GET/api/v1/versions/{a}/diff/{b}

    Semantic diff between two versions of the same artifact: block-level added/removed/changed with word-level changes inside changed blocks.

  • PUT/api/v1/artifacts/{id}/review-cadence

    Sets the document's review cadence in days, or null for the workspace default (<kind>:publish).

    Audited as artifact.review_cadence_set

  • POST/api/v1/artifacts/{id}/recertify

    The owner confirms the published content is still correct, with an optional comment; a new review period starts now (<kind>:publish).

    Audited as artifact.recertified

  • POST/api/v1/artifacts/{id}/verification

    Verifying a controlled artifact needs a recent authentication, like approving it.

    Audited as artifact.verified

  • DELETE/api/v1/artifacts/{id}/verification

    Removes the document's verification badge (<kind>:approve).

    Audited as artifact.verification_removed

  • GET/api/v1/artifacts/{id}/change-requests

    Change requests proposed for a document, optionally filtered by state (<kind>:view).

  • POST/api/v1/artifacts/{id}/change-requests

    Proposes a change to a document with a title and optional body; any reader may (<kind>:view). 201.

    Audited as change_request.opened

  • GET/api/v1/change-requests/{id}

    One change request (<kind>:view on its document).

  • POST/api/v1/change-requests/{id}/resolve

    decision accepted, declined (needs a resolution) or implemented (needs a version_id of the same document); <kind>:publish. 409 from a wrong state.

    Audited as change_request.resolved

  • PUT/api/v1/artifacts/{id}/applicability

    Sets a document's category, who it applies to (applicability audience, null = everyone) and the restricted flag (<kind>:publish). 422 for an empty audience.

    Audited as artifact.applicability_set

  • GET/api/v1/me/policies

    The policies that apply to the caller, with what changed since the previous version.

  • GET/api/v1/collections

    The collections the caller may see, or all of them with collection:manage (collection:view). can_manage says which.

  • POST/api/v1/collections

    A new collection with a name, optionally under parent_id (collection:manage). 201.

    Audited as collection.created

  • PATCH/api/v1/collections/{id}

    Renames a collection and/or moves it (and its subtree) under parent_id (null = top level); collection:manage. A collection cannot move under itself.

    Audited as collection.updated

  • DELETE/api/v1/collections/{id}

    Deletes an empty collection (collection:manage). 204; 409 while it still holds documents or sub-collections.

    Audited as collection.deleted

  • PUT/api/v1/collections/{id}/visibility

    Sets who may see a collection (visibility audience, null = everyone); collection:manage. 422 for an empty audience.

    Audited as collection.visibility_set

  • PUT/api/v1/artifacts/{id}/collection

    Filing a document needs publish on its kind (the people who control where it lives).

    Audited as artifact.collection_set

  • PUT/api/v1/artifacts/{id}/owner

    Changes a document's accountable owner to an active member (membership_id); <kind>:publish.

    Audited as artifact.owner_set

  • GET/api/v1/artifacts/{id}/raci

    A document's RACI assignments (<kind>:view).

  • PUT/api/v1/artifacts/{id}/raci

    Replaces a document's RACI, with at most one accountable assignee (<kind>:publish).

    Audited as artifact.raci_set

  • GET/api/v1/raci/export

    RACI of every document the caller may see, as CSV. Audited.

  • GET/api/v1/versions/{id}/export

    A self-contained, print-ready HTML document (browsers print it as a tagged PDF), Markdown, or a Word document with real headings, lists and tables. Superseded and retired versions carry a "not the current version" banner. Audited.

  • POST/api/v1/versions/{id}/submit

    Sends a draft for review (<kind>:edit_draft); when the workspace requires no review for it, it is approved at once.

    Audited as version.approved, version.submitted, version.withdrawn

  • POST/api/v1/versions/{id}/withdraw

    Takes a version out of review back to draft (<kind>:edit_draft).

  • POST/api/v1/versions/{id}/reviews

    Records a decision (approve, reject, changes_requested; the last two need a comment). Authors cannot review their own (403); controlled approvals need step-up.

    Audited as campaign.activated, campaign.created, version.approved, version.published, version.retired, version.reviewed, version.scheduled

  • POST/api/v1/versions/{id}/publish

    Publishes an approved version now or at effective_at, as a major or minor change (<kind>:publish; controlled needs step-up). reack says who must re-acknowledge.

  • GET/api/v1/versions/{id}/publish-preview

    What publishing a version would do: its number as a major or minor change, whether each asks for a re-acknowledgement, how many people that reaches and by when.

  • POST/api/v1/versions/{id}/retire

    Retires a published or deprecated version with a reason; it is read-only afterwards (<kind>:publish).

Acknowledgement campaigns

Ask people to acknowledge a published document by a deadline, with reminders, escalation to managers and evidence exports.

  • GET/api/v1/campaigns

    Every acknowledgement campaign in the workspace, optionally filtered by ?state=. Needs campaign:view.

  • POST/api/v1/campaigns

    A draft campaign for a published SOP or policy version (version_id, audience, optional title, due_at, audience_snapshot, ack_text). Needs campaign:manage; 201.

    Audited as campaign.created

  • GET/api/v1/campaigns/{id}

    One campaign with an ETag; a draft also carries audience_preview, the number of active members its audience reaches now. Needs campaign:view.

  • GET/api/v1/campaigns/{id}/recipients

    The campaign's assignments, optionally ?state=pending|completed|overdue (422 otherwise). Needs campaign:view.

  • GET/api/v1/campaigns/{id}/evidence

    Evidence export as CSV. The export itself is audited (campaign.evidence_exported).

  • POST/api/v1/campaigns/{id}/activate

    Launches a draft, snapshotting its audience into assignments and notifying every assignee. Needs campaign:manage; 409 unless it is a draft.

    Audited as campaign.activated

  • POST/api/v1/campaigns/{id}/close

    Closes an active campaign (optional reason), freezing it as evidence; open assignments stay as the record. Needs campaign:manage; 409 in any other state.

    Audited as campaign.closed

  • POST/api/v1/campaigns/{id}/cancel

    Cancels a draft or active campaign; a reason is required (422). Needs campaign:manage; 409 in any other state.

    Audited as campaign.cancelled

  • POST/api/v1/campaigns/{id}/remind

    Sends a reminder now to every open assignment of active members and returns {reminded}. Needs campaign:manage; 409 unless the campaign is active.

    Audited as campaign.reminded

  • GET/api/v1/me/acknowledgements

    The caller's own acknowledgements (?state=pending|completed; default: open and completed), limited to the kinds they may acknowledge.

  • POST/api/v1/acknowledgements/{id}/complete

    Acknowledges the caller's own assignment, optionally checking content_hash (409 content_changed). 409 already_acknowledged on repeat; needs sop:acknowledge for SOPs or policy:view for policies (announcements are acknowledged on the announcement).

    Audited as acknowledgement.completed

Announcements

Official messages with audiences, approval before publishing, scheduling, read receipts and required acknowledgements.

  • GET/api/v1/announcements

    Announcements the caller composes or decides on, optionally filtered by ?state=; managers see all. Each item says whether publishing it needs_approval. Needs announcement:create or announcement:publish.

  • POST/api/v1/announcements

    A new draft announcement (title, content, audience, priority, ack and timing fields). Needs announcement:create; 201 with an ETag. 409 when announcements are turned off.

    Audited as announcement.created

  • GET/api/v1/announcements/feed

    The published, unexpired announcements sent to the caller, with read and acknowledgement receipts; ?unread=true keeps only unread ones. Needs announcement:view.

  • GET/api/v1/announcements/{id}

    One announcement the caller may see (author, publisher once submitted, or recipient), with an ETag. Anyone else gets 404.

  • PATCH/api/v1/announcements/{id}

    Replaces a draft's content; author with announcement:create, or a manager. Optional If-Match: "<revision>" (412 if stale); 409 unless the announcement is a draft.

    Audited as announcement.edited

  • POST/api/v1/announcements/{id}/submit

    Sends the author's draft for approval; approvers are notified when the workspace's approval rule applies to its audience. 409 unless it is a draft.

    Audited as announcement.submitted

  • POST/api/v1/announcements/{id}/approve

    Approves a pending announcement and publishes it now, or schedules it for publish_at. Needs announcement:publish; 403 sod_violation if the approver is the author.

    Audited as announcement.approved, announcement.published, announcement.rejected, announcement.scheduled, campaign.activated, campaign.created

  • POST/api/v1/announcements/{id}/reject

    Returns a pending announcement to draft with a required comment (422 if empty). Needs announcement:publish; 409 unless it is pending approval.

  • POST/api/v1/announcements/{id}/publish

    Publishes a draft now, or schedules it for its publish_at, when no approval applies (409 otherwise). Author holding announcement:publish, or a manager; 422 if expired.

    Audited as announcement.archived, announcement.published, announcement.read, announcement.scheduled, campaign.activated, campaign.closed, campaign.created

  • POST/api/v1/announcements/{id}/archive

    Withdraws a scheduled or published announcement with a reason and closes its acknowledgement campaign. Needs announcement:publish or authorship; 409 otherwise.

  • POST/api/v1/announcements/{id}/reads

    Records that the caller read it (first read only; repeats change nothing) and returns the receipt. Needs announcement:view.

  • POST/api/v1/announcements/{id}/acks

    Acknowledges the announcement and completes the caller's campaign assignment with it: one acknowledgement, one audit record (announcement.acknowledged, citing the assignment).

    Audited as announcement.acknowledged

  • GET/api/v1/announcements/{id}/analytics

    Reach, read and acknowledgement counts, overall and by department. Author with announcement:create, or announcement:manage.

Requests

Request types you may start, starting a request, your and all requests, request details with steps, timeline and why each step went to whom, comments and watching.

  • GET/api/v1/request-types

    The request types the caller may start (enabled in this workspace, and the caller is in the type's audience), with their start forms priced in the workspace currency. ?scope=all lists every built-in with enabled and audience, for Settings › Requests (workspace:settings.view).

  • GET/api/v1/requests

    Requests by scope: mine (default, request:view_own) or all (request:view_all), optionally filtered by status.

  • POST/api/v1/requests

    Starts a request of type with input answers (201, with Location). Requires an Idempotency-Key (a repeat replays the first response; 409 if reused for another body). Needs request:start.

    Audited as instance.started, request.started

  • POST/api/v1/requests/preview

    "Who approves this" before sending (request:start): a dry run of the routing for the answers so far. A read: nothing is written, nothing is audited, the transaction rolls back.

  • GET/api/v1/requests/{id}

    One request in full, plus can_cancel (whether you may cancel it). 404 unless you may see it (view-all, involved, or in your team).

  • POST/api/v1/requests/{id}/cancel

    Cancels an open request with a reason. Only the requester or a holder of request:cancel; 404 if you can't see it.

    Audited as instance.cancelled, request.cancelled

  • GET/api/v1/requests/{id}/comments

    The conversation on a request (internal notes hidden from the requester) and its watchers.

  • POST/api/v1/requests/{id}/comments

    Posts a comment (request:comment and able to see the request). Each mentioned member must be able to see the request too (422 on /mentions naming who can't). Audited request.commented.

    Audited as request.commented

  • PUT/api/v1/requests/{id}/watch

    Follows a request: new comments and its outcome reach the caller. Audited request.watched.

    Audited as request.watched

  • DELETE/api/v1/requests/{id}/watch

    Stops following a request. Audited request.unwatched.

    Audited as request.unwatched

My work

Your approvals, tasks and acknowledgements: decide, reassign and delegate work items, and team workload for managers.

  • GET/api/v1/work

    Work items by state: open (default) and done are yours (work:view_assigned); unassigned and overdue are workspace-wide and need work:reassign.

  • GET/api/v1/work/workload

    Workload per person: scope=team (default) is the caller's reporting line, for a manager in the directory or work:view_team; scope=all is everyone and needs workspace oversight.

  • GET/api/v1/work/workload/{member}

    The open steps one person holds: visible for someone in the caller's reporting line, or anyone with workspace oversight. Others get 404.

  • GET/api/v1/operations/incidents

    Technical incidents: monitoring:view_dashboards or request:retry.

  • POST/api/v1/operations/incidents/{source}/{id}/retry

    Runs an incident again (audited incident.retried, a reason required).

    Audited as incident.retried

  • POST/api/v1/operations/incidents/{source}/{id}/dismiss

    Gives up on an incident (audited incident.dismissed, a reason required).

    Audited as incident.dismissed

  • POST/api/v1/work/{id}/decisions

    Records your decision (outcome, comment, fields, files). Requires If-Match with the item's revision (428 without) and work:complete; 409 when already decided. An optional Idempotency-Key replays the first response.

    Audited as instance.advanced, instance.completed, work_item.decided, work_item.reassigned

  • POST/api/v1/work/{id}/reassign

    Moves an open step to one member (admin oversight). Idempotency-Key optional: a replay of the same key returns the first answer.

  • POST/api/v1/work/{id}/delegate

    Hands a step the caller holds to a colleague. Separation of duties is re-checked (422 sod_excluded on /membership_id); audited work_item.delegated.

Processes (Guided Studio)

Design processes: templates, drafts saved with If-Match, validation in plain language, test runs, who a step will go to, and publishing.

  • GET/api/v1/processes

    Every process in the workspace, with its draft and published versions.

  • POST/api/v1/processes

    A new process with its first draft from a template.

    Audited as artifact.created

  • GET/api/v1/process-templates

    A blank process and every built-in request type.

  • POST/api/v1/processes/validate

    The problems publishing would report, in plain language.

  • POST/api/v1/processes/test-run

    What a draft does for these answers and choices, worked out in memory (nothing is stored, nobody is told).

  • POST/api/v1/processes/who

    Who each candidate of a step reaches in the directory today.

  • GET/api/v1/processes/{id}

    GET /processes/{id}.

  • PUT/api/v1/processes/{id}/draft

    PUT /processes/{id}/draft with If-Match: "<revision>": saves the open draft, or opens a new draft from the published version first (then If-Match is not needed).

    Audited as version.approved, version.created, version.edited, version.published, version.submitted, work_item.delegated

  • POST/api/v1/processes/{id}/publish

    Submits the draft and, when no review is needed, publishes it. A process that does not compile is refused with its problems (422 {problems}).

Out of office and delegation

Set and clear out of office, for yourself or (as an admin) someone else, with a delegate who decides on your behalf.

  • GET/api/v1/me/out-of-office

    Your current out-of-office period and delegate, if any, and can_set: whether you may change it (work:delegate and the self-service setting). Membership only.

  • PUT/api/v1/me/out-of-office

    Sets your out-of-office from starts_on to ends_on (dates) with a delegate and optional note. Needs work:delegate; 403 when self-service is turned off.

    Audited as member.ooo_set

  • DELETE/api/v1/me/out-of-office

    Clears your out-of-office (204). Same checks as setting it.

    Audited as member.ooo_cleared

  • GET/api/v1/members/{id}/out-of-office

    Someone's out-of-office. Needs work:delegate and either work:reassign over that person or being their manager; 404 if they are not in the workspace.

  • PUT/api/v1/members/{id}/out-of-office

    Sets someone's out-of-office (starts_on, ends_on, delegate, note). Needs work:delegate and either work:reassign or being their manager.

    Audited as member.ooo_set

  • DELETE/api/v1/members/{id}/out-of-office

    Clears someone's out-of-office (204). Needs work:delegate and either work:reassign or being their manager.

    Audited as member.ooo_cleared

Files

Resumable uploads, virus scanning, previews and short-lived download links.

  • GET/api/v1/files

    The workspace Files view (file:view_all), or the caller's own uploads.

  • POST/api/v1/files

    Starts a resumable upload from name, size and optional content_type (file:upload). 201 with the file and the chunk_size to send it in.

  • GET/api/v1/files/usage

    Storage usage for the workspace Files view (file:view_all).

  • GET/api/v1/files/orphans

    Completed files nothing links to, older than files.orphan_purge_after (file:view_all).

  • POST/api/v1/files/orphans/purge

    Deletes every orphaned file and returns how many (file:delete).

    Audited as file.orphans_purged

  • POST/api/v1/files/retention

    Bulk retention change (E2E-R1-12): sets or clears the class of the chosen files, with a reason.

    Audited as file.retention_changed

  • GET/api/v1/files/{id}

    One file and the records it is linked to, for anyone who may see it (its uploader, file:view_all, or a reader of what it is attached to); otherwise 404.

  • DELETE/api/v1/files/{id}

    Deletes a file (204). Uploaders may delete their own unattached files; anything else needs file:delete. Your own file attached to a record without it is 409 file_linked.

  • PUT/api/v1/files/{id}/content

    One chunk at offset (raw bytes). Resume by reading received from GET /files/{id}.

  • POST/api/v1/files/{id}/complete

    Finishes the caller's own upload once every byte has arrived; the file is hashed, type-sniffed and scanned. 409 when the upload is not open or bytes are still missing.

    Audited as file.deleted, file.quarantined, file.rejected, file.scan_failed, file.uploaded

  • GET/api/v1/files/{id}/download-url

    A signed, single-member download link valid for 5 minutes.

  • GET/api/v1/files/{id}/preview-url

    A signed preview link for a clean previewable file (kind: image|pdf|docx|xlsx|csv), or kind: none and no URL. Same permission rules as download-url.

  • POST/api/v1/files/{id}/links

    Attaches a file the caller can see to a draft document version (object_type: artifact_version, object_id); needs edit_draft on its kind. 204; 409 past draft.

    Audited as file.linked

  • GET/api/v1/versions/{id}/attachments

    Files attached to a document version (readers of the version see them).

  • GET/api/v1/files/download/{token}

    Serves a file for a valid token; re-checks membership, permission and file state every time.

  • GET/api/v1/files/preview/{token}

    Serves a previewable file for a valid preview token, re-authorized on every request, under a strict sandboxing CSP. Anything not on the preview allow-list is 415.

Notifications

Your in-app notifications and how you are notified.

  • GET/api/v1/notifications

    Your in-app notifications, newest first, with the unread count. Query: unread=true for unread only, limit (default 50).

  • POST/api/v1/notifications/read-all

    Marks all your notifications read; returns how many were marked.

  • POST/api/v1/notifications/{id}/read

    Marks one of your notifications read (204).

  • GET/api/v1/me/notification-preferences

    Your delivery preferences (digests, quiet hours), with your overrides applied over the workspace defaults.

  • PUT/api/v1/me/notification-preferences

    Replaces the caller's overrides; null fields follow the workspace defaults. Audited.

    Audited as notification_preferences.changed

Profile

Your name, time zone and language.

  • GET/api/v1/me/profile

    Your email, display name, time zone and language, plus the languages offered. Shared by every workspace you belong to.

  • PATCH/api/v1/me/profile

    Changes any of display_name, timezone (IANA name) and locale; omitted fields are kept. 422 for an invalid value. Audited; returns the updated profile.

    Audited as user.profile_updated

Saved views and exports

Saved table views (personal, shared, by role) and recorded CSV and XLSX exports.

  • GET/api/v1/views

    My views and the views shared with me for a list (mine first, then by name).

  • POST/api/v1/views

    Saves a view of a list (list, name, state), private or shared with everyone (shared) or one role (shared_role); is_default makes it your default for that list. 201.

    Audited as view.created

  • GET/api/v1/views/roles

    The roles a view can be shared with.

  • PATCH/api/v1/views/{id}

    Only the owner changes a view (shared views included).

    Audited as view.updated

  • DELETE/api/v1/views/{id}

    Deletes one of your saved views (204). 403 for a shared view you don't own.

    Audited as view.deleted

  • POST/api/v1/exports

    Records that the member exported a grid view. CSV: the browser builds the file from rows it was already allowed to see, and gets 204. XLSX: the browser sends exactly those cells and gets the workbook back; text cells stay text (no formulas).

    Audited as data.exported

Settings

Workspace settings with layered defaults, department overrides, floors and configuration profiles.

  • GET/api/v1/branding

    The workspace look for every member: the accent with its contrast-safe shades, the default theme, the logo and the effective terminology (every term, workspace label or product default). Readable without settings permissions: it is what the UI paints.

  • PATCH/api/v1/workspace

    Renames the workspace (owners and admins: workspace:settings.manage). Recorded on the workspace chain, and on the system chain by the platform service.

    Audited as workspace.renamed

  • GET/api/v1/settings

    The effective value of every workspace setting (workspace:settings.view).

  • GET/api/v1/settings/effective

    The explainer: final value and every contributing layer.

  • GET/api/v1/settings/{key}

    One setting's effective value and the layers behind it, with its revision as the ETag (workspace:settings.view); 404 for an unknown key.

  • PUT/api/v1/settings/{key}

    Sets a workspace setting (value, optional reason) under the key's own permission (some also need step-up); optional If-Match: "<revision>" (412 if stale), 422 if invalid.

    Audited as setting.changed

  • GET/api/v1/settings/{key}/history

    The last 100 changes to one setting, newest first (workspace:settings.view).

  • GET/api/v1/settings/departments/{id}

    Department-scopable keys resolved for one department, with its own overrides.

  • PUT/api/v1/settings/departments/{id}/{key}

    Sets a department override (value, optional reason) under the key's permission on that department; optional If-Match (412 if stale), 422 if not scopable.

    Audited as setting.override_set

  • DELETE/api/v1/settings/departments/{id}/{key}

    Removes a department's override so the workspace value applies again; optional If-Match (412 if stale), 404 if there is no override.

    Audited as setting.override_cleared

  • GET/api/v1/settings/profile/plan

    Preview of a profile switch: every key that would change. The ETag is the plan basis.

  • POST/api/v1/settings/profile/apply

    Applies the plan whose basis is in If-Match (required): step-up, a reason, and the permission of every key it changes. One audit event (settings.profile_switched).

    Audited as settings.profile_switched

Audit trail

The hash-chained audit trail: search events, object timelines, chain verification, saved queries and exports.

  • GET/api/v1/audit/events

    Audit events filtered by time, actor or member, action, category, object and result, in keyset pages (page[size], page[cursor]). audit:view sees the workspace chain; audit:view_own sees only the caller's own events.

  • GET/api/v1/audit/objects/{object_type}/{id}/timeline

    Every event for one object, oldest first. artifact spans all of the artifact's versions (the library records lifecycle events per version).

  • GET/api/v1/audit/verify

    Recomputes the workspace chain and reports the first sequence that does not verify.

  • GET/api/v1/audit/saved-queries

    The caller's saved audit queries. Needs audit:view or audit:view_own.

  • POST/api/v1/audit/saved-queries

    Saves a named set of audit filters for the caller; 201. Needs audit:view or audit:view_own.

    Audited as audit.query_saved

  • DELETE/api/v1/audit/saved-queries/{id}

    Deletes one of the caller's saved audit queries; 204 (404 if not theirs). Needs audit:view or audit:view_own.

    Audited as audit.query_deleted

  • GET/api/v1/audit/exports

    The caller's background exports, newest first.

  • POST/api/v1/audit/exports

    Synchronous export of up to 10 000 events (newest first). Procera-Export-Truncated: true means more matched; narrow the filters. Larger exports move to the export job later.

    Audited as audit.export_requested, audit.exported

  • GET/api/v1/audit/exports/{id}

    The state of one of the caller's background audit exports (rows, file, error). Needs audit:export; 404 for anyone else's.

  • POST/api/v1/audit/query

    POST /audit/query: AND/OR filter tree, sorted on the chain, keyset pages.

Encryption keys

The workspace's encryption keys and their rotation.

  • GET/api/v1/security/encryption

    The active key version, every key version with how many values it still wraps, and how many await rewrap. Needs workspace:settings.view.

  • POST/api/v1/security/encryption/rotate

    Rotates to a new key version; existing values are rewrapped in the background. Needs workspace:settings.manage, a recent sign-in and a reason (422 if empty).

    Audited as key.rotated

Templates

The template gallery.

  • GET/api/v1/templates

    The packs, what each creates and, for members who may install it, which of its documents already exist in the workspace (by key).

  • POST/api/v1/templates/{key}/install

    Creates a pack's documents as drafts (documents to pick some, on_conflict skip or copy, campaign for a suggested plan). 201 when anything was created. Needs template:install and <kind>:create for each kind installed.

    Audited as artifact.created, department.created, template.installed

Password recovery

Forgotten passwords, reset links, changing your password and admin account controls.

  • POST/api/v1/auth/password/forgot

    Always 202 with the same body, whether or not the account exists (no enumeration). The email is queued in the background so the answer takes the same time either way.

  • GET/api/v1/auth/password/reset/check

    Whether a reset link still works, so the page can say so before the person types a password.

  • POST/api/v1/auth/password/reset

    Sets a new password from a reset link (token, password); 204, and the account owner is emailed. An invalid, expired or used link is refused with 400. Rate-limited per IP.

    Audited as password.reset

  • POST/api/v1/auth/password/expired

    After password_change_required: sets the new password and continues the sign-in (two-step verification next when the account has it).

    Audited as login.password_ok_mfa_required, login.succeeded, password.changed

  • POST/api/v1/me/password

    Account › Security: change the password. Other sessions end; this one continues with the returned access token.

  • POST/api/v1/members/{id}/security/reset-link

    Emails the member a password reset link. Needs member:security.manage on the member, a recent step-up and a reason; 204, 409 if deactivated.

    Audited as member.mfa_reset, member.password_change_required, member.password_reset_link_sent, member.signed_out_everywhere, mfa.reset, password.change_required, password.reset_requested, sessions.revoked_all

  • POST/api/v1/members/{id}/security/require-password-change

    Makes the member choose a new password at next sign-in (member:security.manage, step-up, reason); 204. 409 if deactivated or used in other workspaces.

  • POST/api/v1/members/{id}/security/sign-out-everywhere

    Ends all of the member's sessions (member:security.manage, step-up, reason); 204. 409 if deactivated or used in other workspaces.

  • POST/api/v1/members/{id}/security/reset-mfa

    Removes the member's two-step verification (member:security.manage, step-up, reason); 204. 409 if deactivated or used in other workspaces.

System plane: workspaces

For system administrators: create, rename, suspend, restore and schedule deletion of workspaces, and quotas. Never reads workspace content.

  • POST/sysapi/v1/bootstrap

    One-time creation of the first system owner from the bootstrap token, with email, display_name and password (201). 401 for a bad or used token, 409 once bootstrapped.

    Audited as platform.bootstrapped, user.created

  • GET/sysapi/v1/me

    Your user id and system roles. Like every system route, needs a system role and MFA.

  • GET/sysapi/v1/workspaces

    The console's workspace grid (?state=&profile=&q=): metadata, quotas and usage counts.

  • POST/sysapi/v1/workspaces

    Creates a workspace (201, with Location and ETag); 409 if the slug is taken. Needs system.owner or system.admin.

    Audited as member.invited, workspace.created

  • GET/sysapi/v1/workspaces/{id}

    One workspace with its quotas and usage counts, as the console lists it, with an ETag of its revision. Any system role.

  • PATCH/sysapi/v1/workspaces/{id}

    Renames a workspace (name). Honours If-Match (412 when stale). Needs system.owner or system.admin.

    Audited as workspace.renamed

  • POST/sysapi/v1/workspaces/{id}/suspend

    Suspends a workspace, mode read_only or locked, with a reason. Needs system.owner/system.admin and a recent sign-in; If-Match honoured (412).

    Audited as workspace.suspended

  • POST/sysapi/v1/workspaces/{id}/restore

    Returns a suspended or deletion-scheduled workspace to active (409 otherwise). Needs system.owner/system.admin and a recent sign-in; If-Match honoured (412).

    Audited as workspace.restored

  • POST/sysapi/v1/workspaces/{id}/schedule-deletion

    Schedules deletion after grace_days, with a reason. Needs system.owner/system.admin and a recent sign-in; If-Match honoured (412).

    Audited as workspace.deletion_scheduled

  • PUT/sysapi/v1/workspaces/{id}/quotas

    Replaces a workspace's quotas; returns the workspace with usage. Needs system.owner or system.admin and a recent sign-in.

    Audited as workspace.quotas_changed

  • POST/sysapi/v1/workspaces/{id}/owner-invitation/link

    A fresh one-time link to the owner's pending invitation (for when email isn't set up). Handing over a credential needs a recent authentication; the earlier link stops working.

    Audited as member.invitation_link_copied, platform.floor_changed, workspace.owner_invitation_link_copied

  • GET/sysapi/v1/settings

    The platform floors every workspace's settings are held to. Any system role.

  • PUT/sysapi/v1/settings/{key}

    Sets a platform floor. Raising it never rewrites workspace values; looser ones resolve as clamped. Floors bind every workspace's security, so this needs a recent authentication (step-up) and, when If-Match carries the listed revision, refuses a stale edit (412).

System plane: console

For system administrators: platform health and configuration.

  • GET/sysapi/v1/quota-defaults

    The quota bundle each configuration profile starts with. Any system role.

  • PUT/sysapi/v1/quota-defaults/{profile}

    Replaces one profile's default quotas for new workspaces. Needs system.owner or system.admin and a recent sign-in; If-Match honoured (412).

    Audited as platform.quota_defaults_changed

  • GET/sysapi/v1/health

    Platform health. status is ok, degraded (something needs a look) or down.

  • GET/sysapi/v1/system-audit/events

    The system chain through the shared explorer, newest first. System administrators' names are resolved; every other actor keeps its pseudonymous subject reference.

  • GET/sysapi/v1/system-audit/verify

    Recomputes the system chain and reports the first sequence that does not verify.

System plane: email

For system administrators: the outgoing email (SMTP) settings.

  • GET/sysapi/v1/email

    The email relay in use (source: environment, database or none), the stored settings without the password, and the mail queue, with an ETag. Needs owner, admin or auditor.

  • PUT/sysapi/v1/email

    Saves the SMTP relay settings. Requires If-Match (428 without; "0" when none stored), a recent sign-in and system.owner/system.admin; 409 when set by the environment.

    Audited as platform.email_changed

  • POST/sysapi/v1/email/test

    Sends one test message to to through the relay in use and returns whether it was sent (or a safe error category); audited either way. 409 if email is not set up.

    Audited as platform.email_tested

  • GET/sysapi/v1/platform

    Read-only deployment facts: nothing here is configurable from the console yet.

Hr

HR time and attendance, slice 1: work locations, holiday calendars and holidays (read directory:view, change calendar:manage), a member's HR record (change directory:manage) and a person's own holidays.

  • GET/api/v1/work-locations

    Locations with their time zone, work week, holiday calendar and active member count (directory:view).

  • POST/api/v1/work-locations

    A new location (calendar:manage). 201. Audited work_location.created.

    Audited as work_location.created

  • GET/api/v1/work-locations/{id}

    One location, with an ETag of its revision (directory:view).

  • PATCH/api/v1/work-locations/{id}

    PATCH /work-locations/{id} (the full location, as for create, plus archived): changes it (calendar:manage; honours If-Match); a rename renames it on members' profiles; a location people work at can't be archived (409). Audited work_location.updated.

    Audited as work_location.updated

  • GET/api/v1/holiday-calendars

    Calendars with the locations using them and the years they have holidays in (directory:view).

  • POST/api/v1/holiday-calendars

    A new calendar, empty or a copy of another's holidays (calendar:manage). 201. Audited holiday_calendar.created.

    Audited as holiday_calendar.created

  • GET/api/v1/holiday-calendars/{id}

    One calendar, with an ETag of its revision (directory:view).

  • PATCH/api/v1/holiday-calendars/{id}

    Renames a calendar (calendar:manage; honours If-Match). Audited holiday_calendar.updated.

    Audited as holiday_calendar.updated

  • DELETE/api/v1/holiday-calendars/{id}

    Deletes a calendar and its holidays when no location uses it (409 otherwise; calendar:manage). 204. Audited holiday_calendar.deleted.

    Audited as holiday_calendar.deleted

  • GET/api/v1/holiday-calendars/{id}/holidays

    The year's holidays [{date, name, optional}] (directory:view).

  • PUT/api/v1/holiday-calendars/{id}/holidays/{date}

    Adds the holiday on that date or renames it (calendar:manage). 201 when new, 200 when changed. Audited holiday.set.

    Audited as holiday.set

  • DELETE/api/v1/holiday-calendars/{id}/holidays/{date}

    Removes it (calendar:manage). 204. Audited holiday.removed.

    Audited as holiday.removed

  • POST/api/v1/holiday-calendars/{id}/import

    Imports CSV (date,name[,optional]) or iCal text, at most 400 holidays and 1 MB (calendar:manage) → {added, updated, skipped: [{line, text, reason}]}. Audited holiday_calendar.imported.

    Audited as holiday_calendar.imported

  • POST/api/v1/holiday-calendars/{id}/copy-year

    Copies a year's holidays to the same month and day of another year, skipping dates already taken (calendar:manage) → as import. Audited holiday_calendar.year_copied.

    Audited as holiday_calendar.year_copied

  • GET/api/v1/members/{id}/hr

    {employee_no, employment_type, work_location_id, start_date, probation_end} (directory:view).

  • PUT/api/v1/members/{id}/hr

    PUT /members/{id}/hr (the whole record): sets it (directory:manage); the work location also becomes the profile's location; 409 for a duplicate employee number. Audited member.hr_updated.

    Audited as member.hr_updated

  • GET/api/v1/me/holidays

    The caller's work location (or null) and its holidays in the range (default: today and the next 366 days; at most 2 years).

  • GET/api/v1/leave-types

    The workspace's leave types (directory:view).

  • POST/api/v1/leave-types

    POST /leave-types {key?, name, colour?, paid?, limited?, sensitive?, half_days?, needs_attachment_after_days?, min_notice_days?, max_consecutive_days?, negative_limit_days?, counts_non_working_days?, optional_holidays_only?} → 201 (time_off:manage_policies). Audited leave_type.created.

    Audited as leave_type.created

  • POST/api/v1/leave-types/defaults

    For a workspace without leave types, the common ones (annual, sick, casual, optional holiday, unpaid) and a "Standard" policy for everyone → 201 {types, policy} (409 when types exist; time_off:manage_policies). Audited leave_type.defaults_created.

    Audited as leave_type.defaults_created

  • PATCH/api/v1/leave-types/{id}

    PATCH /leave-types/{id} (the whole type, plus archived; the key never changes): honours If-Match (time_off:manage_policies). Audited leave_type.updated.

    Audited as leave_type.updated

  • GET/api/v1/leave-policies

    Policies with their rules and how many people each applies to (time_off:manage_policies or time_off:view_all).

  • POST/api/v1/leave-policies

    POST /leave-policies {name, audience, priority?, year_start_month?, rules: [{leave_type_id, yearly_days, accrual upfront|monthly, prorate?, probation_waits?, carry_over_max_days?, carry_over_expiry_months?}]} → 201 (time_off:manage_policies). Audited leave_policy.created.

    Audited as leave_policy.created

  • GET/api/v1/leave-policies/{id}

    One policy with an ETag (as the list).

  • PATCH/api/v1/leave-policies/{id}

    PATCH /leave-policies/{id} (the whole policy, plus archived): honours If-Match; new rules apply from the next grant or accrual (time_off:manage_policies). Audited leave_policy.updated.

    Audited as leave_policy.updated

  • GET/api/v1/leave-policies/{id}/members

    Who it applies to now {count, items: [{id, name}]} (25 at most; as the list).

  • PUT/api/v1/members/{id}/leave-policy

    Gives one person a policy regardless of audiences, or goes back to audiences (time_off:manage_policies) → {policy: {id, name, via}}. Audited leave_policy.assigned.

    Audited as leave_policy.assigned

  • GET/api/v1/me/time-off

    My policy and balances for the leave year containing on (default today): per type {leave_type, year_start, year_end, yearly_days, accrual, granted, carried, taken, pending, adjusted, expired, available, entries}, and my leave requests. Procera writes due grants first.

  • GET/api/v1/members/{id}/time-off

    Someone's policy and balances, as /me/time-off (themselves, or time_off:view_all).

  • POST/api/v1/members/{id}/time-off/adjustments

    A correction in the current leave year (time_off:adjust_balances, step-up) → 201 entry. Audited time_off.adjusted.

    Audited as time_off.adjusted

  • GET/api/v1/time-off/balances

    Everyone's current-year balances by type {types, items: [{id, name, email, policy, balances: {type_id: {available, taken, pending}}}]} (time_off:view_all).

  • POST/api/v1/time-off/quote

    How many days a leave request would take for me, date by date, my balance before and after, and any problems ([{field, code, message}]) the request would be refused for. Changes nothing.

  • GET/api/v1/team-calendar

    Who's out between two dates (≤ 92 days) → {from, to, people [{id, name, title, department, location_id, me}], absences [{membership_id, start_date, start_half, end_date, end_half, state, label, colour, request_id?, days?}], holidays [{location_id, date, name, optional}], work_weeks}. team (default) is me, my manager, my manager's other reports and my reports; department/location default to mine. Colleagues see approved time off as "Out"; the person, their manager chain and HR (time_off:view_all) see the type and pending requests. everyone needs time_off:view_all.

  • GET/api/v1/org-chart

    Everyone active with their manager, title, department and location, and whether they're out today (approved leave, never why) → {items: [{id, name, title, department, location, manager_id, out_today}]} (directory:view).

Signing

Employee e-signing: envelopes from a PDF, recipients, fields, send and void for senders (document:send_for_signature; everyone's with document:view_all, voiding anyone's with document:void), and the signing ceremony for recipients: open it, sign (consent, a typed or drawn signature, their fields) or decline. strong envelopes need a recent sign-in to sign. API tokens never reach /signing/*. The document is served from here.

  • GET/api/v1/envelopes

    Envelopes I sent (default), or every envelope with document:view_all; newest first, at most 500, without fields.

  • POST/api/v1/envelopes

    A draft envelope for an uploaded, clean PDF (title, optional message, file_id, order sequential|parallel, mode simple|strong, expires_in_days 1–365, default 30, bulk to send a copy to each person in a group; document:send_for_signature). 201 with the envelope; 422 for a PDF that can't be signed.

    Audited as envelope.created, file.linked

  • GET/api/v1/envelopes/{id}

    An envelope with its recipients and fields, for its sender, its recipients (once sent) or document:view_all.

  • PUT/api/v1/envelopes/{id}/recipients

    Replaces a draft's recipients in signing order ([{membership_id, role: signer|cc}], at most 50, active members). Honours If-Match.

    Audited as envelope.recipients_set

  • PUT/api/v1/envelopes/{id}/fields

    Replaces a draft's fields (kind signature|initials|date_signed| name|text|checkbox, recipient_id of a signer, page, x/y/w/h as fractions of the page from its top-left corner, required, label). Honours If-Match.

    Audited as envelope.fields_set

  • POST/api/v1/envelopes/{id}/send

    Sends a draft (every signer needs a signature field); the document and fields are frozen and the signers whose turn it is are notified. Honours If-Match.

    Audited as envelope.sent

  • POST/api/v1/envelopes/{id}/void

    Cancels a sent envelope with a reason (its sender, or anyone with document:void); signers who were asked are told.

    Audited as envelope.voided

  • POST/api/v1/envelopes/{id}/send-bulk

    Sends a bulk draft to everyone in audience (all, departments, groups, roles, locations): one copy each, signed by that person, grouped in a batch (at most 2,000 people). Honours If-Match. 201 with the batch.

    Audited as envelope.bulk_sent

  • POST/api/v1/envelopes/{id}/remind

    Reminds the signer whose turn it is now (its sender; at most once an hour per signer). Returns {reminded}.

    Audited as envelope.reminded

  • GET/api/v1/envelope-batches

    Group sends I made (or everyone's with document:view_all), newest first, with their counts.

  • GET/api/v1/envelope-batches/{id}

    A group send with each person's progress (its sender or document:view_all): counts {total, signed, waiting, viewed, declined, cancelled, expired}, rows.

  • POST/api/v1/envelope-batches/{id}/remind

    Reminds everyone in a group send who hasn't signed yet (its sender; at most once an hour each). Returns {reminded}.

    Audited as envelope.batch_reminded

  • POST/api/v1/envelope-batches/{id}/void

    Cancels every copy still waiting for a signature, with a reason (its sender, or document:void). Returns {cancelled}.

    Audited as envelope.batch_voided

  • GET/api/v1/envelopes/{id}/document

    The PDF (application/pdf): the original the signers review, or the signed copy with its certificate once everyone has signed (409 before).

  • GET/api/v1/me/signatures

    The envelopes I'm on: to_sign when it's my turn, then the rest (waiting, signed, declined, completed copies).

  • GET/api/v1/signing/{recipient}

    The signing ceremony for my recipient row: the envelope, my fields, whether it's my turn, everyone's progress, and the consent text. The first open is recorded.

  • POST/api/v1/signing/{recipient}/sign

    Signs on my turn with document_hash (the one shown), consent: true, adopted {kind: typed|drawn, text, image?} and values for my text and checkbox fields. strong envelopes need a recent sign-in (401 step_up_required). The last signature completes the envelope.

    Audited as envelope.completed, envelope.declined, envelope.signed, file.linked, file.uploaded

  • POST/api/v1/signing/{recipient}/decline

    Declines with a reason; the envelope ends for everyone and the sender is told.

Sso

Single sign-on: finding the workspaces whose provider vouches for an email, starting a sign-in (redirect to the provider) and finishing it (the provider redirects back; a session opens in that workspace and the browser returns to the app), and the workspace's provider settings for admins (workspace:settings.manage, step-up to change).

  • POST/api/v1/auth/sso/discover

    The workspaces whose identity provider vouches for this email's domain ({items: [{id, slug, name}]}); empty when none do. No session needed.

  • GET/api/v1/auth/sso/start

    Redirects (303) to the workspace's identity provider with a one-time state, a nonce and a PKCE challenge. 404 when the workspace has no provider.

  • GET/api/v1/auth/sso/callback

    The provider's redirect. Verifies the sign-in, opens a session in the workspace (refresh cookie) and redirects (303) into the app; any problem redirects to the sign-in page with sso_error (refusals are logged). Audited login.sso_succeeded.

  • GET/api/v1/sso

    The workspace's provider settings (never the secret: has_secret), or null (workspace:settings.view).

  • PUT/api/v1/sso

    Sets the provider (issuer, client_id, client_secret (absent keeps it, empty clears it), domains, enabled, required); the issuer's discovery document must answer first (workspace:settings.manage, step-up; honours If-Match).

    Audited as sso.configured

  • DELETE/api/v1/sso

    Removes the provider; everyone signs in with passwords again (workspace:settings.manage, step-up). 204.

    Audited as sso.removed