Skip to content

Tracker SDK

Examples use a client built as tracker = TrackerClient(oauth_token="…", organization_id="…").

TrackerClient

TrackerClient(*, oauth_token: str, organization_id: str, http: HTTPConfig | None = None, transport: BaseTransport | None = None, before_send: BeforeSend | None = None)

Bases: DomainClient

Holds the per-resource tracker clients, all sharing one httpx2 core session.

Examples:

>>> tracker.me.get().login
'alice'

probe

probe() -> None

One cheap authenticated read: the current user.

me

MeClient

MeClient(*, session: SyncSession)

Bases: Resource

The authenticated Tracker user.

get

get() -> Me

GET /myself → the authenticated Me (a safe auth probe).

Returns:

Type Description
Me

The authenticated user.

Examples:

>>> tracker.me.get().login
'alice'

issues

IssuesClient

IssuesClient(*, session: SyncSession)

Bases: Resource

Get, search (paginated), count, create, update, move and suggest Tracker issues.

get

get(key: str, *, expand: str | None = None, fields: str | None = None) -> Issue

GET /issues/{key} → a single Issue.

Parameters:

Name Type Description Default
key str

The issue's key.

required
expand str | None

The extra blocks to include: transitions, attachments, comments.

None
fields str | None

The comma-separated issue fields to include in the reply.

None

Returns:

Type Description
Issue

The issue.

Examples:

>>> tracker.issues.get("DE-7").summary
'Fix the login page'

search

search(body: dict[str, Any], *, limit: int | None = None, expand: str | None = None, scroll_type: str | None = None, per_scroll: int | None = None, scroll_ttl_millis: int | None = None) -> ItemList[Issue]

POST /issues/_search → every matching issue, page by page, at most limit.

body is {"filter": …} or {"query": …}. limit=None fetches every page (up to Tracker's 10 000 results); when the cap leaves issues behind, a warning is logged to ycli.http. scroll_type reads the results by scrolling instead, which has no such cap.

Parameters:

Name Type Description Default
body dict[str, Any]

The search body, {"filter": …} or {"query": …}.

required
limit int | None

The most issues to return; None fetches every page.

None
expand str | None

The extra blocks to include: transitions, attachments, comments.

None
scroll_type str | None

sorted (the order of the search) or unsorted to scroll through the results instead of paging by number.

None
per_scroll int | None

The issues a scroll page holds (1000 at most; the API's default is 100).

None
scroll_ttl_millis int | None

How long the scroll stays open between requests, in milliseconds.

None

Returns:

Type Description
ItemList[Issue]

The matching issues.

Raises:

Type Description
ValueError

If limit is below 1.

Examples:

>>> found = tracker.issues.search(
...     {"filter": {"queue": "DE", "status": "open"}}, limit=500
... )
>>> found.root[0].key
'DE-7'

count

count(body: dict[str, Any]) -> int

POST /issues/_count → the number of matching issues.

Parameters:

Name Type Description Default
body dict[str, Any]

The search body, {"filter": …} or {"query": …}.

required

Returns:

Type Description
int

The number of matching issues.

create

create(body: dict[str, Any], *, notify: bool | None = None) -> Issue

POST /issues/ — create from a ready body; returns the created Issue.

Parameters:

Name Type Description Default
body dict[str, Any]

The issue fields.

required
notify bool | None

Whether to notify the users in the issues' fields; None leaves the API's default (it notifies).

None

Returns:

Type Description
Issue

The created issue.

update

update(key: str, body: dict[str, Any]) -> Issue

PATCH /issues/{key} — update fields; returns the updated Issue.

Parameters:

Name Type Description Default
key str

The issue's key.

required
body dict[str, Any]

The fields to change.

required

Returns:

Type Description
Issue

The updated issue.

move

move(key: str, queue: str, *, expand: str | None = None, initial_status: bool | None = None, move_all_fields: bool | None = None, notify: bool | None = None, notify_author: bool | None = None) -> Issue

POST /issues/{key}/_move?queue=<key> — returns the moved Issue (new key).

Parameters:

Name Type Description Default
key str

The issue's key.

required
queue str

The key of the queue to move the issue to.

required
expand str | None

The extra blocks to include: attachments, comments, workflow, transitions.

None
initial_status bool | None

Whether to reset the status to the new queue's initial one; needed when the queues have different workflows.

None
move_all_fields bool | None

Whether to keep the versions, components and projects the new queue also has; the API clears them by default.

None
notify bool | None

Whether to notify the users in the issues' fields; None leaves the API's default (it notifies).

None
notify_author bool | None

Whether to notify the author of the change; None leaves the API's default (it does not).

None

Returns:

Type Description
Issue

The moved issue.

suggest

suggest(text: str, *, queue: str | None = None, full: bool | None = None, fields: str | None = None, expand: str | None = None, embed: str | None = None) -> ItemList[Issue]

GET /issues/_suggest?input=<text> → issues whose summary contains text.

Parameters:

Name Type Description Default
text str

The text to look for in issue summaries.

required
queue str | None

The key of the queue to search in.

None
full bool | None

Whether to return each issue in full; fields, expand and embed need it.

None
fields str | None

The comma-separated issue fields to include (with full).

None
expand str | None

The extra blocks to include (with full).

None
embed str | None

The blocks named in expand to return in more detail (with full).

None

Returns:

Type Description
ItemList[Issue]

The matching issues.

scroll_clear

scroll_clear(body: dict[str, str]) -> None

Release a search scroll's server resources (POST …/scroll/_clear).

Parameters:

Name Type Description Default
body dict[str, str]

The scroll ids mapped to their scroll tokens.

required

comments

CommentsClient

CommentsClient(*, session: SyncSession)

Bases: Resource

List (relative-paginated), get, add, edit, delete and react to an issue's comments.

list

list(key: str, *, limit: int | None = None, expand: str | None = None) -> ItemList[Comment]

All comments on an issue, draining the id=<last comment id> relative cursor.

GET /issues/{key}/comments returns one page at a time; each next page repeats with id=<id of the last comment seen> until a page comes back empty. Capped at limit (None = every comment); a small cap narrows the page to limit rows.

Parameters:

Name Type Description Default
key str

The issue key.

required
limit int | None

The most comments to return; None returns every comment.

None
expand str | None

The extra blocks to include: attachments, html or all.

None

Returns:

Type Description
ItemList[Comment]

The issue's comments.

Examples:

>>> [comment.text for comment in tracker.comments.list("DE-11", limit=500).root]
['first', 'second', 'third']

get

get(key: str, comment_id: int | str, *, expand: str | None = None) -> Comment

GET /issues/{key}/comments/{comment_id} — one comment. Returns it.

comment_id is the numeric id or the string longId. expand adds attachments, html or all extra fields.

Parameters:

Name Type Description Default
key str

The issue key.

required
comment_id int | str

The comment's numeric id or string longId.

required
expand str | None

The extra fields to include (attachments, html or all).

None

Returns:

Type Description
Comment

The comment.

Examples:

>>> tracker.comments.get("DE-5", 9001, expand="attachments,html").text_html
'<p>My <strong>first</strong> comment</p>'

add

add(key: str, body: dict[str, Any]) -> Comment

POST /issues/{key}/comments/ — add a comment. Returns it.

Parameters:

Name Type Description Default
key str

The issue key.

required
body dict[str, Any]

The new comment: its text and optional summonees and attachment ids.

required

Returns:

Type Description
Comment

The created comment.

Examples:

>>> tracker.comments.add("DE-14", {"text": "Готово ✅"}).id
141

edit

edit(key: str, comment_id: int | str, body: dict[str, Any]) -> Comment

PATCH /issues/{key}/comments/{comment_id} — edit a comment. Returns it.

Parameters:

Name Type Description Default
key str

The issue key.

required
comment_id int | str

The comment's numeric id or string longId.

required
body dict[str, Any]

The comment fields to change.

required

Returns:

Type Description
Comment

The updated comment.

Examples:

>>> tracker.comments.edit("DE-16", "161", {"text": "fixed typo"}).text
'fixed typo'

delete

delete(key: str, comment_id: str) -> None

Delete a comment (DELETE …/comments/{id} → 204). Raises on non-2xx.

Parameters:

Name Type Description Default
key str

The issue key.

required
comment_id str

The comment's id.

required

Examples:

>>> tracker.comments.delete("DE-17", "171")

react

react(key: str, comment_id: int | str, name: str) -> Comment

POST …/comments/{comment_id}/reactions/{name} — add a reaction. Returns the comment.

name is an uppercase reaction key (LIKE, DISLIKE, HEART, ROCKET, FIRE, …).

Parameters:

Name Type Description Default
key str

The issue key.

required
comment_id int | str

The comment's numeric id or string longId.

required
name str

The reaction key.

required

Returns:

Type Description
Comment

The comment the reaction was added to.

Examples:

>>> tracker.comments.react("DE-18", "181", "HEART").id
181

LinksClient

LinksClient(*, session: SyncSession)

Bases: Resource

List, search (paged), add and delete the links between issues.

list

list(key: str) -> ItemList[Link]

GET /issues/{key}/links → link listing.

Parameters:

Name Type Description Default
key str

The issue's key.

required

Returns:

Type Description
ItemList[Link]

The issue's links.

Examples:

>>> tracker.links.list("DE-41").root[0].object_key
'DE-40'

search

search(key: str, *, link_types: Sequence[str] | None = None, fields: Sequence[str] | None = None, limit: int | None = None) -> ItemList[Link]

POST /issues/{key}/links/_list (a read) → links, paged by page/perPage.

link_types keeps only links of these relationships and fields picks the fields to return. Despite the docs calling them type ids, the API takes the relationship phrases of :meth:add (relates, depends on, is subtask for, has epic, …) and answers 400 to a type id such as subtask. Capped at limit (None = every link).

Parameters:

Name Type Description Default
key str

The issue's key.

required
link_types Sequence[str] | None

Keep only links of these relationships.

None
fields Sequence[str] | None

The fields to return for each link.

None
limit int | None

The most links to return; None returns every link.

None

Returns:

Type Description
ItemList[Link]

The matching links.

Examples:

>>> found = tracker.links.search(
...     "DE-44", link_types=["relates", "subtask"], fields=["id", "type"]
... )
>>> [link.id for link in found.root]
[441, 442]

add

add(key: str, body: dict[str, Any]) -> Link

POST /issues/{key}/links — link two issues. Returns the link.

Parameters:

Name Type Description Default
key str

The issue's key.

required
body dict[str, Any]

The link's relationship and the other issue.

required

Returns:

Type Description
Link

The created link.

Examples:

>>> tracker.links.add(
...     "DE-42", {"relationship": "is dependent by", "issue": "OPS-9"}
... ).object_key
'OPS-9'

delete

delete(key: str, link_id: str) -> None

Delete a link (DELETE …/links/{link_id} → 204). Raises on non-2xx.

Parameters:

Name Type Description Default
key str

The issue's key.

required
link_id str

The link's id.

required

Examples:

>>> tracker.links.delete("DE-43", "431")

transitions

TransitionsClient

TransitionsClient(*, session: SyncSession)

Bases: Resource

List an issue's workflow transitions and execute one.

list

list(key: str) -> ItemList[Transition]

GET /issues/{key}/transitions → available transitions.

Parameters:

Name Type Description Default
key str

The issue's key.

required

Returns:

Type Description
ItemList[Transition]

The transitions available for the issue.

Examples:

>>> tracker.transitions.list("DE-51").root[0].id
'close'

execute

execute(key: str, transition_id: str, body: dict[str, Any]) -> ItemList[Transition]

POST /issues/{key}/transitions/{id}/_execute → available transitions after move.

Returns the transitions available for the issue in its new status, parsed as a ItemList[Transition].

Parameters:

Name Type Description Default
key str

The issue's key.

required
transition_id str

The id of the transition to execute.

required
body dict[str, Any]

The transition's fields, such as comment or resolution.

required

Returns:

Type Description
ItemList[Transition]

The transitions available after the move.

Examples:

>>> result = tracker.transitions.execute(
...     "DE-52", "close", {"comment": "done", "resolution": "fixed"}
... )
>>> result.root[0].id
'reopen'

worklog

WorklogClient

WorklogClient(*, session: SyncSession)

Bases: Resource

An issue's worklog (relative-paginated) and its writes; org-wide search and listing.

list

list(key: str, *, limit: int | None = None) -> ItemList[Worklog]

All worklog entries on an issue, draining the id=<last record id> cursor.

GET /issues/{key}/worklog sorts by ascending record id and pages relatively: each next page repeats with id=<id of the last record seen> until a page comes back empty. Capped at limit (None = every entry); a small cap narrows the page to limit rows.

Parameters:

Name Type Description Default
key str

The issue's key.

required
limit int | None

The most entries to return; None returns every entry.

None

Returns:

Type Description
ItemList[Worklog]

The issue's worklog entries, ascending by record id.

Examples:

>>> [entry.duration for entry in tracker.worklog.list("DE-61", limit=500).root]
['PT1H', 'PT2H', 'PT3H']

search

search(body: dict[str, Any]) -> ItemList[Worklog]

POST /worklog/_search → org-wide worklog entries matching the body filter.

body is {"createdBy": …, "createdAt": {"from": …, "to": …}} (all optional).

Parameters:

Name Type Description Default
body dict[str, Any]

The filter on createdBy and createdAt.

required

Returns:

Type Description
ItemList[Worklog]

The matching worklog entries.

Examples:

>>> found = tracker.worklog.search(
...     {
...         "createdBy": "veikus",
...         "createdAt": {"from": "2018-06-06T00:00:00", "to": "2018-06-07T00:00:00"},
...     }
... )
>>> found.root[0].duration
'PT2H'

global_list

global_list(created_by: str | None = None, created_at: Sequence[str] | str | None = None) -> ItemList[Worklog]

GET /worklog?createdBy=…&createdAt=from:…&createdAt=to:… → org-wide worklog.

created_at is a list of from:<ts> / to:<ts> strings (repeated createdAt query params). Distinct from :meth:list, which is scoped to a single issue.

Parameters:

Name Type Description Default
created_by str | None

Only entries created by this user.

None
created_at Sequence[str] | str | None

from:<ts> / to:<ts> strings bounding the creation time.

None

Returns:

Type Description
ItemList[Worklog]

The organisation's matching worklog entries.

Examples:

>>> tracker.worklog.global_list(
...     created_by="alice", created_at=["from:2019-01-01", "to:2019-02-01"]
... ).root[0].duration
'P3W'

create

create(key: str, body: dict[str, Any]) -> Worklog

POST /issues/{key}/worklog — log time spent. Returns the created entry.

Parameters:

Name Type Description Default
key str

The issue's key.

required
body dict[str, Any]

The entry's duration, and optionally start and comment.

required

Returns:

Type Description
Worklog

The created entry.

Examples:

>>> tracker.worklog.create("DE-66", {"duration": "PT2H", "comment": "pairing"}).duration
'PT2H'

edit

edit(key: str, record_id: int | str, body: dict[str, Any]) -> Worklog

PATCH /issues/{key}/worklog/{record_id} — edit an entry. Returns it.

Parameters:

Name Type Description Default
key str

The issue's key.

required
record_id int | str

The entry's id.

required
body dict[str, Any]

The fields to change.

required

Returns:

Type Description
Worklog

The edited entry.

Examples:

>>> tracker.worklog.edit("DE-67", "671", {"duration": "PT45M"}).duration
'PT45M'

delete

delete(key: str, record_id: str) -> None

Delete a worklog entry (DELETE …/worklog/{id} → 204). Raises on non-2xx.

Parameters:

Name Type Description Default
key str

The issue's key.

required
record_id str

The entry's id.

required

Examples:

>>> tracker.worklog.delete("DE-68", "681")

changelog

ChangelogClient

ChangelogClient(*, session: SyncSession)

Bases: Resource

The change history of an issue (relative-paginated).

list

list(key: str, *, limit: int | None = None, field: str | None = None, change_type: str | None = None, sort: str | None = None) -> ItemList[ChangelogEntry]

All changelog events on an issue, draining the id=<last change id> cursor.

GET /issues/{key}/changelog returns one page at a time; each next page repeats with id=<id of the last change seen> until a page comes back empty. Capped at limit (None = the full history); a small cap narrows the page to limit rows.

Parameters:

Name Type Description Default
key str

The issue key.

required
limit int | None

The most events to return; None returns the full history.

None
field str | None

Keep the changes of this field, e.g. status or checklistItems.

None
change_type str | None

Keep the changes of this type, e.g. IssueWorkflow.

None
sort str | None

The order of the changes: asc or desc.

None

Returns:

Type Description
ItemList[ChangelogEntry]

The changelog events.

Examples:

>>> [change.id for change in tracker.changelog.list("DE-21", limit=500).root]
['ch1', 'ch2', 'ch3']

checklists

ChecklistsClient

ChecklistsClient(*, session: SyncSession)

Bases: Resource

Get, add, edit and delete an issue's checklist items, or clear the whole checklist.

get

get(key: str) -> ItemList[ChecklistItem]

GET /issues/{key}/checklistItems → the issue's checklist items.

Parameters:

Name Type Description Default
key str

The issue key.

required

Returns:

Type Description
ItemList[ChecklistItem]

The issue's checklist items.

Examples:

>>> tracker.checklists.get("DE-31").root[0].text
'Review the PR'

create

create(key: str, body: dict[str, Any]) -> Checklist

POST /issues/{key}/checklistItems — add an item. Returns the issue wrapper.

Parameters:

Name Type Description Default
key str

The issue key.

required
body dict[str, Any]

The new item: its text and optional checked flag, assignee and deadline.

required

Returns:

Type Description
Checklist

The issue wrapper with the updated checklist.

Examples:

>>> tracker.checklists.create("DE-32", {"text": "step 1"}).key
'DE-32'

edit

edit(key: str, item_id: str, body: dict[str, Any]) -> Checklist

PATCH /issues/{key}/checklistItems/{item_id} — edit an item. Returns the wrapper.

Parameters:

Name Type Description Default
key str

The issue key.

required
item_id str

The checklist item's id.

required
body dict[str, Any]

The item fields to change.

required

Returns:

Type Description
Checklist

The issue wrapper with the updated checklist.

Examples:

>>> tracker.checklists.edit("DE-34", "5f4", {"text": "step 2"}).key
'DE-34'

delete

delete(key: str, item_id: str) -> Checklist

DELETE /issues/{key}/checklistItems/{item_id} — remove one item (200 + wrapper).

Parameters:

Name Type Description Default
key str

The issue key.

required
item_id str

The checklist item's id.

required

Returns:

Type Description
Checklist

The issue wrapper with the remaining checklist.

Examples:

>>> tracker.checklists.delete("DE-36", "5f6").checklist_items[0].text
'left'

clear

clear(key: str) -> Checklist

DELETE /issues/{key}/checklistItems — remove the whole checklist (200 + wrapper).

Parameters:

Name Type Description Default
key str

The issue key.

required

Returns:

Type Description
Checklist

The issue wrapper with an empty checklist.

Examples:

>>> tracker.checklists.clear("DE-37").checklist_items
[]

columns

ColumnsClient

ColumnsClient(*, session: SyncSession)

Bases: Resource

List, get, create, edit and delete the columns of an agile board.

list

list(board_id: int) -> ItemList[Column]

GET /boards/{board_id}/columns → the board's column listing.

Parameters:

Name Type Description Default
board_id int

The board's id.

required

Returns:

Type Description
ItemList[Column]

The board's columns.

Examples:

>>> tracker.columns.list(73).root[0].name
'Open'

get

get(board_id: int, column_id: int) -> Column

GET /boards/{board_id}/columns/{column_id} → a single board column.

Parameters:

Name Type Description Default
board_id int

The board's id.

required
column_id int

The column's id.

required

Returns:

Type Description
Column

The column.

Examples:

>>> tracker.columns.get(74, 2).name
'Review'

create

create(board_id: int, body: ColumnCreate) -> Column

Create a board column from a typed ColumnCreate body. Returns the new Column.

Parameters:

Name Type Description Default
board_id int

The board's id.

required
body ColumnCreate

The new column's settings.

required

Returns:

Type Description
Column

The created column.

Examples:

>>> from ycli.yandex.tracker.columns.models import ColumnCreate
>>> tracker.columns.create(
...     75, ColumnCreate(name="Approve", statuses=["needInfo", "adjustment"])
... ).id
5

edit

edit(board_id: int, column_id: int, body: ColumnUpdate) -> Column

Edit a board column from a typed ColumnUpdate body. Returns the updated Column.

Only the fields set on body are sent, so omitted fields stay unchanged.

Parameters:

Name Type Description Default
board_id int

The board's id.

required
column_id int

The column's id.

required
body ColumnUpdate

The fields to change.

required

Returns:

Type Description
Column

The updated column.

Examples:

>>> from ycli.yandex.tracker.columns.models import ColumnUpdate
>>> tracker.columns.edit(76, 6, ColumnUpdate(name="Pause")).name
'Pause'

delete

delete(board_id: int, column_id: int) -> None

DELETE /boards/{board_id}/columns/{column_id} — delete a column (204, no body).

Parameters:

Name Type Description Default
board_id int

The board's id.

required
column_id int

The column's id.

required

Examples:

>>> tracker.columns.delete(78, 8)

priorities

PrioritiesClient

PrioritiesClient(*, session: SyncSession)

Bases: Resource

List, create and edit issue priorities.

list

list(*, localized: bool | None = None) -> ItemList[Priority]

GET /priorities → priority listing.

Parameters:

Name Type Description Default
localized bool | None

False returns the names in every language; the API's default is the caller's language only.

None

Returns:

Type Description
ItemList[Priority]

The priorities.

Examples:

>>> tracker.priorities.list().root[0].key
'normal'

create

create(body: PriorityCreate) -> Priority

Create a priority from a typed PriorityCreate body. Returns the new Priority.

Parameters:

Name Type Description Default
body PriorityCreate

The new priority's key, localized name and order.

required

Returns:

Type Description
Priority

The created priority.

Examples:

>>> from ycli.yandex.tracker.models import LocalizedName
>>> from ycli.yandex.tracker.priorities.models import PriorityCreate
>>> tracker.priorities.create(
...     PriorityCreate(key="one", name=LocalizedName(ru="Низкий", en="Low"), order=60)
... ).key
'one'

edit

edit(priority_id: str, body: PriorityUpdate, *, version: int | None = None) -> Priority

Edit priority priority_id from a typed PriorityUpdate body.

version is the current priority version; when set it is sent as ?version= for optimistic locking (the API rejects a stale version with 409).

Parameters:

Name Type Description Default
priority_id str

The priority's key or id.

required
body PriorityUpdate

The fields to change.

required
version int | None

The current priority version, sent as ?version=; None sends none.

None

Returns:

Type Description
Priority

The updated priority.

Examples:

>>> from ycli.yandex.tracker.priorities.models import PriorityUpdate
>>> tracker.priorities.edit(
...     "blocker", PriorityUpdate(description="Stops all"), version=7
... ).key
'blocker'

issuetypes

IssueTypesClient

IssueTypesClient(*, session: SyncSession)

Bases: Resource

List, create and edit issue types.

list

list() -> ItemList[IssueType]

GET /issuetypes → issue-type listing.

Returns:

Type Description
ItemList[IssueType]

The issue types.

Examples:

>>> tracker.issuetypes.list().root[0].key
'bug'

create

create(body: IssueTypeCreate) -> IssueType

Create an issue type from a typed IssueTypeCreate body. Returns the IssueType.

Parameters:

Name Type Description Default
body IssueTypeCreate

The new issue type's key and localized name.

required

Returns:

Type Description
IssueType

The created issue type.

Examples:

>>> from ycli.yandex.tracker.models import LocalizedName
>>> from ycli.yandex.tracker.issuetypes.models import IssueTypeCreate
>>> tracker.issuetypes.create(
...     IssueTypeCreate(key="client", name=LocalizedName(ru="Клиент", en="Client"))
... ).key
'client'

edit

edit(issue_type_id: str, body: IssueTypeUpdate, *, version: int | None = None) -> IssueType

Edit issue type issue_type_id from a typed IssueTypeUpdate body.

version is the current issue-type version; when set it is sent as ?version= for optimistic locking (the API rejects a stale version with 409).

Parameters:

Name Type Description Default
issue_type_id str

The issue type's id.

required
body IssueTypeUpdate

The fields to change.

required
version int | None

The current issue-type version, sent as ?version=; None sends none.

None

Returns:

Type Description
IssueType

The updated issue type.

Examples:

>>> from ycli.yandex.tracker.models import LocalizedName
>>> from ycli.yandex.tracker.issuetypes.models import IssueTypeUpdate
>>> tracker.issuetypes.edit(
...     "23",
...     IssueTypeUpdate(name=LocalizedName(ru="Покупатель", en="Buyer")),
...     version=2,
... ).key
'client'

linktypes

LinkTypesClient

LinkTypesClient(*, session: SyncSession)

Bases: Resource

List the kinds of links between issues.

list

list() -> ItemList[LinkType]

GET /linktypes → link-type listing.

Returns:

Type Description
ItemList[LinkType]

The link types.

Examples:

>>> tracker.linktypes.list().root[0].id
'relates'

users

UsersClient

UsersClient(*, session: SyncSession)

Bases: Resource

Get one user; list every user over the relative id cursor.

get

get(login_or_id: str, expand: str | None = None) -> User

GET /users/{login_or_id} → one user account.

expand=groups adds the user's groups. For a numeric login use login:<digits>.

Parameters:

Name Type Description Default
login_or_id str

The user's login or numeric id.

required
expand str | None

Extra blocks to include; groups adds the user's groups.

None

Returns:

Type Description
User

The user.

Examples:

>>> tracker.users.get("username", expand="groups").display
'Ivan Ivanov'

list

list(*, limit: int | None = None, expand: str | None = None) -> ItemList[User]

All organisation users, draining the id=<last uid> relative cursor internally.

Users come back sorted by ascending uid; each next page repeats with id=<uid of the last user seen>. Capped at limit (None = every user).

Parameters:

Name Type Description Default
limit int | None

The most users to return; None returns every user.

None
expand str | None

Extra blocks to include, as in :meth:get.

None

Returns:

Type Description
ItemList[User]

The organisation's users, ascending by uid.

Examples:

>>> [user.uid for user in tracker.users.list(limit=500, expand="groups").root]
[1, 2, 3]

statuses

StatusesClient

StatusesClient(*, session: SyncSession)

Bases: Resource

List, create and edit issue statuses.

list

list() -> ItemList[Status]

GET /statuses → status listing.

Returns:

Type Description
ItemList[Status]

The statuses.

Examples:

>>> tracker.statuses.list().root[0].key
'open'

create

create(body: StatusCreate) -> Status

Create an issue status from a typed StatusCreate body. Returns the new Status.

Parameters:

Name Type Description Default
body StatusCreate

The new status's key, localized name and type.

required

Returns:

Type Description
Status

The created status.

Examples:

>>> from ycli.yandex.tracker.models import LocalizedName
>>> from ycli.yandex.tracker.statuses.models import StatusCreate
>>> tracker.statuses.create(
...     StatusCreate(
...         key="pause", name=LocalizedName(ru="Пауза", en="Paused"), type="paused"
...     )
... ).key
'pause'

edit

edit(status_id: str, body: StatusUpdate, *, version: int | None = None) -> Status

Edit status status_id from a typed StatusUpdate body. Returns the Status.

version is the current status version; when set it is sent as ?version= for optimistic locking (the API rejects a stale version with 409).

Parameters:

Name Type Description Default
status_id str

The status's key or id.

required
body StatusUpdate

The fields to change.

required
version int | None

The current status version, sent as ?version=; None sends none.

None

Returns:

Type Description
Status

The updated status.

Examples:

>>> from ycli.yandex.tracker.statuses.models import StatusUpdate
>>> tracker.statuses.edit(
...     "29", StatusUpdate(description="Issue is paused"), version=5
... ).version
6

resolutions

ResolutionsClient

ResolutionsClient(*, session: SyncSession)

Bases: Resource

List, create and edit issue resolutions.

list

list() -> ItemList[Resolution]

GET /resolutions → resolution listing.

Returns:

Type Description
ItemList[Resolution]

The resolutions.

Examples:

>>> tracker.resolutions.list().root[0].key
'fixed'

create

create(body: ResolutionCreate) -> Resolution

Create a resolution from a typed ResolutionCreate body. Returns the Resolution.

Parameters:

Name Type Description Default
body ResolutionCreate

The new resolution's key and localized name.

required

Returns:

Type Description
Resolution

The created resolution.

Examples:

>>> from ycli.yandex.tracker.models import LocalizedName
>>> from ycli.yandex.tracker.resolutions.models import ResolutionCreate
>>> tracker.resolutions.create(
...     ResolutionCreate(
...         key="wontFix", name=LocalizedName(ru="Отклонено", en="Won't fix")
...     )
... ).key
'wontFix'

edit

edit(resolution_id: str, body: ResolutionUpdate, *, version: int | None = None) -> Resolution

Edit resolution resolution_id from a typed ResolutionUpdate body.

version is the current resolution version; when set it is sent as ?version= for optimistic locking (the API rejects a stale version with 409).

Parameters:

Name Type Description Default
resolution_id str

The resolution's key or id.

required
body ResolutionUpdate

The fields to change.

required
version int | None

The current resolution version, sent as ?version=; None sends none.

None

Returns:

Type Description
Resolution

The updated resolution.

Examples:

>>> from ycli.yandex.tracker.resolutions.models import ResolutionUpdate
>>> tracker.resolutions.edit(
...     "9", ResolutionUpdate(description="Won't be fixed"), version=3
... ).version
4

queues

QueuesClient

QueuesClient(*, session: SyncSession)

Bases: Resource

List (page-paginated), get, create, delete and restore queues; tags, versions, access.

list

list(*, limit: int | None = None, expand: str | None = None) -> ItemList[Queue]

GET /queues/ → flat ItemList[Queue], draining page/perPage internally.

Capped at limit (None = every queue). The API returns 50 queues per page; this advances the page number up to X-Total-Pages, or until a short page comes back.

Parameters:

Name Type Description Default
limit int | None

The most queues to return; None returns every queue.

None
expand str | None

The extra blocks to include in each queue, as in :meth:get.

None

Returns:

Type Description
ItemList[Queue]

The queues, across all pages.

Examples:

>>> tracker.queues.list(limit=500).root[-1].key
'TAIL'

get

get(queue_id: str, expand: str | None = None) -> Queue

GET /queues/{queue_id} → a single :class:Queue.

queue_id is the queue key (case-sensitive) or numeric id. Pass expand to include extra blocks, e.g. expand="all" (or projects,components,versions,types,team, workflows,fields,issueTypesConfig).

Parameters:

Name Type Description Default
queue_id str

The queue's key or numeric id.

required
expand str | None

Extra blocks to include; None includes none.

None

Returns:

Type Description
Queue

The queue.

Examples:

>>> tracker.queues.get("TEST", expand="all").key
'TEST'

tags

tags(queue_id: str) -> ItemList[str]

GET /queues/{queue_id}/tags → the queue's tag names as a flat string array.

Parameters:

Name Type Description Default
queue_id str

The queue's key or numeric id.

required

Returns:

Type Description
ItemList[str]

The queue's tag names.

Examples:

>>> tracker.queues.tags("TAGQ").root
['tag1', 'tag2']

versions

versions(queue_id: str) -> ItemList[QueueVersionInfo]

GET /queues/{queue_id}/versions → the queue's versions.

Parameters:

Name Type Description Default
queue_id str

The queue's key or numeric id.

required

Returns:

Type Description
ItemList[QueueVersionInfo]

The queue's versions.

Examples:

>>> tracker.queues.versions("VERQ").root[0].name
'v0.1'

fields

fields(queue_id: str) -> ItemList[QueueField]

GET /queues/{queue_id}/fields → the queue's required/local fields.

Parameters:

Name Type Description Default
queue_id str

The queue's key or numeric id.

required

Returns:

Type Description
ItemList[QueueField]

The queue's fields.

Examples:

>>> tracker.queues.fields("FLDQ").root[0].id
'myfield'

create

create(body: QueueCreate) -> Queue

Create a queue from a typed QueueCreate body. Returns the created Queue.

Parameters:

Name Type Description Default
body QueueCreate

The new queue's key, name, lead and defaults.

required

Returns:

Type Description
Queue

The created queue.

Examples:

>>> from ycli.yandex.tracker.queues.models import QueueCreate
>>> new_queue = QueueCreate(
...     key="DESIGN",
...     name="Design",
...     lead="lead-login",
...     default_type="task",
...     default_priority="normal",
... )
>>> tracker.queues.create(new_queue).key
'DESIGN'

delete

delete(queue_id: str) -> None

DELETE /queues/{queue_id} — delete a queue (204, empty body).

Parameters:

Name Type Description Default
queue_id str

The queue's key or numeric id.

required

Examples:

>>> tracker.queues.delete("GONE")

restore

restore(queue_id: str) -> Queue

POST /queues/{queue_id}/_restore — restore a deleted queue (admin only).

Returns the restored Queue.

Parameters:

Name Type Description Default
queue_id str

The queue's key or numeric id.

required

Returns:

Type Description
Queue

The restored queue.

Examples:

>>> tracker.queues.restore("BACK").key
'BACK'

set_permissions

set_permissions(queue_id: str, body: QueuePermissionsUpdate) -> QueuePermissions

Manage queue access from a typed QueuePermissionsUpdate body.

Returns the queue's effective QueuePermissions after the change.

Parameters:

Name Type Description Default
queue_id str

The queue's key or numeric id.

required
body QueuePermissionsUpdate

The permission changes, per category.

required

Returns:

Type Description
QueuePermissions

The queue's permissions after the change.

Examples:

>>> from ycli.yandex.tracker.queues.models import (
...     QueuePermissionScope,
...     QueuePermissionsUpdate,
... )
>>> change = QueuePermissionsUpdate(create=QueuePermissionScope(roles=["author"]))
>>> tracker.queues.set_permissions("PERM", change).version
11

tag_remove

tag_remove(queue_id: str, body: QueueTagRemove) -> None

Remove a tag from a queue (admin only; 204, empty body).

Parameters:

Name Type Description Default
queue_id str

The queue's key or numeric id.

required
body QueueTagRemove

The tag to remove.

required

Examples:

>>> from ycli.yandex.tracker.queues.models import QueueTagRemove
>>> tracker.queues.tag_remove("TAGGED", QueueTagRemove(tag="obsolete"))

version_create

version_create(body: QueueVersionCreate) -> QueueVersionInfo

Create a queue version from a typed QueueVersionCreate body.

Returns the created QueueVersionInfo.

Parameters:

Name Type Description Default
body QueueVersionCreate

The new version's queue and name.

required

Returns:

Type Description
QueueVersionInfo

The created version.

Examples:

>>> from ycli.yandex.tracker.queues.models import QueueVersionCreate
>>> tracker.queues.version_create(QueueVersionCreate(queue="RELQ", name="v2.0")).name
'v2.0'

version_get

version_get(version_id: int, *, fields: str | None = None) -> QueueVersionInfo

GET /versions/{version_id} → one queue version.

fields is a comma list of the fields to return (name,dueDate,released, …).

Parameters:

Name Type Description Default
version_id int

The version's id.

required
fields str | None

A comma list of the fields to return; None returns the defaults.

None

Returns:

Type Description
QueueVersionInfo

The version.

Examples:

>>> tracker.queues.version_get(901, fields="name,dueDate,released").name
'Release 1.0'

version_edit

version_edit(version_id: int, body: QueueVersionUpdate, *, fields: str | None = None) -> QueueVersionInfo

PATCH /versions/{version_id} → change the set fields of a version.

Parameters:

Name Type Description Default
version_id int

The version's id.

required
body QueueVersionUpdate

The fields to change.

required
fields str | None

A comma list of the fields to return; None returns the defaults.

None

Returns:

Type Description
QueueVersionInfo

The updated version.

Examples:

>>> from ycli.yandex.tracker.queues.models import QueueVersionUpdate
>>> tracker.queues.version_edit(
...     903, QueueVersionUpdate(name="Release 1.1"), fields="name,description"
... ).version
2

version_delete

version_delete(version_id: int) -> None

DELETE /versions/{version_id} → 204; raises on non-2xx.

Parameters:

Name Type Description Default
version_id int

The version's id.

required

Examples:

>>> tracker.queues.version_delete(905)

user_permissions

user_permissions(queue_id: str, user_id: str) -> QueueUserAccess

GET /queues/{queue_id}/permissions/users/{user_id} → what a user may do in a queue.

user_id is a login or a numeric uid.

Parameters:

Name Type Description Default
queue_id str

The queue's key or numeric id.

required
user_id str

The user's login or numeric uid.

required

Returns:

Type Description
QueueUserAccess

The user's rights in the queue.

Examples:

>>> tracker.queues.user_permissions("PERMQ", "carol").user.display
'Carol'

group_permissions

group_permissions(queue_id: str, group_id: int) -> QueueGroupAccess

GET /queues/{queue_id}/permissions/groups/{group_id} → what a group may do.

Parameters:

Name Type Description Default
queue_id str

The queue's key or numeric id.

required
group_id int

The group's id.

required

Returns:

Type Description
QueueGroupAccess

The group's rights in the queue.

Examples:

>>> tracker.queues.group_permissions("PERMG", 77).group.display
'Editors'

localfields

LocalFieldsClient

LocalFieldsClient(*, session: SyncSession)

Bases: Resource

List, get, create and edit a queue's local (queue-scoped custom) fields.

list

list(queue_id: str) -> ItemList[LocalField]

GET /queues/{queue_id}/localFields → the queue's local fields.

queue_id is the queue key (case-sensitive) or numeric id. Local fields are custom fields scoped to a single queue; the response is a bare JSON array.

Parameters:

Name Type Description Default
queue_id str

The queue's key or numeric id.

required

Returns:

Type Description
ItemList[LocalField]

The queue's local fields.

Examples:

>>> tracker.localfields.list("ORG").root[0].key
'loc_field_key'

get

get(queue_id: str, field_key: str) -> LocalField

GET /queues/{queue_id}/localFields/{field_key} → one local field.

field_key is the field key returned by :meth:list.

Parameters:

Name Type Description Default
queue_id str

The queue's key or numeric id.

required
field_key str

The field's key.

required

Returns:

Type Description
LocalField

The local field.

Examples:

>>> tracker.localfields.get("OPS", "deadline_note").name
'Deadline note'

create

create(queue_id: str, body: FieldCreate) -> LocalField

Create a local field in queue queue_id from a typed FieldCreate body.

Parameters:

Name Type Description Default
queue_id str

The queue's key or numeric id.

required
body FieldCreate

The new field's name, id, category and type.

required

Returns:

Type Description
LocalField

The created local field.

Examples:

>>> from ycli.yandex.tracker.models import FieldCreate, LocalizedName
>>> new_field = FieldCreate(
...     name=LocalizedName(ru="Поле", en="Field"),
...     id="loc_new",
...     category="cat-3",
...     type="ru.yandex.startrek.core.fields.StringFieldType",
... )
>>> tracker.localfields.create("DEV", new_field).key
'loc_new'

edit

edit(queue_id: str, field_key: str, body: LocalFieldUpdate) -> LocalField

Edit local field field_key of queue queue_id from a typed LocalFieldUpdate.

This endpoint has no ?version= optimistic lock; only the fields set on body are sent, so omitted fields stay unchanged.

Parameters:

Name Type Description Default
queue_id str

The queue's key or numeric id.

required
field_key str

The field's key.

required
body LocalFieldUpdate

The fields to change.

required

Returns:

Type Description
LocalField

The updated local field.

Examples:

>>> from ycli.yandex.tracker.localfields.models import LocalFieldUpdate
>>> tracker.localfields.edit("SUP", "loc_edit", LocalFieldUpdate(order=102)).order
102

fields

FieldsClient

FieldsClient(*, session: SyncSession)

Bases: Resource

List, get, create and edit global fields; create and edit their categories.

list

list() -> ItemList[CustomField]

GET /fields → all global fields of the organisation.

Returns:

Type Description
ItemList[CustomField]

The global fields.

Examples:

>>> tracker.fields.list().root[0].id
'ruName'

get

get(field_id: str) -> CustomField

GET /fields/{field_id} → parameters of one issue field.

Parameters:

Name Type Description Default
field_id str

The field's id.

required

Returns:

Type Description
CustomField

The field.

Examples:

>>> tracker.fields.get("enName").id
'enName'

create

create(body: FieldCreate) -> CustomField

Create a global field from a typed FieldCreate body. Returns the CustomField.

Parameters:

Name Type Description Default
body FieldCreate

The new field's settings.

required

Returns:

Type Description
CustomField

The created field.

Examples:

>>> from ycli.yandex.tracker.models import FieldCreate, LocalizedName
>>> tracker.fields.create(
...     FieldCreate(
...         name=LocalizedName(ru="Поле"),
...         id="myField",
...         category="cat-1",
...         type="ru.yandex.startrek.core.fields.StringFieldType",
...     )
... ).id
'myField'

edit

edit(field_id: str, body: FieldUpdate, *, version: int | None = None) -> CustomField

Edit field field_id from a typed FieldUpdate body (rename and/or options).

version is the current field version; when set it is sent as ?version= for optimistic locking (the API rejects a stale version).

Parameters:

Name Type Description Default
field_id str

The field's id.

required
body FieldUpdate

The fields to change.

required
version int | None

The field's current version, for optimistic locking.

None

Returns:

Type Description
CustomField

The updated field.

Examples:

>>> from ycli.yandex.tracker.models import LocalizedName
>>> from ycli.yandex.tracker.fields.models import FieldUpdate
>>> tracker.fields.edit(
...     "ruName", FieldUpdate(name=LocalizedName(ru="Имя")), version=3
... ).id
'ruName'

category_create

category_create(body: FieldCategoryCreate) -> FieldCategoryRecord

Create a field category from a typed FieldCategoryCreate body.

Parameters:

Name Type Description Default
body FieldCategoryCreate

The new category's settings.

required

Returns:

Type Description
FieldCategoryRecord

The created category.

Examples:

>>> from ycli.yandex.tracker.models import LocalizedName
>>> from ycli.yandex.tracker.fields.models import FieldCategoryCreate
>>> tracker.fields.category_create(
...     FieldCategoryCreate(name=LocalizedName(ru="Своя"), order=400)
... ).id
'604f99'

category_edit

category_edit(category_id: str, body: FieldCategoryUpdate, *, version: int | None = None) -> FieldCategoryRecord

Edit field category category_id from a typed FieldCategoryUpdate body.

version is the current category version; when set it is sent as ?version= for optimistic locking.

Parameters:

Name Type Description Default
category_id str

The category's id.

required
body FieldCategoryUpdate

The fields to change.

required
version int | None

The category's current version, for optimistic locking.

None

Returns:

Type Description
FieldCategoryRecord

The updated category.

Examples:

>>> from ycli.yandex.tracker.fields.models import FieldCategoryUpdate
>>> tracker.fields.category_edit(
...     "604f99", FieldCategoryUpdate(order=500), version=1
... ).version
2

components

ComponentsClient

ComponentsClient(*, session: SyncSession)

Bases: Resource

List, get, create, edit and delete queue components; read who may use them.

list

list() -> ItemList[Component]

GET /components → all components created by the organisation's users.

Returns:

Type Description
ItemList[Component]

The components.

Examples:

>>> tracker.components.list().root[0].name
'Backend'

create

create(body: ComponentCreate) -> Component

Create a component from a typed ComponentCreate body. Returns the Component.

Parameters:

Name Type Description Default
body ComponentCreate

The new component's settings.

required

Returns:

Type Description
Component

The created component.

Examples:

>>> from ycli.yandex.tracker.components.models import ComponentCreate
>>> tracker.components.create(ComponentCreate(name="UI", queue="WEB")).id
111175

edit

edit(component_id: int, body: ComponentUpdate, *, version: int | None = None) -> Component

Edit component component_id from a typed ComponentUpdate body.

version is the current component version; when set it is sent as ?version= for optimistic locking (the API rejects a stale version with 409).

Parameters:

Name Type Description Default
component_id int

The component's id.

required
body ComponentUpdate

The fields to change.

required
version int | None

The component's current version, for optimistic locking.

None

Returns:

Type Description
Component

The updated component.

Examples:

>>> from ycli.yandex.tracker.components.models import ComponentUpdate
>>> tracker.components.edit(111175, ComponentUpdate(name="Web UI"), version=4).version
5

list_for_queue

list_for_queue(queue_id: str, *, fields: str | None = None) -> ItemList[Component]

GET /queues/{queue_id}/components → the components of one queue.

fields is a comma list of extra fields (version,description,lead,assignAuto); self, id, name and queue always come back.

Parameters:

Name Type Description Default
queue_id str

The queue's key or id.

required
fields str | None

The extra fields to include, comma-separated.

None

Returns:

Type Description
ItemList[Component]

The queue's components.

Examples:

>>> tracker.components.list_for_queue("COMPQ", fields="version,description").root[
...     0
... ].name
'Frontend'

get

get(component_id: int, *, fields: str | None = None) -> Component

GET /components/{component_id} → one component.

Parameters:

Name Type Description Default
component_id int

The component's id.

required
fields str | None

The extra fields to include, comma-separated.

None

Returns:

Type Description
Component

The component.

Examples:

>>> tracker.components.get(125, fields="name,lead,assignAuto").name
'Backend'

delete

delete(component_id: int) -> None

DELETE /components/{component_id} → 204; raises on non-2xx.

Parameters:

Name Type Description Default
component_id int

The component's id.

required

Examples:

>>> tracker.components.delete(127)

user_permissions

user_permissions(component_id: int, user_id: str) -> ComponentUserAccess

GET /components/{id}/permissions/users/{user_id} → a user's rights on a component.

user_id is a login or a numeric uid.

Parameters:

Name Type Description Default
component_id int

The component's id.

required
user_id str

The user's login or numeric uid.

required

Returns:

Type Description
ComponentUserAccess

The user's rights on the component.

Examples:

>>> tracker.components.user_permissions(128, "dan").user.display
'Dan'

group_permissions

group_permissions(component_id: int, group_id: int) -> ComponentGroupAccess

GET /components/{id}/permissions/groups/{group_id} → a group's rights on it.

Parameters:

Name Type Description Default
component_id int

The component's id.

required
group_id int

The group's id.

required

Returns:

Type Description
ComponentGroupAccess

The group's rights on the component.

Examples:

>>> tracker.components.group_permissions(129, 88).group.display
'Reviewers'

filters

FiltersClient

FiltersClient(*, session: SyncSession)

Bases: Resource

Get, create, edit and delete saved issue filters.

get

get(filter_id: str) -> Filter

GET /filters/{filter_id} → parameters of one saved filter.

Parameters:

Name Type Description Default
filter_id str

The filter's id.

required

Returns:

Type Description
Filter

The saved filter.

Examples:

>>> tracker.filters.get("12345").name
'My open issues'

create

create(body: FilterCreate) -> Filter

Create a saved filter from a typed FilterCreate body. Returns the Filter.

Parameters:

Name Type Description Default
body FilterCreate

The new filter's name, query and filter object.

required

Returns:

Type Description
Filter

The created filter.

Examples:

>>> from ycli.yandex.tracker.filters.models import FilterCreate
>>> tracker.filters.create(FilterCreate(name="My open", filter={"status": "open"})).id
12346

edit

edit(filter_id: str, body: FilterUpdate) -> Filter

Edit filter filter_id from a typed FilterUpdate body. Returns the Filter.

This endpoint has no ?version= optimistic lock; the filter object is replaced wholesale rather than merged.

Parameters:

Name Type Description Default
filter_id str

The filter's id.

required
body FilterUpdate

The fields to change.

required

Returns:

Type Description
Filter

The updated filter.

Examples:

>>> from ycli.yandex.tracker.filters.models import FilterUpdate
>>> tracker.filters.edit("12347", FilterUpdate(name="Renamed")).name
'Renamed'

delete

delete(filter_id: str) -> None

DELETE /filters/{filter_id} → 204; raises on non-2xx.

The docs name the /v2/filters/{id} route; the /v3/ one used by every other filter call deletes it too.

Parameters:

Name Type Description Default
filter_id str

The filter's id.

required

Examples:

>>> tracker.filters.delete("12349")

applications

ApplicationsClient

ApplicationsClient(*, session: SyncSession)

Bases: Resource

List the external applications issues can be linked to.

list

list() -> ItemList[Application]

GET /applications → external applications that issues can be linked to.

Returns:

Type Description
ItemList[Application]

The external applications.

Examples:

>>> tracker.applications.list().root[0].id
'my-application'

boards

BoardsClient

BoardsClient(*, session: SyncSession)

Bases: Resource

List (relative-paginated), get, create, edit and delete agile boards.

list

list(*, limit: int | None = None) -> ItemList[Board]

All agile boards in the organisation, draining the id=<last board id> cursor.

/boards/_paginate sorts by ascending board id; each next page repeats with id=<id of the last board seen> until a page comes back empty. Capped at limit (None = every board); a small cap narrows the page to limit rows.

Parameters:

Name Type Description Default
limit int | None

The most boards to return; None returns every board.

None

Returns:

Type Description
ItemList[Board]

The boards, in ascending id order.

Examples:

>>> [board.name for board in tracker.boards.list(limit=500).root]
['Alpha', 'Beta', 'Gamma']

get

get(board_id: int) -> Board

GET /boards/{board_id} → a single agile board.

Parameters:

Name Type Description Default
board_id int

The board's id.

required

Returns:

Type Description
Board

The board.

Examples:

>>> tracker.boards.get(31).name
'Kanban'

create

create(body: BoardCreate) -> Board

Create an agile board from a typed BoardCreate body. Returns the new Board.

The endpoint path is literally /liveBoards/ — the older POST /boards/ is deprecated and silently ignores the request body.

Parameters:

Name Type Description Default
body BoardCreate

The new board's settings.

required

Returns:

Type Description
Board

The created board.

Examples:

>>> tracker.boards.create(BoardCreate(name="Release train", owner="alice")).id
41

edit

edit(board_id: int, body: BoardUpdate) -> Board

Edit an agile board from a typed BoardUpdate body. Returns the updated Board.

Only the fields set on body are sent, so omitted fields stay unchanged.

Parameters:

Name Type Description Default
board_id int

The board's id.

required
body BoardUpdate

The fields to change.

required

Returns:

Type Description
Board

The updated board.

Examples:

>>> tracker.boards.edit(51, BoardUpdate(name="Renamed board")).name
'Renamed board'

delete

delete(board_id: int) -> None

DELETE /boards/{board_id} — delete a board (204, empty body).

Parameters:

Name Type Description Default
board_id int

The board's id.

required

Examples:

>>> tracker.boards.delete(61)

sprints

SprintsClient

SprintsClient(*, session: SyncSession)

Bases: Resource

List a board's sprints; get, create, edit, delete, start and archive a sprint.

list

list(board_id: int) -> ItemList[Sprint]

GET /boards/{board_id}/sprints → the board's sprint listing.

Parameters:

Name Type Description Default
board_id int

The board's id.

required

Returns:

Type Description
ItemList[Sprint]

The board's sprints.

Examples:

>>> tracker.sprints.list(3).root[0].name
'Sprint 1'

get

get(sprint_id: int) -> Sprint

GET /sprints/{sprint_id} → a single sprint.

Parameters:

Name Type Description Default
sprint_id int

The sprint's id.

required

Returns:

Type Description
Sprint

The sprint.

Examples:

>>> tracker.sprints.get(4402).status
'in_progress'

create

create(body: SprintCreate) -> Sprint

Create a sprint from a typed SprintCreate body. Returns the created Sprint.

Parameters:

Name Type Description Default
body SprintCreate

The new sprint's name, board and dates.

required

Returns:

Type Description
Sprint

The created sprint.

Examples:

>>> from ycli.yandex.tracker.sprints.models import SprintBoardInput, SprintCreate
>>> new_sprint = SprintCreate(
...     name="Sprint 9",
...     board=SprintBoardInput(id="17"),
...     start_date="2026-10-05",
...     end_date="2026-10-19",
... )
>>> tracker.sprints.create(new_sprint).id
4403

edit

edit(sprint_id: int, body: SprintUpdate, *, version: int | None = None) -> Sprint

Edit a sprint from a typed SprintUpdate body. Returns the updated Sprint.

Only the fields set on body are sent, so omitted fields stay unchanged. version is the sprint's current version, sent as ?version= — the API requires it (or an If-Match header) for optimistic locking and answers 428 without one.

Parameters:

Name Type Description Default
sprint_id int

The sprint's id.

required
body SprintUpdate

The fields to change.

required
version int | None

The sprint's current version, sent as ?version=.

None

Returns:

Type Description
Sprint

The updated sprint.

Examples:

>>> from ycli.yandex.tracker.sprints.models import SprintUpdate
>>> tracker.sprints.edit(4404, SprintUpdate(name="Updated"), version=5).name
'Updated'

delete

delete(sprint_id: int) -> None

DELETE /sprints/{sprint_id} — delete a sprint (204, empty body).

Parameters:

Name Type Description Default
sprint_id int

The sprint's id.

required

Examples:

>>> tracker.sprints.delete(4406)

start

start(sprint_id: int, *, version: int | None = None) -> Sprint

POST /sprints/{sprint_id}/_start — start a sprint (status → in_progress).

version is the sprint's current version, sent as ?version= — the API requires it (or an If-Match header) for optimistic locking and answers 428 without one.

Parameters:

Name Type Description Default
sprint_id int

The sprint's id.

required
version int | None

The sprint's current version, sent as ?version=.

None

Returns:

Type Description
Sprint

The started sprint.

Examples:

>>> tracker.sprints.start(4407, version=6).status
'in_progress'

archive

archive(sprint_id: int, *, version: int | None = None) -> Sprint

POST /sprints/{sprint_id}/_archive — archive a sprint (status → archived).

version is the sprint's current version, sent as ?version= — the API requires it (or an If-Match header) for optimistic locking and answers 428 without one.

Parameters:

Name Type Description Default
sprint_id int

The sprint's id.

required
version int | None

The sprint's current version, sent as ?version=.

None

Returns:

Type Description
Sprint

The archived sprint.

Examples:

>>> tracker.sprints.archive(4409, version=7).status
'archived'

attachments

AttachmentsClient

AttachmentsClient(*, session: SyncSession)

Bases: Resource

Issue /attachments — list, get, upload, delete, plus two binary downloads.

list

list(issue_key: str) -> ItemList[Attachment]

GET /issues/{issue_key}/attachments → files attached to the issue (and its comments).

Parameters:

Name Type Description Default
issue_key str

The issue key.

required

Returns:

Type Description
ItemList[Attachment]

The issue's attachments.

Examples:

>>> tracker.attachments.list("JUNE-2").root[0].name
'picture.jpg'

download

download(issue_key: str, file_id: str, filename: str) -> bytes

Download an attachment's raw bytes (a non-2xx answer raises a typed error).

Binary output is CLI/SDK-only — never an MCP payload. In the CLI this feeds a BinaryResult (a file or stdout); the SDK returns the bytes.

Parameters:

Name Type Description Default
issue_key str

The issue key.

required
file_id str

The attachment's id.

required
filename str

The attachment's file name, as it is in the download path.

required

Returns:

Type Description
bytes

The attachment's raw bytes.

Examples:

>>> tracker.attachments.download("JUNE-3", "4159", "report.pdf")[:4]
b'%PDF'

download_thumbnail

download_thumbnail(issue_key: str, file_id: str) -> bytes

Download a graphic attachment's preview-thumbnail bytes (a non-2xx answer raises).

Only graphic files have a thumbnail; CLI/SDK-only, like :meth:download.

Parameters:

Name Type Description Default
issue_key str

The issue key.

required
file_id str

The attachment's id.

required

Returns:

Type Description
bytes

The thumbnail's raw bytes.

Examples:

>>> tracker.attachments.download_thumbnail("JUNE-4", "4160")[:4]
b'\x89PNG'

get

get(issue_key: str, file_id: str) -> Attachment

GET /issues/{issue_key}/attachments/{file_id} → the attachment's metadata.

The raw bytes are :meth:download; this returns name, size, MIME type and uploader.

Parameters:

Name Type Description Default
issue_key str

The issue key.

required
file_id str

The attachment's id.

required

Returns:

Type Description
Attachment

The attachment's metadata.

Examples:

>>> tracker.attachments.get("JUNE-5", "4161").mimetype
'text/plain'

delete

delete(issue_key: str, file_id: str) -> None

DELETE /issues/{issue_key}/attachments/{file_id} → 204; raises on non-2xx.

Parameters:

Name Type Description Default
issue_key str

The issue key.

required
file_id str

The attachment's id.

required

Examples:

>>> tracker.attachments.delete("JUNE-6", "4162")

upload

upload(issue_key: str, *, filename: str, data: bytes, rename_to: str | None = None) -> Attachment

POST /issues/{issue_key}/attachments → attach a file (multipart field file).

filename names the part; rename_to (the ?filename= query) stores the file under another name. Returns the created :class:Attachment.

Parameters:

Name Type Description Default
issue_key str

The issue key.

required
filename str

The multipart part's file name.

required
data bytes

The file's bytes.

required
rename_to str | None

The name to store the file under; None keeps filename.

None

Returns:

Type Description
Attachment

The created attachment.

Examples:

>>> tracker.attachments.upload(
...     "JUNE-7", filename="upload.txt", data=b"attachment bytes", rename_to="kept.txt"
... ).id
'4161'

upload_temp

upload_temp(*, filename: str, data: bytes, rename_to: str | None = None) -> Attachment

POST /attachments → upload a temporary file to attach later, once.

The returned id goes into attachmentIds of an issue or comment body; Tracker accepts it for one attachment only.

Parameters:

Name Type Description Default
filename str

The multipart part's file name.

required
data bytes

The file's bytes.

required
rename_to str | None

The name to store the file under; None keeps filename.

None

Returns:

Type Description
Attachment

The temporary attachment, whose id is the one to attach.

Examples:

>>> tracker.attachments.upload_temp(
...     filename="temp-upload.txt", data=b"temporary bytes", rename_to="scratch.txt"
... ).id
'4170'

macros

MacrosClient

MacrosClient(*, session: SyncSession)

Bases: Resource

List, get, create, edit and delete a queue's macros.

list

list(queue_id: str) -> ItemList[Macro]

GET /queues/{queue_id}/macros → the queue's macros.

Parameters:

Name Type Description Default
queue_id str

The queue's key or numeric id.

required

Returns:

Type Description
ItemList[Macro]

The queue's macros.

Examples:

>>> tracker.macros.list("TEST").root[0].name
'Close'

get

get(queue_id: str, macro_id: int) -> Macro

GET /queues/{queue_id}/macros/{macro_id} → a single macro.

Parameters:

Name Type Description Default
queue_id str

The queue's key or numeric id.

required
macro_id int

The macro's id.

required

Returns:

Type Description
Macro

The macro.

Examples:

>>> tracker.macros.get("OPS", 4).name
'Escalate'

create

create(queue_id: str, body: MacroCreate) -> Macro

Create a macro from a typed MacroCreate body. Returns the created Macro.

Parameters:

Name Type Description Default
queue_id str

The queue's key or numeric id.

required
body MacroCreate

The new macro's name, comment text and issue update.

required

Returns:

Type Description
Macro

The created macro.

Examples:

>>> from ycli.yandex.tracker.macros.models import MacroCreate
>>> tracker.macros.create("DEV", MacroCreate(name="Triage", body="Taking a look")).id
5

edit

edit(queue_id: str, macro_id: int, body: MacroUpdate) -> Macro

Edit a macro from a typed MacroUpdate body. Returns the updated Macro.

Only the fields set on body are sent, so omitted fields stay unchanged.

Parameters:

Name Type Description Default
queue_id str

The queue's key or numeric id.

required
macro_id int

The macro's id.

required
body MacroUpdate

The fields to change.

required

Returns:

Type Description
Macro

The updated macro.

Examples:

>>> from ycli.yandex.tracker.macros.models import MacroUpdate
>>> tracker.macros.edit("QA", 6, MacroUpdate(name="Renamed")).name
'Renamed'

delete

delete(queue_id: str, macro_id: int) -> None

DELETE /queues/{queue_id}/macros/{macro_id} — delete a macro (204, empty body).

Parameters:

Name Type Description Default
queue_id str

The queue's key or numeric id.

required
macro_id int

The macro's id.

required

Examples:

>>> tracker.macros.delete("SUP", 7)

triggers

TriggersClient

TriggersClient(*, session: SyncSession)

Bases: Resource

List, get, create and edit a queue's triggers; read a trigger's webhook log.

list

list(queue_id: str, *, limit: int | None = None) -> ItemList[Trigger]

GET /queues/{queue_id}/triggers → every trigger of the queue, ascending by id.

Drains the relative cursor (id=<last trigger id>). Capped at limit (None = every trigger); a small cap narrows the page to limit rows.

Parameters:

Name Type Description Default
queue_id str

The queue's key or numeric id.

required
limit int | None

The most triggers to return; None returns every trigger.

None

Returns:

Type Description
ItemList[Trigger]

The queue's triggers, ascending by id.

Examples:

>>> [trigger.name for trigger in tracker.triggers.list("LISTQ", limit=500).root]
['First', 'Second']

get

get(queue_id: str, trigger_id: int) -> Trigger

GET /queues/{queue_id}/triggers/{trigger_id} → a single trigger.

Parameters:

Name Type Description Default
queue_id str

The queue's key or numeric id.

required
trigger_id int

The trigger's id.

required

Returns:

Type Description
Trigger

The trigger.

Examples:

>>> tracker.triggers.get("DESIGN", 16).name
'On comment'

create

create(queue_id: str, body: TriggerCreate) -> Trigger

Create a trigger from a typed TriggerCreate body. Returns the created Trigger.

Parameters:

Name Type Description Default
queue_id str

The queue's key or numeric id.

required
body TriggerCreate

The new trigger's name, actions and conditions.

required

Returns:

Type Description
Trigger

The created trigger.

Examples:

>>> from ycli.yandex.tracker.models import AutomationAction
>>> from ycli.yandex.tracker.triggers.models import TriggerCreate
>>> new_trigger = TriggerCreate(
...     name="Reopen on comment",
...     actions=[AutomationAction(type="Transition", status={"key": "open"})],
... )
>>> tracker.triggers.create("ART", new_trigger).id
17

edit

edit(queue_id: str, trigger_id: int, body: TriggerUpdate, *, version: int | None = None) -> Trigger

Edit a trigger from a typed TriggerUpdate body. Returns the updated Trigger.

Pass version (the trigger's current version) to guard against a concurrent edit; only the fields set on body are sent.

Parameters:

Name Type Description Default
queue_id str

The queue's key or numeric id.

required
trigger_id int

The trigger's id.

required
body TriggerUpdate

The fields to change.

required
version int | None

The trigger's current version, sent as ?version=; None sends none.

None

Returns:

Type Description
Trigger

The updated trigger.

Examples:

>>> from ycli.yandex.tracker.triggers.models import TriggerUpdate
>>> tracker.triggers.edit(
...     "BIZ", 18, TriggerUpdate(name="Renamed trigger"), version=3
... ).name
'Renamed trigger'

webhook_log

webhook_log(queue_id: str, trigger_id: int, issue_id: str | None = None, limit: int | None = None, date_from: str | None = None, date_to: str | None = None) -> ItemList[WebhookLogEntry]

GET /queues/{queue_id}/triggers/{trigger_id}/webhooks/log → HTTP-action run logs.

Returns the trigger's Webhook-action execution records (default 10, limit up to 100). Optionally scope to one issue_id or a date_from/date_to window.

Parameters:

Name Type Description Default
queue_id str

The queue's key or numeric id.

required
trigger_id int

The trigger's id.

required
issue_id str | None

Only the runs for this issue.

None
limit int | None

The most records to return (default 10, up to 100).

None
date_from str | None

Only runs from this time on (ISO 8601).

None
date_to str | None

Only runs up to this time (ISO 8601).

None

Returns:

Type Description
ItemList[WebhookLogEntry]

The trigger's Webhook-action execution records.

Examples:

>>> tracker.triggers.webhook_log(
...     "DEV",
...     6,
...     issue_id="DEV-5",
...     limit=100,
...     date_from="2026-01-01T00:00:00.000+0300",
...     date_to="2026-02-01T00:00:00.000+0300",
... ).root[0].duration
235

autoactions

AutoactionsClient

AutoactionsClient(*, session: SyncSession)

Bases: Resource

Get and create a queue's autoactions; read their run logs.

get

get(queue_id: str, action_id: int) -> Autoaction

GET /queues/{queue_id}/autoactions/{action_id} → a single autoaction.

Parameters:

Name Type Description Default
queue_id str

The queue's key or id.

required
action_id int

The autoaction's id.

required

Returns:

Type Description
Autoaction

The autoaction.

Examples:

>>> tracker.autoactions.get("DESIGN", 9).name
'Nightly'

create

create(queue_id: str, body: AutoactionCreate) -> Autoaction

Create an autoaction from a typed AutoactionCreate body. Returns the Autoaction.

Parameters:

Name Type Description Default
queue_id str

The queue's key or id.

required
body AutoactionCreate

The new autoaction's settings.

required

Returns:

Type Description
Autoaction

The created autoaction.

Examples:

>>> from ycli.yandex.tracker.autoactions.models import AutoactionCreate
>>> from ycli.yandex.tracker.models import AutomationAction
>>> tracker.autoactions.create(
...     "OPS",
...     AutoactionCreate(
...         name="Stale sweep",
...         query="Status: Open",
...         actions=[AutomationAction(type="Transition")],
...     ),
... ).id
10

logs

logs(queue_id: str, action_id: int) -> ItemList[AutoactionLogEntry]

GET /queues/{queue_id}/autoactions/{action_id}/logs → per-run summaries.

Parameters:

Name Type Description Default
queue_id str

The queue's key or id.

required
action_id int

The autoaction's id.

required

Returns:

Type Description
ItemList[AutoactionLogEntry]

One summary per run.

Examples:

>>> tracker.autoactions.logs("QA", 11).root[0].search_hits
3

log_detail

log_detail(queue_id: str, action_id: int, run_id: str) -> ItemList[AutoactionRunEntry]

GET .../autoactions/{action_id}/logs/{run_id} → per-issue outcomes of one run.

Parameters:

Name Type Description Default
queue_id str

The queue's key or id.

required
action_id int

The autoaction's id.

required
run_id str

The run's id.

required

Returns:

Type Description
ItemList[AutoactionRunEntry]

The outcome for each issue the run touched.

Examples:

>>> tracker.autoactions.log_detail("SUP", 12, "run-2").root[0].status.value
'success'

bulk

BulkClient

BulkClient(*, session: SyncSession)

Bases: Resource

/bulkchange (mass update/move/transition + status reads).

update

update(body: dict[str, Any], *, notify: bool | None = None) -> BulkChange

POST /bulkchange/_update — mass-edit issues. Returns the started BulkChange.

Parameters:

Name Type Description Default
body dict[str, Any]

The request body: the issues to change and the field values to set.

required
notify bool | None

Whether to notify the users in the issues' fields; None leaves the API's default (it notifies).

None

Returns:

Type Description
BulkChange

The started bulk change.

Examples:

>>> tracker.bulk.update(
...     {"issues": ["DE-1", "DE-2"], "values": {"priority": "minor"}}
... ).status
'CREATED'

move

move(body: dict[str, Any], *, notify: bool | None = None) -> BulkChange

POST /bulkchange/_move — mass-move issues to another queue. Returns a BulkChange.

Parameters:

Name Type Description Default
body dict[str, Any]

The request body: the target queue and the issues to move.

required
notify bool | None

Whether to notify the users in the issues' fields; None leaves the API's default (it notifies).

None

Returns:

Type Description
BulkChange

The started bulk change.

Examples:

>>> tracker.bulk.move({"queue": "CHECK", "issues": ["DE-3"]}).id
'2cd'

transition

transition(body: dict[str, Any], *, notify: bool | None = None) -> BulkChange

POST /bulkchange/_transition — mass status transition. Returns a BulkChange.

Parameters:

Name Type Description Default
body dict[str, Any]

The request body: the transition to run and the issues to run it on.

required
notify bool | None

Whether to notify the users in the issues' fields; None leaves the API's default (it notifies).

None

Returns:

Type Description
BulkChange

The started bulk change.

Examples:

>>> tracker.bulk.transition({"transition": "close", "issues": ["DE-4"]}).status
'CREATED'

get

get(bulk_id: str) -> BulkChange

GET /bulkchange/{bulk_id} → the operation's current status (poll this to wait).

Parameters:

Name Type Description Default
bulk_id str

The bulk change's id.

required

Returns:

Type Description
BulkChange

The bulk change with its current status.

Examples:

>>> tracker.bulk.get("4gh").is_terminal
True

issues

issues(bulk_id: str) -> ItemList[BulkIssueResult]

GET /bulkchange/{bulk_id}/issues → issues for which the operation failed.

Parameters:

Name Type Description Default
bulk_id str

The bulk change's id.

required

Returns:

Type Description
ItemList[BulkIssueResult]

The per-issue results.

Examples:

>>> tracker.bulk.issues("5ij").root[0].issue
'DE-9'

RemoteLinksClient

RemoteLinksClient(*, session: SyncSession)

Bases: Resource

List, create and delete an issue's links to objects in external applications.

list

list(issue_key: str) -> ItemList[RemoteLink]

GET /issues/{issue_key}/remotelinks → the issue's external-app links.

Parameters:

Name Type Description Default
issue_key str

The issue's key.

required

Returns:

Type Description
ItemList[RemoteLink]

The issue's external-app links.

Examples:

>>> tracker.remotelinks.list("JUNE-2").root[0].object_key
'TEST-17'

create

create(issue_key: str, body: dict[str, Any], backlink: str | None = None) -> RemoteLink

POST /issues/{issue_key}/remotelinks?backlink=… — add an external link.

backlink="true" asks Tracker to also create the mirror link in the external app.

Parameters:

Name Type Description Default
issue_key str

The issue's key.

required
body dict[str, Any]

The link's relationship, external object key and origin.

required
backlink str | None

"true" also creates the mirror link in the external app.

None

Returns:

Type Description
RemoteLink

The created external link.

Examples:

>>> tracker.remotelinks.create(
...     "JUNE-3",
...     {"relationship": "BLOCKS", "key": "TEST-18", "origin": "ru.yandex.bitbucket"},
...     backlink="true",
... ).object_key
'TEST-18'

delete

delete(issue_key: str, link_id: str) -> None

Delete an external link (DELETE …/remotelinks/{link_id} → 204). Raises on non-2xx.

Parameters:

Name Type Description Default
issue_key str

The issue's key.

required
link_id str

The external link's id.

required

Examples:

>>> tracker.remotelinks.delete("JUNE-6", "55")

import

ImportClient

ImportClient(*, session: SyncSession)

Bases: Resource

The Tracker /_import endpoints (admin-only).

task

task(body: dict[str, Any]) -> Issue

POST /issues/_import — import an issue preserving its history. Returns the Issue.

Parameters:

Name Type Description Default
body dict[str, Any]

The issue fields, including the source createdAt and createdBy.

required

Returns:

Type Description
Issue

The imported issue.

Examples:

>>> tracker.import_.task(
...     {
...         "queue": "TEST",
...         "summary": "Old task",
...         "createdAt": "2017-08-29T12:34:41.740+0000",
...         "createdBy": "11",
...         "key": "TEST-41",
...     }
... ).key
'TEST-41'

comment

comment(issue_key: str, body: dict[str, Any]) -> Comment

POST /issues/{issue_key}/comments/_import — import a comment; returns Comment.

Parameters:

Name Type Description Default
issue_key str

The issue's key.

required
body dict[str, Any]

The comment fields, including the source createdAt and createdBy.

required

Returns:

Type Description
Comment

The imported comment.

Examples:

>>> tracker.import_.comment(
...     "TEST-2",
...     {
...         "text": "Old comment",
...         "createdAt": "2019-02-03T04:05:06.000+0000",
...         "createdBy": "13",
...     },
... ).text
'Old comment'
link(issue_key: str, body: dict[str, Any]) -> Link

POST /issues/{issue_key}/links/_import — import an issue link. Returns the Link.

Parameters:

Name Type Description Default
issue_key str

The issue's key.

required
body dict[str, Any]

The link fields, including the source createdAt and createdBy.

required

Returns:

Type Description
Link

The imported link.

Examples:

>>> tracker.import_.link(
...     "TEST-3",
...     {
...         "relationship": "depends on",
...         "issue": "TEST-4",
...         "createdAt": "2020-03-04T05:06:07.000+0000",
...         "createdBy": "14",
...     },
... ).object.key
'TEST-4'

worklog

worklog(issue_key: str, body: dict[str, Any]) -> ItemList[Worklog]

POST /issues/{issue_key}/worklogs/_import — import a worklog (note plural path).

Returns a ItemList[Worklog] — the live endpoint answers with a JSON array of the created worklog record(s), not a single object.

Parameters:

Name Type Description Default
issue_key str

The issue's key.

required
body dict[str, Any]

The worklog fields, including the source createdAt and createdBy.

required

Returns:

Type Description
ItemList[Worklog]

The created worklog record(s).

Examples:

>>> tracker.import_.worklog(
...     "TEST-5",
...     {
...         "duration": "PT2H",
...         "createdAt": "2021-04-05T06:07:08.000+0000",
...         "createdBy": "15",
...         "start": "2021-04-05T09:00:00.000+0000",
...     },
... ).root[0].duration
'PT2H'

file

file(issue_key: str, *, filename: str, created_at: str, created_by: str, data: bytes) -> Attachment

Import a file (multipart/form-data) preserving its createdAt / createdBy.

data are the raw file bytes; filename / created_at / created_by become query parameters. Returns the created Attachment.

Parameters:

Name Type Description Default
issue_key str

The issue's key.

required
filename str

The attachment's file name.

required
created_at str

The source creation time.

required
created_by str

The source author.

required
data bytes

The raw file bytes.

required

Returns:

Type Description
Attachment

The created attachment.

Examples:

>>> tracker.import_.file(
...     "JUNE-5",
...     filename="renamed.png",
...     created_at="2022-05-06T07:08:09.000+0000",
...     created_by="16",
...     data=b"PNGDATA",
... ).name
'renamed.png'

comment_file

comment_file(issue_key: str, comment_id: str, *, filename: str, created_at: str, created_by: str, data: bytes) -> Attachment

Import a file onto a comment, preserving its createdAt / createdBy.

POST /issues/{issue_key}/comments/{comment_id}/attachments/_import (multipart); created_at must fall between the comment's creation and its last update (for a comment never edited, exactly its createdAt), else Tracker answers 422. Returns the created Attachment.

Parameters:

Name Type Description Default
issue_key str

The issue's key.

required
comment_id str

The comment's id.

required
filename str

The attachment's file name.

required
created_at str

The source creation time.

required
created_by str

The source author.

required
data bytes

The raw file bytes.

required

Returns:

Type Description
Attachment

The created attachment.

Examples:

>>> tracker.import_.comment_file(
...     "JUNE-7",
...     "2238",
...     filename="scan.png",
...     created_at="2024-07-08T09:10:11.000+0000",
...     created_by="18",
...     data=b"PNGDATA",
... ).name
'scan.png'

dashboards

DashboardsClient

DashboardsClient(*, session: SyncSession)

Bases: Resource

/dashboards (create a dashboard, add a cycle-time widget).

create

create(body: dict[str, Any]) -> Dashboard

POST /dashboards/ — create a dashboard. Returns the created Dashboard.

Parameters:

Name Type Description Default
body dict[str, Any]

The new dashboard's settings.

required

Returns:

Type Description
Dashboard

The created dashboard.

Examples:

>>> tracker.dashboards.create({"name": "Team board", "layout": "two-columns"}).id
10

add_cycle_time_widget

add_cycle_time_widget(dashboard_id: str, body: dict[str, Any]) -> Widget

POST /dashboards/{dashboard_id}/widgets/cycleTime — add a cycle-time widget.

Returns the created Widget.

Parameters:

Name Type Description Default
dashboard_id str

The dashboard's id.

required
body dict[str, Any]

The widget's settings: its description, issue query and statuses.

required

Returns:

Type Description
Widget

The created widget.

Examples:

>>> tracker.dashboards.add_cycle_time_widget(
...     "11", {"description": "Cycle time", "query": "Queue: DE"}
... ).id
123456

entities

EntitiesClient

EntitiesClient(*, session: SyncSession)

Bases: Resource

/entities/{entity_type} and its sub-resources.

create

create(entity_type: str, body: dict[str, Any], *, fields: str | None = None) -> Entity

POST /entities/{entity_type} — create an entity from a {fields: …} body.

Parameters:

Name Type Description Default
entity_type str

The entity type (project, portfolio or goal).

required
body dict[str, Any]

The new entity, as {"fields": {...}}.

required
fields str | None

The comma-separated entity fields to include in the reply.

None

Returns:

Type Description
Entity

The created entity.

Examples:

>>> tracker.entities.create("project", {"fields": {"summary": "Q4 launch"}}).id
'655f'

get

get(entity_type: str, entity_id: str, expand: str | None = None, fields: str | None = None) -> Entity

GET /entities/{entity_type}/{entity_id} → a single entity (raises on non-2xx).

fields is a comma-separated selector of extra fields keys (summary, checklistItems, keyResultItems, metricItems, …); expand=attachments embeds attachment metadata.

Parameters:

Name Type Description Default
entity_type str

The entity type (project, portfolio or goal).

required
entity_id str

The entity's id.

required
expand str | None

The extras to embed, such as attachments.

None
fields str | None

The extra fields keys to include, comma-separated.

None

Returns:

Type Description
Entity

The entity.

Examples:

>>> tracker.entities.get(
...     "goal", "g2", expand="attachments", fields="summary,keyResultItems"
... ).fields.summary
'Ship'

edit

edit(entity_type: str, entity_id: str, body: dict[str, Any], *, expand: str | None = None, fields: str | None = None) -> Entity

PATCH /entities/{entity_type}/{entity_id} — edit fields/comment/links. Returns it.

Parameters:

Name Type Description Default
entity_type str

The entity type (project, portfolio or goal).

required
entity_id str

The entity's id.

required
body dict[str, Any]

The changes: fields, a comment or links.

required
expand str | None

The extra blocks to include in the reply.

None
fields str | None

The comma-separated entity fields to include in the reply.

None

Returns:

Type Description
Entity

The updated entity.

Examples:

>>> tracker.entities.edit("project", "655f04", {"fields": {"summary": "Renamed"}}).id
'655f04'

delete

delete(entity_type: str, entity_id: str, *, with_board: bool | None = None) -> None

Delete an entity. Pass with_board=True to delete its board too. Raises on non-2xx.

Parameters:

Name Type Description Default
entity_type str

The entity type (project, portfolio or goal).

required
entity_id str

The entity's id.

required
with_board bool | None

Whether to delete the entity's board too.

None

Examples:

>>> tracker.entities.delete("project", "655f07", with_board=True)

search

search(entity_type: str, body: dict | None = None, *, fields: str | None = None, per_page: int | None = None, page: int | None = None) -> ItemList[Entity]

POST /entities/{entity_type}/_search → flat ItemList[Entity] of values.

body carries input (substring), filter (field→value), orderBy, orderAsc and rootOnly. fields selects extra fields keys in the results; per_page/page page the server-side listing.

Parameters:

Name Type Description Default
entity_type str

The entity type (project, portfolio or goal).

required
body dict | None

The search: input, filter, orderBy, orderAsc, rootOnly.

None
fields str | None

The extra fields keys to include, comma-separated.

None
per_page int | None

The page size.

None
page int | None

The page number.

None

Returns:

Type Description
ItemList[Entity]

The matching entities.

Examples:

>>> tracker.entities.search(
...     "project", {"input": "Q4", "filter": {"entityStatus": "in_progress"}}
... ).root[0].id
'655f'

history

history(entity_type: str, entity_id: str, *, limit: int | None = None, selected: str | None = None, new_events_on_top: bool | None = None, direction: str | None = None) -> ItemList[EntityEvent]

GET …/events/_relative → flat ItemList[EntityEvent], draining from=<id>.

Walks the relative-cursor listing (each page repeats with from = the last event's id) until exhausted or limit events collected. With selected the API builds one window around that event and does not take from, so that window is all there is.

Parameters:

Name Type Description Default
entity_type str

The entity type (project, portfolio or goal).

required
entity_id str

The entity's id.

required
limit int | None

The most events to return; None returns every event.

None
selected str | None

The id of the event to build the list around, instead of from the start.

None
new_events_on_top bool | None

Whether to return the newest events first.

None
direction str | None

forward (the API's default) or backward, which inverts new_events_on_top.

None

Returns:

Type Description
ItemList[EntityEvent]

The entity's events.

Examples:

>>> [event.id for event in tracker.entities.history("project", "655f13").root]
['e1', 'e2']

permissions

permissions(entity_type: str, entity_id: str) -> ExtendedPermissions

GET …/extendedPermissions → access settings (acl + permissionSources).

Parameters:

Name Type Description Default
entity_type str

The entity type (project, portfolio or goal).

required
entity_id str

The entity's id.

required

Returns:

Type Description
ExtendedPermissions

The entity's access settings.

Examples:

>>> tracker.entities.permissions("project", "655f15").acl.read.roles
['OWNER']

set_permissions

set_permissions(entity_type: str, entity_id: str, body: dict[str, Any]) -> ExtendedPermissions

PATCH …/extendedPermissions — set access settings. Returns the new settings.

The acl object accepts only grant / revoke actions, each mapping an access level (READ/WRITE/GRANT) to users/groups/roles.

Parameters:

Name Type Description Default
entity_type str

The entity type (project, portfolio or goal).

required
entity_id str

The entity's id.

required
body dict[str, Any]

The acl with its grant and revoke actions.

required

Returns:

Type Description
ExtendedPermissions

The new access settings.

Examples:

>>> tracker.entities.set_permissions(
...     "portfolio",
...     "pf16",
...     {"acl": {"grant": {"READ": {"users": ["8000000000000002"]}}}},
... ).acl.read.users[0].id
'8000000000000002'

direct_permissions

direct_permissions(entity_type: str, entity_id: str) -> Acl

GET …/permissions → the direct READ / WRITE / GRANT rights, without inheritance.

:meth:permissions is the extended view (acl plus where rights are inherited from).

Parameters:

Name Type Description Default
entity_type str

The entity type (project, portfolio or goal).

required
entity_id str

The entity's id.

required

Returns:

Type Description
Acl

The entity's direct rights.

Examples:

>>> tracker.entities.direct_permissions("project", "655f17").grant.roles
['AUTHOR', 'OWNER']

set_direct_permissions

set_direct_permissions(entity_type: str, entity_id: str, body: DirectPermissionsUpdate) -> Acl

PATCH …/permissions — grant and revoke direct rights. Returns the resulting rights.

Parameters:

Name Type Description Default
entity_type str

The entity type (project, portfolio or goal).

required
entity_id str

The entity's id.

required
body DirectPermissionsUpdate

The rights to grant and to revoke.

required

Returns:

Type Description
Acl

The resulting direct rights.

Examples:

>>> from ycli.yandex.tracker.entities.models import AclInput, AclPrincipalsInput
>>> grant = AclInput(read=AclPrincipalsInput(users=["ann"]))
>>> update = DirectPermissionsUpdate(grant=grant)
>>> tracker.entities.set_direct_permissions("goal", "g18", update).read.users[0].display
'Ann'

bulk_update

bulk_update(entity_type: str, body: dict[str, Any]) -> BulkChangeOperation

POST …/bulkchange/_update — mass-edit entities (async). Returns the operation.

The response is a handle whose status starts at CREATED; poll :meth:bulk_status with id until it reaches a terminal status.

Parameters:

Name Type Description Default
entity_type str

The entity type (project, portfolio or goal).

required
body dict[str, Any]

The entities to change (metaEntities) and the values to set.

required

Returns:

Type Description
BulkChangeOperation

The started bulk-change operation.

Examples:

>>> tracker.entities.bulk_update(
...     "project", {"metaEntities": ["655f17"], "values": {"comment": "Handed over"}}
... ).status
'CREATED'

bulk_status

bulk_status(operation_id: str) -> BulkChangeOperation

GET /bulkchange/{operation_id} → the current bulk-change operation status.

Parameters:

Name Type Description Default
operation_id str

The bulk-change operation's id.

required

Returns:

Type Description
BulkChangeOperation

The operation with its current status.

Examples:

>>> tracker.entities.bulk_status("658").status
'COMPLETE'

create_report

create_report(body: dict[str, Any]) -> Entity

POST /entities/report/ — build an issue report from a {fields: …} body.

The body carries the report name plus export parameters (type/format, the issue filter and the column fields); the response is the created report entity.

Parameters:

Name Type Description Default
body dict[str, Any]

The report, as {"fields": {...}}.

required

Returns:

Type Description
Entity

The created report entity.

Examples:

>>> tracker.entities.create_report(
...     {
...         "fields": {
...             "summary": "Support export",
...             "parameters": {
...                 "type": "issueFilterExport",
...                 "format": "csv",
...                 "filter": {"query": "Queue: SUPPORT"},
...                 "fields": ["key", "summary", "assignee"],
...             },
...         }
...     }
... ).entity_type
'report'

comments_list

comments_list(entity_type: str, entity_id: str, expand: str | None = None) -> ItemList[Comment]

GET …/comments → all comments on the entity.

expand embeds extras (html, attachments, reactions, or all).

Parameters:

Name Type Description Default
entity_type str

The entity type (project, portfolio or goal).

required
entity_id str

The entity's id.

required
expand str | None

The extras to embed.

None

Returns:

Type Description
ItemList[Comment]

The entity's comments.

Examples:

>>> tracker.entities.comments_list("project", "655f20").root[0].text
'Готово'

comments_relative

comments_relative(entity_type: str, entity_id: str, *, limit: int | None = None) -> ItemList[Comment]

GET …/comments/_relative → flat ItemList[Comment], draining from=<longId>.

The paginated twin of :meth:comments_list; walks the relative-cursor listing until exhausted or limit comments collected.

Parameters:

Name Type Description Default
entity_type str

The entity type (project, portfolio or goal).

required
entity_id str

The entity's id.

required
limit int | None

The most comments to return; None returns every comment.

None

Returns:

Type Description
ItemList[Comment]

The entity's comments.

Examples:

>>> [
...     c.id
...     for c in tracker.entities.comments_relative("portfolio", "pf22", limit=10).root
... ]
[31, 32]

comments_get

comments_get(entity_type: str, entity_id: str, comment_id: str, expand: str | None = None) -> Comment

GET …/comments/{comment_id} → a single comment.

Parameters:

Name Type Description Default
entity_type str

The entity type (project, portfolio or goal).

required
entity_id str

The entity's id.

required
comment_id str

The comment's id.

required
expand str | None

The extras to embed.

None

Returns:

Type Description
Comment

The comment.

Examples:

>>> tracker.entities.comments_get("project", "655f23", "23").text
'hi'

comments_create

comments_create(entity_type: str, entity_id: str, body: dict[str, Any], *, expand: str | None = None, is_add_to_followers: bool | None = None, notify: bool | None = None, notify_author: bool | None = None) -> Comment

POST …/comments — add a comment. Returns the created comment.

Parameters:

Name Type Description Default
entity_type str

The entity type (project, portfolio or goal).

required
entity_id str

The entity's id.

required
body dict[str, Any]

The new comment: its text and optional summonees.

required
expand str | None

The extra blocks to include in the reply.

None
is_add_to_followers bool | None

Whether to add the comment's author to the followers; None leaves the API's default (it adds).

None
notify bool | None

Whether to notify the users in the entity's fields; None leaves the API's default (it notifies).

None
notify_author bool | None

Whether to notify the author of the change; None leaves the API's default (it does not).

None

Returns:

Type Description
Comment

The created comment.

Examples:

>>> tracker.entities.comments_create("project", "655f25", {"text": "Готово"}).id
22

comments_edit

comments_edit(entity_type: str, entity_id: str, comment_id: str, body: dict[str, Any], *, expand: str | None = None, is_add_to_followers: bool | None = None, notify: bool | None = None, notify_author: bool | None = None) -> Comment

PATCH …/comments/{comment_id} — edit a comment. Returns the updated comment.

The live v3 API only accepts the per-comment route (a PATCH on the …/comments collection answers 405), so the comment id travels in the path, not the body.

Parameters:

Name Type Description Default
entity_type str

The entity type (project, portfolio or goal).

required
entity_id str

The entity's id.

required
comment_id str

The comment's id.

required
body dict[str, Any]

The comment fields to change.

required
expand str | None

The extra blocks to include in the reply.

None
is_add_to_followers bool | None

Whether to add the comment's author to the followers; None leaves the API's default (it adds).

None
notify bool | None

Whether to notify the users in the entity's fields; None leaves the API's default (it notifies).

None
notify_author bool | None

Whether to notify the author of the change; None leaves the API's default (it does not).

None

Returns:

Type Description
Comment

The updated comment.

Examples:

>>> tracker.entities.comments_edit("goal", "g27", "27", {"text": "Fixed typo"}).text
'Fixed typo'

comments_delete

comments_delete(entity_type: str, entity_id: str, comment_id: str, *, notify: bool | None = None, notify_author: bool | None = None) -> None

Delete a comment from an entity. Raises on non-2xx.

Parameters:

Name Type Description Default
entity_type str

The entity type (project, portfolio or goal).

required
entity_id str

The entity's id.

required
comment_id str

The comment's id.

required
notify bool | None

Whether to notify the users in the entity's fields; None leaves the API's default (it notifies).

None
notify_author bool | None

Whether to notify the author of the change; None leaves the API's default (it does not).

None

Examples:

>>> tracker.entities.comments_delete("portfolio", "pf28", "28")

checklists_create

checklists_create(entity_type: str, entity_id: str, body: list[dict[str, Any]], *, expand: str | None = None, fields: str | None = None, notify: bool | None = None, notify_author: bool | None = None) -> Entity

POST …/checklistItems — add items (body is a JSON array). Returns the entity.

Parameters:

Name Type Description Default
entity_type str

The entity type (project, portfolio or goal).

required
entity_id str

The entity's id.

required
body list[dict[str, Any]]

The items to add.

required
expand str | None

The extra blocks to include in the reply.

None
fields str | None

The comma-separated entity fields to include in the reply.

None
notify bool | None

Whether to notify the users in the entity's fields; None leaves the API's default (it notifies).

None
notify_author bool | None

Whether to notify the author of the change; None leaves the API's default (it does not).

None

Returns:

Type Description
Entity

The updated entity.

Examples:

>>> tracker.entities.checklists_create(
...     "project", "655f29", [{"text": "Draft"}, {"text": "Review"}]
... ).id
'655f29'

checklists_edit

checklists_edit(entity_type: str, entity_id: str, body: list[dict[str, Any]], *, expand: str | None = None, fields: str | None = None, notify: bool | None = None, notify_author: bool | None = None) -> Entity

PATCH …/checklistItems — replace items (body is a JSON array of {id, …}).

Parameters:

Name Type Description Default
entity_type str

The entity type (project, portfolio or goal).

required
entity_id str

The entity's id.

required
body list[dict[str, Any]]

The items to replace, each with its id.

required
expand str | None

The extra blocks to include in the reply.

None
fields str | None

The comma-separated entity fields to include in the reply.

None
notify bool | None

Whether to notify the users in the entity's fields; None leaves the API's default (it notifies).

None
notify_author bool | None

Whether to notify the author of the change; None leaves the API's default (it does not).

None

Returns:

Type Description
Entity

The updated entity.

Examples:

>>> tracker.entities.checklists_edit(
...     "goal", "g30", [{"id": "5f", "text": "Renamed"}, {"id": "6a", "text": "Second"}]
... ).id
'g30'

checklists_edit_item

checklists_edit_item(entity_type: str, entity_id: str, item_id: str, body: dict[str, Any], *, expand: str | None = None, fields: str | None = None, notify: bool | None = None, notify_author: bool | None = None) -> Entity

PATCH …/checklistItems/{item_id} — edit one item. Returns the entity.

Parameters:

Name Type Description Default
entity_type str

The entity type (project, portfolio or goal).

required
entity_id str

The entity's id.

required
item_id str

The checklist item's id.

required
body dict[str, Any]

The item fields to change.

required
expand str | None

The extra blocks to include in the reply.

None
fields str | None

The comma-separated entity fields to include in the reply.

None
notify bool | None

Whether to notify the users in the entity's fields; None leaves the API's default (it notifies).

None
notify_author bool | None

Whether to notify the author of the change; None leaves the API's default (it does not).

None

Returns:

Type Description
Entity

The updated entity.

Examples:

>>> tracker.entities.checklists_edit_item(
...     "portfolio", "pf32", "1f", {"text": "Sign off", "checked": True}
... ).id
'pf32'

checklists_delete

checklists_delete(entity_type: str, entity_id: str, *, expand: str | None = None, fields: str | None = None, notify: bool | None = None, notify_author: bool | None = None) -> Entity

DELETE …/checklistItems — clear the whole checklist. Returns the entity.

Parameters:

Name Type Description Default
entity_type str

The entity type (project, portfolio or goal).

required
entity_id str

The entity's id.

required
expand str | None

The extra blocks to include in the reply.

None
fields str | None

The comma-separated entity fields to include in the reply.

None
notify bool | None

Whether to notify the users in the entity's fields; None leaves the API's default (it notifies).

None
notify_author bool | None

Whether to notify the author of the change; None leaves the API's default (it does not).

None

Returns:

Type Description
Entity

The updated entity.

Examples:

>>> tracker.entities.checklists_delete("project", "655f34").id
'655f34'

checklists_delete_item

checklists_delete_item(entity_type: str, entity_id: str, item_id: str, *, expand: str | None = None, fields: str | None = None, notify: bool | None = None, notify_author: bool | None = None) -> Entity

DELETE …/checklistItems/{item_id} — remove one item. Returns the entity.

Parameters:

Name Type Description Default
entity_type str

The entity type (project, portfolio or goal).

required
entity_id str

The entity's id.

required
item_id str

The checklist item's id.

required
expand str | None

The extra blocks to include in the reply.

None
fields str | None

The comma-separated entity fields to include in the reply.

None
notify bool | None

Whether to notify the users in the entity's fields; None leaves the API's default (it notifies).

None
notify_author bool | None

Whether to notify the author of the change; None leaves the API's default (it does not).

None

Returns:

Type Description
Entity

The updated entity.

Examples:

>>> tracker.entities.checklists_delete_item("goal", "g35", "3f").id
'g35'

checklists_move

checklists_move(entity_type: str, entity_id: str, item_id: str, body: dict[str, Any], *, expand: str | None = None, fields: str | None = None, notify: bool | None = None, notify_author: bool | None = None) -> Entity

POST …/checklistItems/{item_id}/_move — reorder an item. Returns the entity.

body is {"before": "<item id>"}.

Parameters:

Name Type Description Default
entity_type str

The entity type (project, portfolio or goal).

required
entity_id str

The entity's id.

required
item_id str

The checklist item's id.

required
body dict[str, Any]

The new position, as {"before": "<item id>"}.

required
expand str | None

The extra blocks to include in the reply.

None
fields str | None

The comma-separated entity fields to include in the reply.

None
notify bool | None

Whether to notify the users in the entity's fields; None leaves the API's default (it notifies).

None
notify_author bool | None

Whether to notify the author of the change; None leaves the API's default (it does not).

None

Returns:

Type Description
Entity

The updated entity.

Examples:

>>> tracker.entities.checklists_move("portfolio", "pf36", "4f", {"before": "5a"}).id
'pf36'
links_list(entity_type: str, entity_id: str, fields: str | None = None) -> ItemList[Link]

GET …/links → the entity's links to other entities.

Parameters:

Name Type Description Default
entity_type str

The entity type (project, portfolio or goal).

required
entity_id str

The entity's id.

required
fields str | None

The extra link fields to include, comma-separated.

None

Returns:

Type Description
ItemList[Link]

The entity's links.

Examples:

>>> tracker.entities.links_list("project", "655f38").root[0].type
'relates'
links_create(entity_type: str, entity_id: str, body: dict) -> None

Create a link (body is {relationship, entity}). Raises on non-2xx.

Parameters:

Name Type Description Default
entity_type str

The entity type (project, portfolio or goal).

required
entity_id str

The entity's id.

required
body dict

The link, as {"relationship": ..., "entity": ...}.

required

Examples:

>>> tracker.entities.links_create(
...     "portfolio", "pf40", {"relationship": "depends on", "entity": "pf41"}
... )
links_delete(entity_type: str, entity_id: str, right: str) -> None

Delete the link to entity right. Raises on non-2xx.

Parameters:

Name Type Description Default
entity_type str

The entity type (project, portfolio or goal).

required
entity_id str

The entity's id.

required
right str

The id of the linked entity.

required

Examples:

>>> tracker.entities.links_delete("goal", "g42", "g43")

attachments_list

attachments_list(entity_type: str, entity_id: str) -> ItemList[Attachment]

GET …/attachments → files attached to the entity (metadata only).

Parameters:

Name Type Description Default
entity_type str

The entity type (project, portfolio or goal).

required
entity_id str

The entity's id.

required

Returns:

Type Description
ItemList[Attachment]

The entity's attachments.

Examples:

>>> tracker.entities.attachments_list("project", "655f44").root[0].name
'Shops.csv'

attachments_get

attachments_get(entity_type: str, entity_id: str, file_id: str) -> Attachment

GET …/attachments/{file_id} → one attachment's metadata (name, size, download URL).

Parameters:

Name Type Description Default
entity_type str

The entity type (project, portfolio or goal).

required
entity_id str

The entity's id.

required
file_id str

The attachment's id.

required

Returns:

Type Description
Attachment

The attachment's metadata.

Examples:

>>> tracker.entities.attachments_get("goal", "g45", "45").name
'flowers.jpg'

attachment_download

attachment_download(file_id: str, filename: str) -> bytes

Download an attachment's raw bytes (a non-2xx answer raises a typed error).

Binary output is CLI/SDK-only — never an MCP payload. file_id and filename come from :meth:attachments_list / :meth:attachments_get (the id and name fields).

Parameters:

Name Type Description Default
file_id str

The attachment's id.

required
filename str

The attachment's file name.

required

Returns:

Type Description
bytes

The attachment's raw bytes.

Examples:

>>> tracker.entities.attachment_download("46", "flowers.jpg")[:4]
b'\xff\xd8\xff\xe0'

attachments_attach

attachments_attach(entity_type: str, entity_id: str, temp_file_id: str, *, expand: str | None = None, fields: str | None = None, notify: bool | None = None, notify_author: bool | None = None) -> Entity

POST …/attachments/{temp_file_id} — attach a previously uploaded temp file.

Returns the updated entity. temp_file_id is the id of a file uploaded to the temp attachments endpoint.

Parameters:

Name Type Description Default
entity_type str

The entity type (project, portfolio or goal).

required
entity_id str

The entity's id.

required
temp_file_id str

The id of the uploaded temporary file.

required
expand str | None

The extra blocks to include in the reply.

None
fields str | None

The comma-separated entity fields to include in the reply.

None
notify bool | None

Whether to notify the users in the entity's fields; None leaves the API's default (it notifies).

None
notify_author bool | None

Whether to notify the author of the change; None leaves the API's default (it does not).

None

Returns:

Type Description
Entity

The updated entity.

Examples:

>>> tracker.entities.attachments_attach("portfolio", "pf47", "tmp47").id
'pf47'

attachments_delete

attachments_delete(entity_type: str, entity_id: str, file_id: str) -> None

Detach a file from an entity. Raises on non-2xx.

The live API answers with an empty body.

Parameters:

Name Type Description Default
entity_type str

The entity type (project, portfolio or goal).

required
entity_id str

The entity's id.

required
file_id str

The attachment's id.

required

Examples:

>>> tracker.entities.attachments_delete("project", "655f48", "48")

workflows

WorkflowsClient

WorkflowsClient(*, session: SyncSession)

Bases: Resource

List, get, create, edit and delete workflows; read the workflows of a queue.

list

list() -> ItemList[Workflow]

GET /workflows → every workflow of the organization except deleted ones.

Returns:

Type Description
ItemList[Workflow]

The workflows.

Examples:

>>> tracker.workflows.list().root[0].name
'Design'

get

get(workflow_id: str) -> Workflow

GET /workflows/{workflow_id} → one workflow with its steps and actions.

Parameters:

Name Type Description Default
workflow_id str

The workflow's id.

required

Returns:

Type Description
Workflow

The workflow.

Examples:

>>> tracker.workflows.get("W21").version
1

for_queue

for_queue(queue_id: str) -> QueueWorkflows

GET /queues/{queue_id}/workflows → workflow id → the issue types that use it.

Parameters:

Name Type Description Default
queue_id str

The queue's key or numeric id.

required

Returns:

Type Description
QueueWorkflows

The queue's workflows, each with the issue types that use it.

Examples:

>>> tracker.workflows.for_queue("WFQ").root["dev"][0].key
'task'

create

create(body: WorkflowCreate) -> Workflow

POST /workflows → create a workflow from a typed WorkflowCreate body.

Parameters:

Name Type Description Default
body WorkflowCreate

The new workflow's name, initial action and steps.

required

Returns:

Type Description
Workflow

The created workflow.

Examples:

>>> from ycli.yandex.tracker.models import LocalizedName
>>> from ycli.yandex.tracker.workflows.models import (
...     WorkflowActionInput,
...     WorkflowCreate,
...     WorkflowStepInput,
... )
>>> new_workflow = WorkflowCreate(
...     id="design-flow",
...     name="Design",
...     initial_action=WorkflowActionInput(
...         name=LocalizedName(ru="Открыть", en="Open"), target="open"
...     ),
...     steps=[WorkflowStepInput(status="open")],
... )
>>> tracker.workflows.create(new_workflow).id
'design-flow'

edit

edit(workflow_id: str, body: WorkflowUpdate, *, version: int) -> Workflow

PATCH /workflows/{workflow_id}?version= → change the set fields of a workflow.

version is the workflow's current version (the API answers 412/428 without a matching one); the reply carries the incremented version.

Parameters:

Name Type Description Default
workflow_id str

The workflow's id.

required
body WorkflowUpdate

The fields to change.

required
version int

The workflow's current version, sent as ?version=.

required

Returns:

Type Description
Workflow

The updated workflow.

Examples:

>>> from ycli.yandex.tracker.workflows.models import WorkflowUpdate
>>> tracker.workflows.edit("W21", WorkflowUpdate(name="QA process"), version=3).version
4

edit_action

edit_action(workflow_id: str, status: str, action_id: str, body: WorkflowActionUpdate, *, version: int) -> Workflow

PATCH /workflows/{id}/steps/{status}/actions/{action_id}?version= → edit one action.

status is the key of the step the action leaves. Returns the whole workflow.

Parameters:

Name Type Description Default
workflow_id str

The workflow's id.

required
status str

The key of the step the action leaves.

required
action_id str

The action's id.

required
body WorkflowActionUpdate

The fields to change.

required
version int

The workflow's current version, sent as ?version=.

required

Returns:

Type Description
Workflow

The whole updated workflow.

Examples:

>>> from ycli.yandex.tracker.models import LocalizedName
>>> from ycli.yandex.tracker.workflows.models import WorkflowActionUpdate
>>> action = WorkflowActionUpdate(
...     name=LocalizedName(ru="Завершить", en="Complete"), target="closed"
... )
>>> tracker.workflows.edit_action(
...     "W23", "inProgress", "close", action, version=2
... ).version
3

delete

delete(workflow_id: str) -> None

DELETE /workflows/{workflow_id} → 204; raises on non-2xx.

Parameters:

Name Type Description Default
workflow_id str

The workflow's id.

required

Examples:

>>> tracker.workflows.delete("W24")

projects

ProjectsClient

ProjectsClient(*, session: SyncSession)

Bases: Resource

List, get, create, edit and delete projects; list a project's queues.

list

list(*, expand: str | None = None) -> ItemList[Project]

GET /projects → every project of the organization.

expand="queues" adds each project's queues.

Parameters:

Name Type Description Default
expand str | None

Extra blocks to include; "queues" adds each project's queues.

None

Returns:

Type Description
ItemList[Project]

Every project.

Examples:

>>> tracker.projects.list(expand="queues").root[0].name
'Project'

get

get(project_id: int, *, expand: str | None = None) -> Project

GET /projects/{project_id} → one project.

Parameters:

Name Type Description Default
project_id int

The project's id.

required
expand str | None

Extra blocks to include, as in :meth:list.

None

Returns:

Type Description
Project

The project.

Examples:

>>> tracker.projects.get(21, expand="queues").version
1

queues

queues(project_id: int, *, expand: str | None = None) -> ItemList[Queue]

GET /projects/{project_id}/queues → the queues whose issues are in the project.

expand takes the same blocks as :meth:QueuesClient.get (all, components, …).

Parameters:

Name Type Description Default
project_id int

The project's id.

required
expand str | None

Extra queue blocks to include.

None

Returns:

Type Description
ItemList[Queue]

The project's queues.

Examples:

>>> tracker.projects.queues(23, expand="components,versions").root[0].key
'ORG'

create

create(body: ProjectCreate) -> Project

POST /projects → create a project from a typed ProjectCreate body.

Projects v3 is the legacy API (entities replace it): the test organization accepted queues but bound no queue, so queues of the new project came back empty.

Parameters:

Name Type Description Default
body ProjectCreate

The new project's name, queues and optional fields.

required

Returns:

Type Description
Project

The created project.

Examples:

>>> from ycli.yandex.tracker.projects.models import ProjectCreate
>>> tracker.projects.create(ProjectCreate(name="Launch", queues="LAUNCH")).id
'9'

edit

edit(project_id: int, body: ProjectUpdate, *, version: int, expand: str | None = None) -> Project

PUT /projects/{project_id}?version= → change the set fields of a project.

version is the project's current version; body.queues is required.

Parameters:

Name Type Description Default
project_id int

The project's id.

required
body ProjectUpdate

The fields to change; queues is required.

required
version int

The project's current version.

required
expand str | None

Extra blocks to include, as in :meth:list.

None

Returns:

Type Description
Project

The updated project.

Examples:

>>> from ycli.yandex.tracker.projects.models import ProjectUpdate
>>> body = ProjectUpdate(queues="EDITQ", name="Renamed")
>>> tracker.projects.edit(31, body, version=5, expand="queues").version
6

delete

delete(project_id: int) -> None

DELETE /projects/{project_id} → 204; raises on non-2xx.

Parameters:

Name Type Description Default
project_id int

The project's id.

required

Examples:

>>> tracker.projects.delete(33)

gaps

GapsClient

GapsClient(*, session: SyncSession)

Bases: Resource

Create, search and delete employee absences (vacations, illness, trips, duty, …).

create

create(body: GapsCreate) -> GapCreated

POST /gaps → create up to 100 absences from a typed GapsCreate body.

Needs Tracker administrator rights. Returns the absences actually saved.

Parameters:

Name Type Description Default
body GapsCreate

The absences to create.

required

Returns:

Type Description
GapCreated

The absences actually saved.

Examples:

>>> from ycli.yandex.tracker.gaps.models import GapInput, GapsCreate, GapWorkflow
>>> gap = GapInput(
...     user="ann",
...     workflow=GapWorkflow.VACATION,
...     date_from="2026-07-01T00:00:00.000Z",
...     date_to="2026-07-15T00:00:00.000Z",
... )
>>> tracker.gaps.create(GapsCreate(gaps=[gap])).gaps[0].id
'68340a1f2b4c1a3d5e7f9011'

search

search(users: Sequence[str], *, date_from: str | None = None, date_to: str | None = None, limit: int | None = None) -> ItemList[UserGaps]

POST /gaps/_search (a read) → each user with the absences that overlap a window.

users are up to 100 logins or ids; the window is date_from to date_to (ISO 8601; the start defaults to now, the end must be after the start). Pages of users are joined; capped at limit users (None = all). Needs administrator rights.

Parameters:

Name Type Description Default
users Sequence[str]

The logins or ids of the users to look up.

required
date_from str | None

Window start (ISO 8601); defaults to now.

None
date_to str | None

Window end (ISO 8601); must be after the start.

None
limit int | None

The most users to return; None returns all.

None

Returns:

Type Description
ItemList[UserGaps]

Each user with the absences that overlap the window.

Examples:

>>> found = tracker.gaps.search(
...     ["ann", "bob"],
...     date_from="2026-07-01T00:00:00.000Z",
...     date_to="2026-08-31T23:59:59.999Z",
... )
>>> [(user.user.login, len(user.gaps)) for user in found.root]
[('ann', 1), ('bob', 0)]

delete

delete(gap_ids: Sequence[str]) -> None

DELETE /gaps?gapIds=… → delete absences by id (up to 100); unknown ids are ignored.

Parameters:

Name Type Description Default
gap_ids Sequence[str]

The ids of the absences to delete.

required

Examples:

>>> tracker.gaps.delete(["68340a1f2b4c1a3d5e7f9011", "68340a1f2b4c1a3d5e7f9012"])