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: page from 1 and limit up to 100; data.meta.totalPages says 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 get 400 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 (unassigned allowed), types, search, createdFrom/createdTo, sortBy/sortDir.
  • Rate limit — about 120 requests a minute; a 429 means 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 issues
GET/work-items/:keyGet an issue
POST/work-itemsCreate an issue
PATCH/work-items/:keyUpdate status, assignee, priority…
GET/work-items/:key/commentsList comments
POST/work-items/:key/commentsAdd a comment
GET/boards/projects/:keyA project's statuses
GET/workflowsAllowed status moves
GET/prioritiesPriorities
GET/membersMembers
GET/projectsProjects