Tickets and projects

Every endpoint for projects and tickets, with its arguments, permissions and answers.

All paths are under https://your-workspace.albaticket.com/api/v1 and need a token (see Authentication). Changes need a token with write access and the member's permission in the ticket's project; the permission each needs is given with it.

Who am I

GET /me

The member the token acts for, and the token's name and access. It is a cheap way to check a token.

{
  "user": {"id": "5f0c2a1e-…", "name": "Ada Lovelace"},
  "agent": {"name": "Nightly report", "access": "read"}
}

access is read or write. For an OAuth token, name is the application's.

Projects

GET /projects

The projects the member can see.

{
  "projects": [
    {"key": "WEB", "name": "Website", "kind": "software", "description": "The public site"},
    {"key": "HELP", "name": "Help desk", "kind": "helpdesk", "description": null}
  ]
}

kind is software, helpdesk, business or sales; a sales project's tickets are deals.

GET /projects/{key}

What a project's tickets take: call it before creating or changing tickets to learn the names it accepts.

{
  "key": "WEB",
  "name": "Website",
  "kind": "software",
  "description": "The public site",
  "ticket_types": ["Task", "Bug", "Story", "Epic", "Sub-task"],
  "priorities": ["Highest", "High", "Medium", "Low", "Lowest"],
  "resolutions": ["Done", "Won't Do", "Duplicate", "Cannot Reproduce", "Declined"],
  "components": ["Checkout", "Search"],
  "versions": ["2.4", "2.5"],
  "labels": ["accessibility", "needs-design"],
  "assignable_users": [
    {"id": "5f0c2a1e-…", "name": "Ada Lovelace"},
    {"id": "9b1d77c4-…", "name": "Grace Hopper"}
  ]
}

assignable_users are the people a ticket in this project may be assigned to: members whose role lets them move tickets, and administrators. Components, versions and labels that have been archived are left out.

Finding tickets

GET /tickets

Tickets across every project the member can see, most recently updated first. Every argument is optional.

Argument
query Words to search for in keys, summaries, labels, descriptions, custom fields, attachment names and comments, or a ticket key. See below.
project A project key
status open (anything not done), todo, in_progress or done: the status's category, not its name
assignee me, unassigned, or a person
reporter me, or a person
label A label
type A ticket type's name
sort relevance (the default with a query), updated (the default without), created, priority or key
limit 1 to 100, default 20
offset For paging
GET /tickets?project=WEB&assignee=me&status=open&sort=priority
{
  "total": 2,
  "limit": 20,
  "offset": 0,
  "tickets": [
    {
      "key": "WEB-142",
      "summary": "Checkout button misaligned on Safari",
      "project": "WEB",
      "type": "Bug",
      "status": {"name": "In Progress", "category": "in_progress"},
      "priority": "High",
      "assignee": {"id": "5f0c2a1e-…", "name": "Ada Lovelace"},
      "labels": ["safari"],
      "updated_at": "2026-09-23T14:05:31.412008Z",
      "url": "https://your-workspace.albaticket.com/tickets/WEB-142"
    }
  ]
}

Each ticket in a list is a summary: enough to choose one. GET /tickets/{key} gives the rest.

Searching with query uses the workspace's search engine and matches words and their beginnings. Internal notes are searched (tokens always act for members), and comments restricted to a role or group are searched where the member is in it. A ticket whose key is exactly query is always included. If the workspace's search engine is not running, only an exact key matches.

Without query, the filters alone decide, straight from the database.

Reading a ticket

GET /tickets/{key}

A ticket with everything its page shows, the comments and logged activities the member may read, its links and sub-tasks, and the transitions the member can take from its status now.

{
  "key": "WEB-142",
  "summary": "Checkout button misaligned on Safari",
  "project": "WEB",
  "type": "Bug",
  "status": {"name": "In Progress", "category": "in_progress"},
  "priority": "High",
  "assignee": {"id": "5f0c2a1e-…", "name": "Ada Lovelace"},
  "labels": ["safari"],
  "updated_at": "2026-09-23T14:05:31.412008Z",
  "url": "https://your-workspace.albaticket.com/tickets/WEB-142",
  "description": {"format": "markdown", "text": "On Safari 18 the button overlaps the total."},
  "environment": null,
  "resolution": null,
  "reporter": {"id": "9b1d77c4-…", "name": "Grace Hopper"},
  "parent": "WEB-100",
  "components": ["Checkout"],
  "fix_versions": ["2.5"],
  "affected_versions": ["2.4"],
  "security_level": null,
  "request_type": null,
  "due_date": "2026-10-01",
  "created_at": "2026-09-20T09:12:00.000000Z",
  "resolved_at": null,
  "original_estimate_seconds": 14400,
  "remaining_estimate_seconds": 7200,
  "time_spent_seconds": 3600,
  "comments": [
    {
      "id": "c7e1…",
      "author": {"id": "9b1d77c4-…", "name": "Grace Hopper"},
      "body": {"format": "markdown", "text": "Reproduced on 18.1 too."},
      "visibility": "public",
      "created_at": "2026-09-21T10:00:00.000000Z",
      "edited_at": null
    }
  ],
  "links": [
    {
      "relation": "is blocked by",
      "ticket": "WEB-139",
      "summary": "Update the CSS reset",
      "status": {"name": "Open", "category": "todo"}
    }
  ],
  "subtasks": [],
  "transitions": [
    {"id": "0d6e…", "name": "Send to review", "to_status": {"name": "In Review", "category": "in_progress"}},
    {"id": "4a2b…", "name": "Resolve", "to_status": {"name": "Resolved", "category": "done"}}
  ]
}
Field
description, environment Rich text with its format, or null
parent The parent's key: an epic, or the ticket a sub-task belongs to
security_level The level's name, when the ticket has one
request_type In a helpdesk project, the kind of request
*_estimate_seconds, time_spent_seconds Durations in seconds
comments Oldest first. visibility is public, internal (a helpdesk internal note, never shown to customers) or restricted (to a role or group)
links relation reads from this ticket: "WEB-142 is blocked by WEB-139". Links to tickets the member cannot see are left out
subtasks Summaries, in board order
transitions What POST /tickets/{key}/transitions may take now; empty if the member may not move the ticket
fields The ticket's custom fields that have a value, by key
deal For a ticket in a sales project, to a member who may see the CRM: its company, deal fields and people (see Deals)

Attachments, watchers, votes and history are on the ticket's page (url) and not in the API.

GET /tickets/{key}/transitions

Just the transitions, with the ticket's status:

{
  "key": "WEB-142",
  "status": {"name": "In Progress", "category": "in_progress"},
  "transitions": [
    {"id": "0d6e…", "name": "Send to review", "to_status": {"name": "In Review", "category": "in_progress"}}
  ]
}

Creating a ticket

POST /tickets

Needs ticket:create in the project. Answers 201 with the ticket, shaped as GET /tickets/{key} but without comments, links, subtasks and transitions.

Field
project Required. The project key.
summary Required.
description Markdown
type A ticket type the project uses. Left out, the project's first, or the request type's.
request_type In a helpdesk project, one of its active forms (see Helpdesk forms) by key or name: the ticket is raised as a request of that type.
priority Left out, the workspace's default
assignee A person who can be assigned in the project. Needs ticket:assign; without it the field is ignored rather than refused. Left out, the component's or the project's default assignee.
labels A list of names. New ones are added to the project.
components A list of the project's component names
parent A ticket key in the same project
due_date YYYY-MM-DD
fields Custom fields by key. Checked before the ticket is made, so a mistyped one leaves no ticket behind.
company In a sales project, the deal's company, by name or id
POST /tickets
Content-Type: application/json

{
  "project": "WEB",
  "summary": "Checkout button misaligned on Safari",
  "description": "On Safari 18 the button overlaps the total.\n\n- [ ] Check 17\n- [ ] Check 18",
  "type": "Bug",
  "priority": "High",
  "assignee": "me",
  "labels": ["safari", "needs design"],
  "components": ["Checkout"]
}

The member is the reporter and starts watching the ticket. The ticket starts in its workflow's first status.

Changing a ticket

PATCH /tickets/{key}

Needs ticket:edit, or ticket:edit_own on a ticket the member reported. Send only what should change; a field left out is left alone. Answers with the ticket.

Field
summary
description Markdown. Replaces the whole description.
type Only to a type whose workflow has the ticket's current status
priority
assignee A person, or null to unassign. Needs ticket:assign, otherwise ignored.
labels Replaces the ticket's labels. [] removes them all.
components Replaces the ticket's components
parent A key, or null
due_date YYYY-MM-DD, or null
fields Custom fields by key; only those given change
company For a deal, its company by name or id, or null
PATCH /tickets/WEB-142
Content-Type: application/json

{"priority": "Highest", "labels": ["safari", "regression"]}

All the changes in one request are one entry in the ticket's history, and the custom fields given are a second. To change the status use a transition; the status cannot be set directly.

Custom fields

A ticket's answer gives its custom fields that have a value under fields, by the field's key and as the ticket's page shows them:

"fields": {"deal_value": "12000", "deal_currency": "EUR", "customer_reference": "PO-7781"}

POST /tickets, PATCH /tickets/{key}, a transition and a card move take fields the same way, an object of keys and values. A field that offers choices takes a choice by its words ("EUR"), without regard to case; a field that takes several takes a list; a date is YYYY-MM-DD; a number may be a number or a string; null or "" clears a field. A key the ticket's type has no field for, and a value a field does not take, answer 422 and say which keys or choices there are, so a program can correct itself. A field's key is shown beside its name under Administration, Fields.

Moving a ticket through its workflow

POST /tickets/{key}/transitions

Needs ticket:transition, and a transition the member may take from the ticket's status now, as GET /tickets/{key} lists them. Answers with the ticket.

Field
transition Required. The transition's name or id, or the name of the status it leads to.
resolution For a transition into a done status. Left out, the ticket keeps its resolution or gets the default one.
comment Markdown, added with the move
comment_internal true makes the comment an internal note the customer does not see, as on a helpdesk ticket's page
assignee For a transition whose screen asks for one: me, a name, an email address or an id, among the people the project's tickets can be assigned to
fields For a transition whose screen asks for a custom field, such as a deal's lost_reason: by key. Only the fields the transition asks for are taken.
POST /tickets/WEB-142/transitions
Content-Type: application/json

{"transition": "Resolved", "resolution": "Done", "comment": "Fixed in 2.5."}

A workflow may attach conditions and validators to a transition. A transition the member cannot take now answers 422 with the ones they can in the message; a validator that refuses answers 422 with its reason.

Assigning

PUT /tickets/{key}/assignee

Needs ticket:assign, and ticket:comment for a comment. Answers with the ticket.

Field
assignee Required. A person who can be assigned in the project, or null to unassign.
comment Markdown, added with the assignment and sent to watchers as one notification
PUT /tickets/WEB-142/assignee
Content-Type: application/json

{"assignee": "Grace Hopper", "comment": "Grace knows the checkout CSS."}

A ticket's present assignee is always accepted, even if they could not be assigned today. Anyone else who is not among the project's assignable_users is refused with 422 (not_assignable).

Commenting

POST /tickets/{key}/comments

Needs ticket:comment. Answers 201 with the comment.

Field
body Required. Markdown.
internal true for a helpdesk internal note, which customers never see
POST /tickets/HELP-88/comments
Content-Type: application/json

{"body": "Customer is on the legacy plan; refund approved.", "internal": true}
{
  "id": "e21f…",
  "author": {"id": "5f0c2a1e-…", "name": "Ada Lovelace"},
  "body": {"format": "markdown", "text": "Customer is on the legacy plan; refund approved."},
  "visibility": "internal",
  "created_at": "2026-09-23T14:12:08.000211Z",
  "edited_at": null
}

Mentioning someone as @username notifies them, as in the browser. A comment's time is always when it was made; it cannot be backdated.

Logging time

POST /tickets/{key}/worklogs

Needs ticket:comment and time:log, as logging time on the ticket page does. Answers 201 with the activity (its id is the same row the activities page shows) and the ticket's totals after it. The time adds to the ticket's time_spent_seconds and comes off its remaining_estimate_seconds, never below zero; a ticket with no remaining estimate keeps none.

Field
time_spent Words: 1h 30m, 45m, 2d. A day is 8 hours, a week 5 days.
time_spent_seconds Instead of time_spent, a number of seconds
started_at When the work began, ISO 8601; now when left out
body A note, in Markdown
POST /tickets/WEB-142/worklogs
Content-Type: application/json

{"time_spent": "1h 30m", "body": "Reproduced on Safari 17 and narrowed it to the flex gap."}
{
  "id": "a41c…",
  "author": {"id": "5f0c2a1e-…", "name": "Ada Lovelace"},
  "started_at": "2026-09-24T09:12:08.000211Z",
  "time_spent_seconds": 5400,
  "time_spent": "1h 30m",
  "body": {"format": "markdown", "text": "Reproduced on Safari 17 and narrowed it to the flex gap."},
  "created_at": "2026-09-24T10:42:08.000211Z",
  "edited_at": null,
  "ticket": {"key": "WEB-142", "time_spent_seconds": 5400, "remaining_estimate_seconds": 9000}
}

A duration that is not one (10 months, 0m) is refused with 422 (invalid_duration). Activities the member may read come back on GET /tickets/{key} as worklogs, newest first.

Permissions at a glance

Endpoint Needs
Every GET project:view on the ticket's project, and the ticket's security level
POST /tickets ticket:create
PATCH /tickets/{key} ticket:edit, or ticket:edit_own on a ticket the member reported (and ticket:assign for assignee); fields need the same
POST /tickets/{key}/transitions ticket:transition
PUT /tickets/{key}/assignee ticket:assign (and ticket:comment with a comment)
POST /tickets/{key}/comments ticket:comment
POST /tickets/{key}/worklogs ticket:comment
GET /projects/{key}/request-types project:view
POST, PATCH, DELETE on request types, and their order helpdesk:forms, and helpdesk:publish for a form open to anyone

Permissions come from the member's roles in each project, directly or through a group; installation administrators hold them all. A read-only token holds project:view alone. A workspace that the hosted service has made read-only for non-payment refuses every change, with 403.