Docs

Reference

REST API

Authentication, errors, pagination and every endpoint of the Comity API, grouped by area.

On this page

Base URL: https://api.comity.so. All REST endpoints are under /v1, take and return JSON, and use the same objects as the MCP tools.

Authentication

Terminal
curl https://api.comity.so/v1/me -H "Authorization: Bearer cmt_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
JSON
{
  "user": { "id": "0199…", "email": "diego@acme.com", "name": "Diego" },
  "org": { "id": "0199…", "slug": "acme", "name": "Acme Inc", "data_mode": "full" },
  "role": "member",
  "token": { "id": "0199…", "name": "cli · diego-laptop", "kind": "pat", "scopes": ["hooks:evaluate", "mcp:tools", "…"] }
}
  • A token belongs to one org; there is no org parameter.
  • Personal tokens (pat) act as their user with that user's role. comity login creates one per machine. Owners and admins can mint more, including for another member (user_email), with POST /v1/tokens.
  • Service tokens (service) have no user. Endpoints marked "personal token" below reject them with 403. They can't hold hooks:evaluate or mcp:tools.
  • Each endpoint requires a scope. Some also require a role: "admin" below means owner or admin.

Invalid, expired or revoked tokens get 401 with WWW-Authenticate: Bearer realm="comity". A personal token stops working when its user leaves the org.

The dashboard uses a session cookie with an X-Comity-Org: <slug> header and X-Comity-CSRF: 1 on writes; cookie sessions can't call hooks or MCP. Use tokens for integrations.

Errors

Errors are RFC 9457 application/problem+json:

JSON
{
  "type": "urn:comity:problem:validation",
  "title": "Invalid request",
  "status": 400,
  "detail": "request failed validation",
  "errors": [{ "path": "title", "message": "String must contain at least 1 character(s)" }]
}
type suffix Status When
bad-request 400 Malformed request
validation 400 Body or query failed validation; see errors[]
unauthorized 401 Missing, invalid, expired or revoked token
plan_limit 402 Plan limit reached (repos; developers in MCP)
forbidden 403 Missing scope or role
not-found 404 Unknown resource or route
conflict 409 State conflict (already exists, already closed, …)
rate-limited 429 Message or question rate limit; see Retry-After
unsupported-protocol 400 Hooks: X-Comity-Protocol is not 1
internal 500 Server error

Billing and sign-in endpoints add their own types (billing_unavailable, already_subscribed, invalid_credentials, …).

Pagination

List endpoints take limit (default 50, max 200) and cursor, and return:

JSON
{ "items": [], "next_cursor": "MTI" }

Pass next_cursor as cursor for the next page. next_cursor is null on the last page. Lists described as "unpaged" always return next_cursor: null. Comma-separated values work for multi-value filters (status=open,acknowledged).

Rate limits

  • Messages: 20 per minute and 200 per day per user.
  • Questions: 10 per minute and 100 per day per user.

Exceeding a limit returns 429 with Retry-After in seconds.

Selectors and references

  • Repo: owner/name or a remote fingerprint such as github.com/acme/api.
  • Intent: INT-12 or 12. Conflict: CF-9 or 9.

Hooks

Method Path Scope Notes
POST /v1/hooks/evaluate hooks:evaluate What comity hook calls on each agent event. Optional header X-Comity-Protocol: 1. See Hook events.

Account

Method Path Scope Notes
GET /v1/me any User, org (incl. data_mode), role, token
PATCH /v1/org policy:write, owner, personal token {data_mode: "full"|"metadata_only"}. Returns {org, previous_data_mode, cleared_signatures}. Switching to metadata_only clears stored signature text. See Data modes.
GET /v1/usage members:read Plan, limits, active developers and repos this month; per-developer list for admins

Repositories and projects

Method Path Scope Notes
GET /v1/repos repos:read Unpaged
POST /v1/repos repos:write, admin {full_name, provider?: "github"|"gitlab"|"bitbucket"|"generic", default_branch?: "main", remotes?: []}. Repos are also registered automatically on the first hook event. 409 if it exists, 402 over the plan's repo limit.
GET /v1/projects repos:read Unpaged
GET /v1/projects/:id repos:read
POST /v1/projects repos:write, admin {name, repos: [selector]}
POST /v1/projects/:id/repos repos:write, admin {repos: [selector]}
DELETE /v1/projects/:id/repos/:repo repos:write, admin :repo = repo id or URL-encoded owner/name
DELETE /v1/projects/:id repos:write, admin 204

Intents

Method Path Scope Notes
GET /v1/intents intents:read ?repo=&owner=me|<email>&status=live|<statuses>&limit=&cursor=
POST /v1/intents intents:write Same body as intent_declare. 201 {intent, conflicts}
GET /v1/intents/:ref intents:read
PATCH /v1/intents/:ref intents:write Owner or admin. Same fields as intent_update's patch plus note. Returns {intent, conflicts}
POST /v1/intents/:ref/close intents:write {outcome: "merged"|"abandoned"|"handoff", note?, handoff_to?}

status=live means draft, active, blocked, in_review.

Terminal
curl -X POST https://api.comity.so/v1/intents \
  -H "Authorization: Bearer $COMITY_TOKEN" -H "Content-Type: application/json" \
  -d '{"repo": "acme/api", "title": "Add invoice PDF export", "scope": {"paths": ["apps/api/src/invoices/**"]}}'

Conflicts

Method Path Scope Notes
GET /v1/conflicts conflicts:read ?status=live|<statuses>&mine=true&repo=&intent=&limit=&cursor=. repo also matches cross-repo conflicts where the repo is the dependent side.
GET /v1/conflicts/:ref conflicts:read
POST /v1/conflicts/:ref/actions conflicts:write Participant or admin. {action, note?}; actions as in conflict_respond. Header X-Comity-Session: <uuid> acts as one of your agent sessions.
POST /v1/conflicts/:ref/feedback conflicts:write {verdict: "useful"|"false_positive"|"too_late"}. 204
POST /v1/conflicts/:ref/override conflicts:write {reason}: 3–2,000 characters, trimmed; optional when the policy sets override.require_reason: false. Role must be in the policy's override.allowed_roles.

status=live means open, acknowledged, resolving.

Messages and conversations

Method Path Scope Notes
GET /v1/messages messages:read, personal token Your inbox. ?unread_only=true&limit=&cursor=. Returned messages are marked read.
POST /v1/messages messages:write {to, body, subject?, requires_ack?, session_id?}; targets as in message_send. 201
POST /v1/messages/:id/ack messages:write {id, acked_at}
GET /v1/conversations messages:read ?kind=channel|direct&limit=(≤100)&cursor=
GET /v1/conversations/:id messages:read
GET /v1/conversations/:id/messages messages:read ?limit=&before=<message id>; oldest → newest; next_cursor is the before for the older page
POST /v1/conversations/:id/messages messages:write {body, requires_ack?}. In a direct thread only participants may post.

Channels are conversations with kind: "channel": #team and one per repo. Reading conversations in the dashboard marks messages read without hiding them from your agents.

Coordination context

Method Path Scope Notes
POST /v1/team-context intents:read Body as team_context
POST /v1/scope-check intents:read Body as scope_check. Writes nothing.
GET /v1/sessions intents:read ?active=true&repo=&limit=&cursor=. {id, repo, user, tool, model, branch, status, intent, started_at, last_heartbeat_at}
GET /v1/presence intents:read, personal token ?repo=&session_id=&limit=(≤100). Teammates' live agents, never your own.

Team memory

Method Path Scope Notes
GET /v1/search personal token; read scope per kind ?q=&kinds=&repo=&limit=(≤50, default 20). Kinds you can't read are skipped.
POST /v1/questions messages:write Body as ask. 201
POST /v1/questions/:id/answers messages:write {answer, session_id?}. 201

Policies

Method Path Scope Notes
GET /v1/policies policy:read ?repo=. {repo_name, org, repo, effective}
PUT /v1/policies policy:write, admin ?repo=. {rules: {…}} replaces that scope's stored rules. See Policies.

Members, invites and tokens

Method Path Scope Notes
GET /v1/members members:read Paged
POST /v1/members members:write, admin {email, name?, role?: "member"}. Only owners add owners or admins.
DELETE /v1/members/:userId members:write, admin, personal token Revokes their tokens. Only owners remove owners or admins. 204
GET /v1/invites members:read, admin Pending invites. Unpaged.
POST /v1/invites members:write, admin {role, email?}. Returns {id, url, role, email, email_sent, expires_at}. Expires after 7 days.
DELETE /v1/invites/:id members:write, admin The invite's id. 204
GET /v1/tokens personal token Admins see all, others their own. {id, name, kind, prefix, user_id, scopes, created_at, last_used_at, expires_at, revoked_at}
POST /v1/tokens tokens:write, admin, personal token {name, kind?: "pat"|"service", user_email?, scopes?, expires_in_days? (≤3650)}. The plaintext token is returned once.
DELETE /v1/tokens/:id own token, or admin 204
Terminal
# A service token for CI that reads conflicts
curl -X POST https://api.comity.so/v1/tokens \
  -H "Authorization: Bearer $COMITY_TOKEN" -H "Content-Type: application/json" \
  -d '{"name": "ci-conflict-report", "kind": "service", "scopes": ["conflicts:read", "intents:read"]}'

Note

Creating tokens needs a token with tokens:write. Tokens from comity login don't have it (their scopes are listed under Security & data), and the dashboard's Team tab lists and revokes tokens but doesn't create them.

Events, stats and streaming

Method Path Scope Notes
GET /v1/events events:read Audit log. ?type=<types>&subject=<uuid>&limit=&cursor=, newest first
GET /v1/feed events:read Same as events plus refs with display names
GET /v1/stats events:read ?days=7|30|90&repo=. Totals, time saved, daily series, heatmap, top clients/members/repos
GET /v1/stream events:read Server-Sent Events. See below.

Event envelope (same as webhook payloads):

JSON
{
  "id": "18342",
  "type": "intent.declared",
  "version": 1,
  "occurred_at": "2026-10-19T12:00:00.000Z",
  "org_id": "0199…",
  "actor": { "kind": "session", "id": "0199…" },
  "subject": { "kind": "intent", "id": "0199…" },
  "payload": { "…": "…" }
}

SSE stream

Terminal
curl -N "https://api.comity.so/v1/stream?types=conflict.detected,hook.blocked&repos=acme/api" \
  -H "Authorization: Bearer $COMITY_TOKEN"
retry: 3000

id: 18342
event: conflict.detected
data: {"id":"18342","type":"conflict.detected","version":1,…}

: ka
  • Filters: types and repos (comma-separated).
  • Resume with the Last-Event-ID header (or ?last_event_id=): missed events are replayed.
  • A : ka comment every 15 seconds keeps the connection alive.
  • On server restart you get event: reconnect with data: {"after_ms":1000}.

Integrations

Owner or admin with a personal token. See Integrations.

Method Path Notes
GET /v1/webhooks Unpaged
POST /v1/webhooks {url, event_types?: ["*"], description?}. 201 with secret (once)
DELETE /v1/webhooks/:id 204
POST /v1/webhooks/:id/test 202 {delivery_id}; re-enables a disabled endpoint
GET /v1/webhooks/:id/deliveries ?limit=
GET /v1/integrations/slack Connection status and last post
PUT /v1/integrations/slack {webhook_url}; null disconnects
POST /v1/integrations/slack/test 202 {notification_id}
GET /v1/github/app Apps, installations, install links
GET /v1/github/app/manifest ?owner=&format=json|html. Starts the GitHub App manifest flow

Billing

Method Path Who Notes
GET /v1/billing owner, admin, billing Plan and subscription
POST /v1/billing/checkout owner, billing {plan: "team"|"business"} → {url}
POST /v1/billing/portal owner, billing → {url}

Health

Method Path Notes
GET /healthz {"status":"ok"}. No auth.
GET /readyz {"status":"ready"|"unavailable","checks":{…}}; 503 when not ready. No auth.

MCP

GET|POST|DELETE /mcp: the MCP Streamable HTTP endpoint, bearer token with mcp:tools. See MCP tools.