Conventions

Product endpoint map, request conventions, pagination, conflicts, and errors.


Protected product endpoints under /v1 use Authorization: Bearer <access-token>. The AI review API at POST /v1/reviews uses a dedicated API key and its own request, retry, and error conventions.

Core endpoint map

Domain Main endpoints
Cases GET/POST /v1/cases, GET/PATCH /v1/cases/:id, assignee, ACL, handoffs, handoff candidates/graph, close, reopen, reports (GET/POST /v1/cases/:id/report, GET /v1/cases/:id/report/history, POST /v1/cases/:id/report/actions)
Tasks GET/POST /v1/cases/:id/tasks, GET /v1/tasks, update, assignee, close, reopen
Records GET/POST /v1/cases/:id/records (GET supports scope expansion across handoffs); PATCH/DELETE /v1/records/:recordId
Playbooks GET/POST /v1/playbooks, GET/DELETE /v1/playbooks/:id, versions, /access, /owner, share-token routes
Playbook alias GET/PUT /v1/aliases, DELETE /v1/aliases/:alias; /v1/aliases/resolve resolves pb: references
Suggestions GET/POST /v1/playbooks/:id/suggestions, GET /v1/suggestions, get, update, resolve
Administration workspaces, workspace members, teams, team members, credits, CLI and MCP tokens
Reports GET/POST /v1/reports/personal, GET /v1/reports/personal/history, GET /v1/reports/personal/:reportId, GET/POST /v1/teams/:teamId/report, GET /v1/teams/:teamId/report/history, POST /v1/reports/:reportId/actions

Creation and parent-scoped browsing follow ownership: tasks and records live below their case, and suggestions below their playbook. Cross-case task and cross-playbook suggestion lists remain top-level inbox views. Alias operations are top-level because (ownerId, alias) identifies the namespace resource; each personal or managed workspace namespace can assign one alias to any readable playbook. The playbook owner's alias is surfaced as official; a third-party alias is returned only in its namespace owner's own context. Use assignedTo=me for your task inbox. For suggestions, view=sent lists the ones you sent and view=inbox the ones on playbooks you manage; both require authentication.

Context and pagination

Supported administrative endpoints take workspaceId as a query parameter. Product-domain routes resolve the actor context from the bearer credential. Never assume that a locally selected CLI workspace applies to a raw HTTP request.

List operations use pageSize and an opaque cursor. Preserve the cursor exactly; do not construct or decode it. Records use stable (createdAt, id) ordering and accept order=asc|desc. Record filters always intersect the caller's live case access.

Mutations

Mutation bodies carry idempotencyKey. Reuse a key only for an identical request after a network failure or uncertain response. Case and task mutations include expectedLockVersion; playbook publication includes baseVersionId. A mismatch returns a conflict so the client can re-read and rebuild intent from fresh state.

Close task and close case requests may include new records, allowing the state transition and final durable outputs to commit together.

Input and playbook selection

PATCH /v1/cases/{caseId} accepts optional title, input, and versionId, plus required expectedLockVersion and idempotencyKey. Supply at least one editable field. Set versionId to attach or replace a readable, non-archived playbook version on an open case, set it to null to remove the playbook, or omit it to preserve the current selection. Case editing access is required; closed cases must be reopened first.

Every title, input, or playbook update validates the resulting input against the selected version's input schema, when a playbook is selected. Supplied input replaces the complete object; omission reuses the current input. Playbook selection and optional input replacement commit together; a validation failure saves neither change. Removing the playbook preserves input without applying the old schema.

{
	"versionId": "<version-uuid>",
	"input": { "customer": "Acme" },
	"expectedLockVersion": 1,
	"idempotencyKey": "<new-uuid>"
}

To remove the playbook, send "versionId": null with the latest lock version and a new idempotency key.

Attaching, replacing, or removing a playbook creates a system activity identifying the previous and selected versions as applicable. Existing tasks, records, and reports are preserved; tasks keep their original step references.

Errors

Status Meaning
400 malformed parameter, query, JSON, unsupported value, or request-schema validation failure
401 missing, expired, or invalid bearer credentials
402 insufficient credits in the resolved context
403 authenticated but not authorized for the operation
404 missing or intentionally hidden resource
409 base version, lock version, lifecycle, or uniqueness conflict
429 rate limited; back off before retrying
5xx server or upstream failure

Retry 429 and transient 5xx responses with bounded exponential backoff. Do not automatically retry validation, authorization, or conflict responses. For a conflict, read current state and obtain human or agent judgment before issuing a new mutation.

External sources

POST   /v1/cases/{caseId}/sources
POST   /v1/cases/{caseId}/sources/{sourceId}/refresh
DELETE /v1/cases/{caseId}/sources/{sourceId}
GET    /v1/cases/{caseId}/sources/{sourceId}

Link a Slack thread, Discord message/thread, or native Google Docs, Sheets, or Slides file using your connected account in the active space. Slack and Discord require a workspace; Google Drive supports personal and workspace spaces. Connect Google Drive and select the file with Google Picker in Web first; a URL alone does not grant file access. Google Drive sources use provider key gdrive. Link and unlink cost 2 credits each, with the initial capture included in linking. Each completed manual or automatic refresh costs 1 credit to that case’s billing account. Closed cases refresh only on explicit requests. Queuing, failures, pagination, and retries are free; requests coalesced into one capture incur one refresh charge for that case. case get includes sources for work collaborators only. Reads use saved captures; check status and timestamps before relying on them. When content is omitted, retrieve the latest complete source with its source ID. Historical content is not retained. Sources are separate from records and are never included in public-only responses. See Slack, Discord, and Google Drive for sharing, automatic updates, and capture removal.

Link takes url and idempotencyKey; refresh and unlink take idempotencyKey. All require case work access; linking requires an open case. Source retrieval takes sourceId, returns the latest saved source, and does not contact the provider. Record list kinds are note, output, activity, agreement, and agreement; there is no source record kind.

Explicit creation settings and sharing updates use access: { visibility, grants }: restricted/public visibility and user/team UUID or registered user email keys with viewer/editor values. Emails resolve to user UUIDs before saving; responses contain UUID keys. An unregistered email or conflicting roles for the same resolved user rejects the entire write. Updates replace the whole object; preserve unrelated grants and include your own grant if you want to retain it. Owners retain management independently of grants. Assignees and maintainers receive no implicit access. Editors can inspect and replace sharing, retain at least one explicit editor when changing grants, and archive or restore. Ordinary resource JSON omits private grant maps; editors can retrieve the complete settings with access-get. See Access and identity.

Public cases publish the current title, input, tasks, records, and readable handoffs, including later additions. Public account references for authors and assignees remain visible; private grants, linked sources, review reports and summaries, and private configuration are omitted. Public visibility grants reading only. Explicit viewers can also read private cases and their linked sources; writes require editing.

Copy a readable published version to adapt it independently. The copy has its own content and versions; it does not inherit sharing, drafts, cases, suggestions, aliases, stars, or history, and edits never synchronize. Choose sharing explicitly; omit ownerId to use the active workspace or personal home. copiedFrom records the immediate source and immutable source version; sourceAccessible indicates whether the source remains readable. Copy lists show only readable copies. Copying costs the normal playbook creation price.

POST /v1/playbooks/:id/copies requires sourceVersionId and idempotencyKey, accepts optional ownerId and title, and accepts one of private, teamIds, or access for initial sharing. GET /v1/playbooks/:id/copies accepts pageSize and cursor.

A team can connect multiple workspaces. Only team participants who are owners or admins of their connected workspace can send, list, or revoke team invitations. The email recipient signs in with that email and chooses an existing workspace or creates one. The sender never chooses the destination. Acceptance connects the workspace and adds the recipient, not every workspace member. Accepting into a workspace already connected to the team returns HTTP 409 without consuming the invitation. Use team member management to add participants in that workspace. Each workspace manages its own participants. The same user can participate through multiple workspaces; removing their membership in one workspace preserves their participation in the others. Disconnect removes that workspace and its participants; other connections remain. Team-based access requires both the participation workspace and the resource home to stay connected. Direct user grants remain after disconnect. Invitations expire after fourteen days; resending closes the previous invitation. Connecting a workspace does not bypass its subscription requirements.

POST/GET /v1/teams/:teamId/invitations?workspaceId=... (POST: { "emails": ["customer@example.com"] }); DELETE /v1/teams/:teamId/invitations/:invitationId; DELETE /v1/teams/:teamId/connection; GET /v1/invitations/:token; POST /v1/invitations/:token/accept?workspaceId=....

The /acl mutation routes are removed. Use /access with nested settings. Team invitations require authentication and do not use resource mutation idempotency keys. Workspace creation accepts optional teamInvitationToken to connect and participate atomically.

Ownership, responsibility, and lifecycle

Playbooks and cases have an owning user or workspace account (ownerId), which is also the billing home. A personal owner has full rights; current owners and admins exercise ownership rights for a workspace. Other members need explicit user or team grants, or public visibility for published reads. Workspace membership alone grants no resource access. Removing a workspace member clears their direct grants on items owned by that workspace, including archived and completed items. If removal would leave any affected item without an explicit editor, add another user or team editor first, including on completed and archived items. Personal and other-workspace shares remain unchanged. Team and management authority follow current membership. Required SSO applies to members; non-member direct shares use normal Epismo authentication. Cross-workspace team sharing verifies the participation workspace's SSO. Lists use explicit access and supported public visibility; ownership alone does not include an item.

Omit ownerId on creation or copy to use the active workspace, or your personal account when no workspace is selected. Supplying it explicitly chooses the home subject to authorization. When a workspace is selected, including a saved connection default, creation requires exactly one of private: true, non-empty teamIds, or access. Personal creation may omit sharing. Team editor access covers the creator without adding a redundant direct grant; an explicitly requested creator grant is preserved. Maintenance and assignment do not replace sharing.

Archiving is separate from closing. Editors and owners can archive through Web, CLI, API, or MCP. Archived cases disappear from active views, counts, and handoff graphs and cannot be opened, but their work is retained. List with archived=true (case list --archived) to obtain the UUID and latest lock version, then restore without changing status, assignment, or sharing. Only an owner may permanently delete an archived case, with the latest expectedLockVersion; this removes its tasks, records, sources, and handoff links. Other cases and external provider content remain.

Suggestions belong to their live parent playbook and have no separate owner or sharing snapshot. authorId records who created the proposal; baseVersionId preserves the immutable content reference for evaluating it. Reading requires current parent access; an archived parent hides its suggestions until restored. Authors may edit their open proposals while retaining parent access; current playbook editors may adopt or decline proposals. After a transfer, existing proposals follow the parent’s current sharing. Publish the result before marking a proposal applied.

POST   /v1/playbooks/{playbookId}/restore
PATCH  /v1/playbooks/{playbookId}/assignee
POST   /v1/cases/{caseId}/archive
POST   /v1/cases/{caseId}/restore
DELETE /v1/cases/{caseId}
POST   /v1/workspaces/{workspaceId}/member-handover

Before removing a member, an owner or admin can preview and execute explicit handover of case assignments, playbook maintenance, and access. Open approvals transfer only when explicitly requested; completed approvals and authorship remain history. Review the preview and choose a current successor. workspace member handover <workspace-id> --member-id <user-id> --successor-id <user-id> defaults to preview; use --preview=false to execute. This explicitly grants the successor editing access. Remove membership separately with workspace member delete --workspace-id <workspace-id> <user-id>, then repeat handover with --preview=false --finalize to clean up intervening changes. Finalize requires membership to have already been removed and does not remove it. Removal revokes the corresponding team and workspace management authority. Initial handover preserves direct grants; membership removal clears them on that workspace’s items, and finalization repeats cleanup for late grants. If removal would leave any affected item without an explicit editor, add another user or team editor first, including on completed and archived items. Personal and other-workspace shares remain unchanged.

The HTTP handover request now uses memberId, successorId, and transferApprovals. Its response contains counts in cases, playbooks, tasks, approvals, and suggestions. The CLI needs a matching release before its handover command can use this contract.

Transfer case ownership with epismo_case_owner_transfer, case owner transfer <case-id> --owner-id <account-id> --lock-version <n>, or PATCH /v1/cases/{caseId}/owner (ownerId, expectedLockVersion, idempotencyKey). Current ownership is required. A personal case may move into the connected workspace; a public case also requires destination workspace management rights. A workspace case may move to its creator or a workspace owner. User-to-user and workspace-to-workspace moves are rejected. The transfer preserves assignment and sharing, changes the billing home, and clears derived review reports. Reassignment alone does none of these ownership changes.

Playbook assignment uses assignedTo, just like case assignment, and uses the same assignedTo field in responses. Use epismo_playbook_assign, playbook assign <playbook-id> --assigned-to <user-id>, or PATCH /v1/playbooks/{playbookId}/assignee. Ownership is required; the target must already be able to edit.

Browse saved evaluations

GET /v1/cases/{caseId} includes the latest readable report and reportJob without loading history or running AI. GET /v1/cases/{caseId}/report/history returns newest-first reports summaries and an optional nextCursor; pass pageSize (default 20, maximum 100) and cursor for pagination. GET /v1/cases/{caseId}/report?reportId=REPORT_ID reads a selected full evaluation; omit reportId for the latest report and job.

History reads require current case work access and match the current sharing audience. Public-only readers cannot see reports or summaries. Superseded reports have historical: true and are read-only: creating or dismissing an unhandled proposal from one returns HTTP 409. Read the latest report and reconsider its proposal before acting. Changes to evidence alone still show an advisory reevaluation notice.

Personal and team reports

Personal reports are private to the generating user in the active billing home. Team reports and proposal handling are shared with current team members in the active workspace. See AI review for work selection, health diagnoses, history and sharing behavior.

Operation Personal Team
Generate POST /v1/reports/personal POST /v1/teams/{teamId}/report
Read latest/job/saved GET /v1/reports/personal GET /v1/teams/{teamId}/report
History GET /v1/reports/personal/history GET /v1/teams/{teamId}/report/history
Handle proposal POST /v1/reports/{reportId}/actions POST /v1/reports/{reportId}/actions

Generation accepts a UUID idempotencyKey and returns HTTP 202 with a job. Reads return ReportResponse (report, optional job, historical); select jobId or reportId, never both. History accepts pageSize and cursor. Actions accept proposalId, action (create or dismiss) and optional dismissal feedback; use the saved contents unchanged and omit idempotencyKey. Team IDs come from the path; workspace and billing authority come from authentication.