REST API¶
Everything the UI does goes through the REST API at /api/v1, described by
api/openapi.yaml (OpenAPI 3.1), which every server also publishes at
/api/v1/openapi.yaml and /api/v1/openapi.json. Generate a client for your language from it,
or use curl. AI assistants can use the MCP server instead.
Authentication¶
Use a personal access token or a service-account token as a bearer token:
curl -H "Authorization: Bearer $WFM_TOKEN" https://wfm.example.com/api/v1/search?jql=assignee%3DcurrentUser%28%29
Scopes: read (GET only), write (everything except /admin), admin. The token never has
more access than its owner. Browser sessions use a cookie and must send X-Requested-With on
requests that change data.
Conventions¶
- Issues are addressed by key (
PLAT-12; old keys after a project rename still work) or id. PATCH /issues/{key}changes only the fields you send; sendnullto clear a field. Includeversion(from the last read) to avoid overwriting someone else's change — a stale version answers 409version_conflict.- Lists accept
limit(andoffsetor abeforecursor where noted). - Dates are
YYYY-MM-DD; timestamps are RFC 3339 UTC.
Errors¶
Errors are RFC 9457 problem documents:
{
"status": 422,
"title": "Unprocessable Entity",
"code": "validation_failed",
"detail": "Finish every sub-task first.",
"errors": [{ "field": "validators[0]", "message": "Finish every sub-task first." }]
}
Codes worth handling:
| Code | Status | Meaning |
|---|---|---|
invalid |
400 | a field is invalid; see errors |
version_conflict |
409 | the issue changed since you read it |
transition_not_available |
422 | the transition does not start from the issue's current status |
transition_not_allowed |
422 | a board move has no transition to the target status |
forbidden |
403 | missing permission, or the transition's condition is false |
screen_invalid / screen_required |
422 | the transition needs screen fields (/issues/{key}/transitions lists them) |
validation_failed |
422 | a workflow validator refused the transition |
status_mapping_required |
422 | publishing a workflow or changing a type's workflow removes statuses in use; resend with statusMapping |
duplicate_key |
409 | project key in use |
too_large |
413 | attachment over the size limit |
A query error answers 400 with position, the character offset of the problem.
Common calls¶
# Create an issue
curl -X POST $WFM/api/v1/issues -H "Authorization: Bearer $T" -H "Content-Type: application/json" \
-d '{"projectKey":"PLAT","typeKey":"bug","summary":"Checkout fails on Safari","priority":"high"}'
# What can I do with it, then do it
curl $WFM/api/v1/issues/PLAT-42/transitions -H "Authorization: Bearer $T"
curl -X POST $WFM/api/v1/issues/PLAT-42/transitions -H "Authorization: Bearer $T" \
-H "Content-Type: application/json" -d '{"transitionId":"start-work"}'
# Search
curl -G $WFM/api/v1/search -H "Authorization: Bearer $T" \
--data-urlencode 'jql=project = PLAT AND sprint in openSprints() ORDER BY rank'
Timeline¶
GET /api/v1/timeline?projects=PLAT,OPS&jql=…&doneDays=90 returns what the timeline draws:
epics and standard issues (not sub-tasks) with their dates and parents, the blocks dependencies
between them (sourceKey blocks targetKey), and each project's sprints and releases. Omit
projects for every project you can browse. Change dates with PATCH /issues/{key}
(startDate, dueDate) and add dependencies with POST /issues/{key}/links (type: blocks).
Metrics and dashboards¶
Issue timing (/issues/{key}/metrics), sprint burndown and burnup (/sprints/{id}/report),
velocity (/projects/{key}/velocity), flow metrics (/metrics/summary, /metrics/flow,
/metrics/created-resolved, /metrics/breakdown, /metrics/cycle-time) and dashboards
(/dashboards). See metrics.md.
Slack and Mattermost¶
Project admins manage chat channels with GET/POST /projects/{key}/chat-channels,
PATCH/DELETE /chat-channels/{id}, POST /chat-channels/{id}/test and
GET /chat-channels/{id}/deliveries. The webhook URL is write-only. See chat.md.
Live events¶
GET /api/v1/events?project=PLAT is a server-sent events
stream for one project (cookie or token auth); ?projects=PLAT,OPS follows several (up to
500), and ?projects=* every project you can browse when the stream opens. The ready event
lists the projects followed. Event names are domain events —
issue.created, issue.updated, issue.transitioned, issue.deleted, comment.created,
sprint.started, sprint.completed, release.released, board.updated, development activity
(pr.opened, commit.pushed, …) — with data {type, projectId, issueId, issueKey, actorId}.
Fetch the resource for its new state. A comment line is sent every 25 seconds to keep proxies
from closing the connection.
Webhooks and SCIM¶
Inbound: /hooks/github/{connection}, /hooks/gitlab/{connection} (scm.md) and
/scim/v2 (identity.md). Outbound: automation.md.
Health and metrics¶
GET /healthz— liveness;GET /readyz— readiness (database reachable).- Prometheus metrics on
WFM_METRICS_ADDR(:9090/metrics):wfm_http_requests_total,wfm_http_request_duration_seconds,wfm_jobs_total,wfm_job_duration_seconds,wfm_scm_webhooks_received_total,wfm_sse_subscribers, plus Go runtime and process metrics. - Traces: set
OTEL_EXPORTER_OTLP_ENDPOINTto export OpenTelemetry traces (HTTP, SQL, jobs).