The REST API

Read, create and update tickets from another system with an API token.

Settings → API tokens

Read, create and update tickets 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 tickets (filters are optional)

curl "https://your-domain.com/api/v1/work-items?projectKey=SUP&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/SUP-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/SUP-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/SUP-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/SUP-42/comments" \
  -H "Authorization: Bearer fk_live_..." -H "Content-Type: application/json" \
  -d '{"body":"Investigating now.","visibility":"INTERNAL"}'

Create a ticket

curl -X POST "https://your-domain.com/api/v1/work-items" \
  -H "Authorization: Bearer fk_live_..." -H "Content-Type: application/json" \
  -d '{"projectKey":"SUP","type":"SUPPORT_TICKET","title":"Printer offline"}'

Valid values

  • Statuses — GET /boards/projects/SUP → 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: Ticket created, updated, status changed, assigned, resolved, comment added. 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 tickets
GET/work-items/:keyGet a ticket
POST/work-itemsCreate a ticket
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