The REST API
Read and write issues from your own scripts with an API token.
Settings → API tokens
Read, create and update issues from another system with an API token. Create a token below, keep it secret, and send it on every request.
Authenticating
- Base URL —
https://your-domain.com/api/v1 - Header —
Authorization: Bearer fk_live_.... The token already belongs to this workspace; no other header is needed. - Replies come as
{ "success": true, "data": … }; on failure{ "success": false, "error": { "statusCode", "message" } }. - Lists are paged:
pagefrom 1 andlimitup to 100;data.meta.totalPagessays how many.
A token acts as the person who created it, with their role. It stops working if they leave the workspace, if it is revoked here, or if the plan loses API access — so create integration tokens from a long-lived admin account. The IP allowlist applies to it too.
Examples
List issues (filters are optional)
curl "https://your-domain.com/api/v1/work-items?projectKey=WEB&statusKeys=todo,in_progress&sortBy=updatedAt&sortDir=desc&limit=100" \ -H "Authorization: Bearer fk_live_..."
Get one
curl "https://your-domain.com/api/v1/work-items/WEB-42" \ -H "Authorization: Bearer fk_live_..."
Change status (a status key of its project)
curl -X PATCH "https://your-domain.com/api/v1/work-items/WEB-42" \
-H "Authorization: Bearer fk_live_..." -H "Content-Type: application/json" \
-d '{"statusKey":"in_progress"}'Assign and set priority (null unassigns)
curl -X PATCH "https://your-domain.com/api/v1/work-items/WEB-42" \
-H "Authorization: Bearer fk_live_..." -H "Content-Type: application/json" \
-d '{"assigneeId":"<user id>","priority":"HIGH"}'Add a comment
curl -X POST "https://your-domain.com/api/v1/work-items/WEB-42/comments" \
-H "Authorization: Bearer fk_live_..." -H "Content-Type: application/json" \
-d '{"body":"Investigating now.","visibility":"INTERNAL"}'Create an issue
curl -X POST "https://your-domain.com/api/v1/work-items" \
-H "Authorization: Bearer fk_live_..." -H "Content-Type: application/json" \
-d '{"projectKey":"WEB","type":"BUG","title":"Checkout fails on Safari"}'Valid values
- Statuses —
GET /boards/projects/WEB→data.statuses[].key. If the workflow lists transitions (GET /workflows), only those moves are accepted; others get400 This workflow transition is not allowed. - Priorities —
GET /priorities:URGENT,HIGH,MEDIUM,LOW. - Assignees —
GET /members→user.id. - Filters on
GET /work-items—projectKey,statusKeys,priorities,assigneeIds(unassignedallowed),types,search,createdFrom/createdTo,sortBy/sortDir. - Rate limit — about 120 requests a minute; a
429means wait and retry.
Hearing about changes
Rather than polling, add a webhook in Settings → Outbound webhooks: Issue created, updated, status changed, assigned, resolved, comment added, and sprint started/completed and release shipped. Each delivery is a JSON POST signed with the webhook's secret in X-Forge-Signature: sha256=…; X-Forge-Delivery is the same on retries, so duplicates can be dropped.
import hashlib, hmac
def is_genuine(raw_body: bytes, signature_header: str, secret: str) -> bool:
expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature_header)Common endpoints
GET
/work-itemsList issuesGET
/work-items/:keyGet an issuePOST
/work-itemsCreate an issuePATCH
/work-items/:keyUpdate status, assignee, priority…GET
/work-items/:key/commentsList commentsPOST
/work-items/:key/commentsAdd a commentGET
/boards/projects/:keyA project's statusesGET
/workflowsAllowed status movesGET
/prioritiesPrioritiesGET
/membersMembersGET
/projectsProjects