The API is an internal browser-session boundary. Mutations require an active
database session, an accessible Workspace membership, and a trusted Origin.
GET /api/auth/setup-statusreports whether the zero-user setup claim is open.POST /api/auth/setup/registeratomically creates the first owner, Workspace, password credential, and default time-entry types usingAUTH_SETUP_TOKEN.GET /api/auth/invitations/:tokeninspects a valid one-time invitation.POST /api/auth/invitations/:token/registercreates a new invited account;POST .../acceptjoins the exact authenticated existing account.PATCH /api/account/passwordchanges the authenticated user's password.POST /api/workspaces/:workspaceSlug/settings/members/:membershipId/password-resetcreates a one-hour administrator reset URL shown once.GET|POST /api/auth/password-resets/:tokeninspects or consumes that URL.POST /api/auth/emergency-recoveryresets an active initial-Workspace owner using the installer setup code.
Passwords are never trimmed. Invitation and recovery lists never expose raw tokens; create and refresh responses are the only places that return bearer URLs.
GET /api/workspaces/:workspaceSlug/clientslists accessible Clients.POST /api/workspaces/:workspaceSlug/clientscreates a Client and default issue namespace for an owner or admin.PATCH /api/workspaces/:workspaceSlug/clients/:clientIdupdates Client name, description, icon-or-emoji key, and validated icon color.GET|POST /api/workspaces/:workspaceSlug/clients/:clientId/resourceslists or creates pinned Client resources.PATCH|DELETE /api/workspaces/:workspaceSlug/clients/:clientId/resources/:resourceIdupdates, reorders, or archives a pinned resource.GET|POST /api/workspaces/:workspaceSlug/clients/:clientId/memberslists the explicit Client roster or adds an active Workspace membership.PATCH|DELETE /api/workspaces/:workspaceSlug/clients/:clientId/members/:membershipIdchanges Client permission or removes a roster member.GET /api/workspaces/:workspaceSlug/memberslists assignable active Workspace members. Guest results are limited to the current membership.
GET /api/workspaces/:workspaceSlug/projectsreturns the accessible Workspace-wide flat Project directory. OptionalclientIdlimits it to one Client. Records include namespace and workflow IDs, latest-update health, priority, lead, target date, Issue counts, and derived completion progress.POST /api/workspaces/:workspaceSlug/projectscreates a Project directly under its Client and provisions its workflow.GET|POST /api/workspaces/:workspaceSlug/projects/:projectId/brancheslists or creates feature, sprint, refactor, release, and other Branches.PATCH /api/workspaces/:workspaceSlug/projects/:projectId/branches/:branchIdupdates or archives a Branch. Branches never fork or merge data.GET /api/workspaces/:workspaceSlug/projects/:projectIdreturns Project overview data, assignees, progress, latest update, resources, and milestones.PATCH /api/workspaces/:workspaceSlug/projects/:projectIdupdates state, priority, singularleadMembershipId, summary, description, visibility, dates, icon identity, or the atomic assignee collection.GET|POST /api/workspaces/:workspaceSlug/projects/:projectId/activityreads activity or publishes an authored update.PATCH /api/workspaces/:workspaceSlug/projects/:projectId/activity/:updateIdedits authored update text; Guest edits are author-only.GET|POST /api/workspaces/:workspaceSlug/projects/:projectId/memberslists or grants the dedicated Project access roster.DELETE /api/workspaces/:workspaceSlug/projects/:projectId/members/:membershipIdrevokes Project access without changing Project assignment data.POST /api/workspaces/:workspaceSlug/projects/:projectId/resourcesadds a validated HTTP/HTTPS resource.POST /api/workspaces/:workspaceSlug/projects/:projectId/milestonesadds a Project milestone.
Project assignees are supplied as membership IDs:
{
"assigneeMembershipIds": ["membership-uuid"]
}GET /api/workspaces/:workspaceSlug/issueslists accessible Issues across Clients. Optional filters areclientId,projectId,branchId,branch=main,assigneeMembershipId, andmine=true.POST /api/workspaces/:workspaceSlug/issuescreates an Issue.GET /api/workspaces/:workspaceSlug/issues/:issueIdreturns full Issue detail, including assignees and labels.PATCH /api/workspaces/:workspaceSlug/issues/:issueIdupdates present fields and requiresexpectedVersion.GET|POST /api/workspaces/:workspaceSlug/issues/:issueId/commentslists or creates comments and replies. POST acceptsbody, optionalparentCommentId, and up to ten readyattachmentIdsuploaded by the actor to the same Issue. A file-only comment is valid.PATCH|DELETE /api/workspaces/:workspaceSlug/issues/:issueId/comments/:commentIdedits or soft-deletes one comment. Only its author may edit it; its author or a Workspace owner/admin may delete it. Deleting also removes files attached directly to that comment.GET /api/workspaces/:workspaceSlug/issues/:issueId/activitylists immutable automated events such as Issue creation, status movement, priority changes, and added or removed labels.PUT /api/workspaces/:workspaceSlug/issues/:issueId/labelsatomically replaces labels.GET /api/workspaces/:workspaceSlug/issue-metadatareturns active issue types and labels.
Create example:
{
"clientId": "uuid",
"projectId": "uuid",
"branchId": "uuid-or-null",
"assigneeMembershipIds": ["membership-uuid"],
"statusId": null,
"parentIssueId": null,
"title": "Implement partner API client",
"description": null,
"priority": "high",
"visibility": "internal"
}An Issue receives the selected Project workflow default, or the Client workflow
default when projectId is null, if statusId is omitted. Every Issue persists
a workflow status. Moving between Client and Project workflows maps status by
category and clears an incompatible Branch. Moving to another Project also
clears Branch unless a valid destination branchId is supplied. A stale
version returns 409 conflict.
GET /api/workspaces/:workspaceSlug/workflows/:workflowId/statuses returns
active statuses ordered by position for an accessible Client or Project workflow.
GET /api/workspaces/:workspaceSlug/inboxlists up to 100 non-dismissed notifications for the current membership.unread=truelimits the feed.POST /api/workspaces/:workspaceSlug/inboxmarks every notification read for the current membership.PATCH /api/workspaces/:workspaceSlug/inbox/:notificationIdacceptsread,unread, ordismissas itsaction.
Assignment, workflow-status, and comment mutations notify the Issue creator and current assignees other than the actor. Notification access is recipient-scoped and current Issue visibility is rechecked when listing.
GET /api/workspaces/:workspaceSlug/favoriteslists the current membership's accessible Client, Project, and Issue favorites.PUT /api/workspaces/:workspaceSlug/favoritesidempotently sets favorite state withtargetType,targetId, andfavorite.
Favorites are membership-scoped and reapply normal target visibility rules. The response includes current labels, context IDs, and entity icon metadata for sidebar rendering.
DELETE /api/workspaces/:workspaceSlug/clients/:clientIdarchives the Client, its Projects, Branches, and Issues. Owners and admins only.DELETE /api/workspaces/:workspaceSlug/projects/:projectIdarchives the Project, its Branches, and its Issues. Owners and admins only.DELETE /api/workspaces/:workspaceSlug/issues/:issueIdarchives one Issue for an authorized Client contributor.
Deletion is soft: historical time logs remain queryable. Any active timer in
the affected scope returns 409 conflict until it is stopped.
GET|POST|DELETE /api/workspaces/:workspaceSlug/timers/activereads, starts, or stops the current user's authoritative timer.GET|POST /api/workspaces/:workspaceSlug/time-logslists finalized logs or creates a manual entry.PATCH /api/workspaces/:workspaceSlug/time-logs/:timeLogIdupdates a finalized entry's duration, time-entry type, and note usingexpectedVersionoptimistic concurrency.GET|POST /api/workspaces/:workspaceSlug/time-categorieslists or manages reporting categories.GET /api/workspaces/:workspaceSlug/time-reports?groupBy=:dimensionaggregates for owners and admins.GET /api/workspaces/:workspaceSlug/clients/:clientId/time-reportreturns up to 1,000 Client-scoped finalized entries for a requiredfrom/toperiod, with optionalprojectId,categoryId, andworkerUserIdfilters.GET /api/workspaces/:workspaceSlug/clients/:clientId/time-report/exportaccepts the same filters plus an optional validated IANAtimeZoneand returns an authenticated landscape A4 PDF.
Time-log filters are issueId, clientId, projectId, categoryId,
branchId, workerUserId, from, and to. Report grouping accepts issue,
project, branch, client, category, or worker; null Branch attribution
is returned as Main.
The PDF client supplies the browser timezone while retaining the exact
inclusive from and exclusive to instants used by the database query. The
timezone controls daily grouping, labels, period text, and entry dates only.
Time-entry edits preserve the original completion timestamp and billable state. Owners and admins may edit any Workspace entry available to them; members may edit only their own entries, and Guests cannot mutate time data.
Every Workspace is provisioned with Planning, Documenting, Developing, Testing, and Other. Owners and admins may add further categories; category keys remain unique within the Workspace. Issue-scoped log requests first authorize the Issue and then return its visible activity entries. Non-Issue report/list requests retain worker-level restrictions for regular members.
Client time reports authorize Client visibility before querying. Owners,
admins, and Guests receive Client-wide scope while members receive only their
own entries. Guest results include direct Client work and only Projects in the
Guest's explicit Project roster. Supplying projectId requires access to that
Project. The response declares scope and whether the 1,000-entry result was
truncated. General time-log and Workspace-report endpoints remain unavailable
to Guests.
The PDF response is private and non-cacheable, uses attachment disposition, and never exposes storage URLs. It contains summary totals, labeled daily bars, a time-entry-type pie chart, Project totals, and entries grouped by Issue. Each displayed duration is independently rounded to the nearest whole hour, with 30 minutes rounding up. A truncated export includes a visible warning.
Manual time-log creation accepts an explicit startedAt epoch. The Issue UI
derives it from the selected local work date and duration so the finalized
entry ends on that date while the server continues to reject future periods.
GET|PATCH /api/account/profilereads or updates the signed-in profile.GET|PATCH /api/account/preferencesmanages theme, density, Issue view, and sidebar defaults.POST|DELETE /api/account/avatarcreates an image upload or removes it.GET|PATCH /api/workspaces/:workspaceSlug/settings/generalmanages Workspace name and icon identity.GET|PATCH /api/workspaces/:workspaceSlug/settings/notificationsmanages the current membership's Inbox event preferences.GET|POST /api/workspaces/:workspaceSlug/settings/memberslists members and invitations or creates an invitation. Guest invitations requireguestAccess.clients, with each Client carryingexcludedProjectIds; the snapshot is provisioned into Client and Project memberships on acceptance.PATCH /api/workspaces/:workspaceSlug/settings/members/:membershipIdchanges role or active, suspended, and removed state with last-owner protection.GET /api/workspaces/:workspaceSlug/settings/storageand/updatesexpose operator-only health, immutable build provenance, official release metadata, heartbeat-backed updater capability, and persisted job progress. A writable queue without a live Docker-capable worker is reported as manual recovery, never automatic readiness. Terminal failures include a plain-language outcome, optional technical details, and automatic rollback state.POST /api/workspaces/:workspaceSlug/settings/updatesaccepts an empty JSON object and queues onlyinstall_latest. It requires the instance operator, a trusted same-origin request, a newer official release, and no active job; success returns202with the queued job.PATCH /api/workspaces/:workspaceSlug/time-categories/:categoryIdrenames, recolors, reorders, changes billing default, or archives a time entry type.
Identity uploads use a two-step intent and raw-body PUT. PNG, JPEG, and WebP inputs are normalized to 256px WebP. Avatar and Workspace icon content remains authenticated and uses private no-store caching.
GET|POST /api/workspaces/:workspaceSlug/attachmentslists one target's files or reserves an upload intent.PUT /api/workspaces/:workspaceSlug/attachments/uploads/:uploadId/contentstreams and finalizes the declared file.DELETE /api/workspaces/:workspaceSlug/attachments/uploads/:uploadIdcancels a pending reservation.GET|DELETE /api/workspaces/:workspaceSlug/attachments/:attachmentIdreturns metadata or removes a file.GET /api/workspaces/:workspaceSlug/attachments/:attachmentId/contentstreams an authorized download.GET|POST /api/workspaces/:workspaceSlug/attachments/:attachmentId/share-linkslists or creates links lasting one hour through 30 days.DELETE /api/workspaces/:workspaceSlug/attachments/:attachmentId/share-links/:linkIdrevokes a link.GET /share/files/:tokenanonymously downloads exactly one unexpired file.
Attachment intents accept targetType (client, project, or issue),
targetId, filename, contentType, and sizeBytes. General files are capped
at 10 MB. Issue uploads remain description attachments until a comment POST
atomically assigns their IDs to that comment. Browser code never receives S3
credentials or bucket object keys.