High-level Helpers¶
jira2py.helpers.JiraHelpers is the public high-level helper facade for Jira Cloud workflows.
from jira2py import JiraAPI
from jira2py.helpers import JiraHelpers
api = JiraAPI()
helpers = JiraHelpers(api)
Facade groups¶
| Property | Helper class | Common methods |
|---|---|---|
helpers.auth |
AuthHelpers |
status(), me() |
helpers.issues |
IssueHelpers |
create(), edit(), transition(), validate_create(), validate_edit() |
helpers.search |
SearchHelpers |
issues() |
helpers.comments |
CommentHelpers |
list(), add(), update(), delete() |
helpers.changelogs |
ChangelogHelpers |
list(), list_by_ids() |
helpers.worklogs |
WorklogHelpers |
list(), add(), update(), delete(), report() |
helpers.attachments |
AttachmentHelpers |
list(), read(), plan_download(), download(), upload(), delete() |
helpers.metadata |
MetadataHelpers |
list_fields(), issue_types(), create_fields(), edit_fields(), transitions(), project(), projects(), statuses(), priorities(), users() |
helpers.links |
LinkHelpers |
list(), types(), create(), delete() |
helpers.filters |
FiltersHelpers |
list(), search(), run() |
Grouped usage¶
helpers.auth.status()
helpers.issues.transition("PROJ-123", "Done")
helpers.comments.update("PROJ-123", "10001", "Updated note")
helpers.changelogs.list("PROJ-123")
helpers.worklogs.add("PROJ-123", "1h")
helpers.attachments.download("10001", output_path="downloads/")
helpers.metadata.statuses()
helpers.links.list("PROJ-123")
helpers.filters.run("12345")
Structured issue reads and presentation¶
IssueHelpers does not retrieve full issues. Use the low-level endpoint as the sole retrieval authority, then optionally pass the returned mapping to public format_issue:
from jira2py.helpers import format_issue
issue = api.issues.get_issue(
"PROJ-123",
fields=["summary", "status", "description"],
)
text = format_issue(
issue,
browse_url=f"{api.credentials.url}/browse/{issue['key']}",
)
format_issue(data, *, browse_url=None) is pure: it performs no I/O, does not change data, and does not choose or retrieve fields. It renders a known field only when that raw key exists in data["fields"]; a present empty value is shown truthfully, while an absent field is omitted. Existing data["names"] labels custom fields when supplied, but the formatter never requests names. ADF values are converted only for this text presentation.
HelperResult¶
Most helper methods return HelperResult.
| Attribute | Type | Meaning |
|---|---|---|
text |
str |
Human-readable helper output |
data |
Any \| None |
Optional structured payload |
raw_content |
str \| None |
Optional serialized raw output |
has_raw_output |
bool |
Whether data or raw_content is present |
Field catalog¶
helpers.metadata.list_fields(project_key=None, *, query=None, field_ids=None, field_types=None, start_at=0, max_results=20) returns one raw Jira /field/search page in HelperResult.data. The values and Jira page metadata remain unchanged, while text is a concise list of display names and canonical id values:
result = helpers.metadata.list_fields(
"PROJ",
query="points",
field_ids=["customfield_10001"],
field_types=["custom"],
start_at=0,
max_results=20,
)
# {"startAt": 0, "maxResults": 20, "total": 1, "isLast": True,
# "values": [{"id": "customfield_10001", "name": "Story Points", ...}]}
A supplied project key is resolved once to Jira's numeric project ID before the field search. query is trimmed, and a blank query is omitted. Canonical field IDs are exact, unpadded strings with no commas; field_types accepts only "system" and "custom"; start_at must be non-negative and max_results positive. These inputs are validated before Jira requests.
This is Jira's /field/search project-context filter, documented for Classic Jira projects. It has no issue-type parameter and does not establish create-screen or edit-screen applicability. Continue to use create_fields() for a project's create-screen metadata and edit_fields() for an existing issue's edit metadata.
Complete changelogs¶
helpers.changelogs.list(issue_key, *, created_at_or_after=None, created_before=None, field_ids=None, result_start_at=0, result_max_results=None) retrieves every Jira changelog page from offset zero before applying local filters. With result pagination omitted, its HelperResult.data is the existing helper-owned aggregate with no pagination fields:
result = helpers.changelogs.list(
"PROJ-123",
created_at_or_after="2026-01-01T00:00:00Z",
created_before="2026-02-01T00:00:00Z",
)
# {"issue_key": "PROJ-123", "changelogs": [...]}
Optional bounds are local ISO-8601 comparisons normalized to UTC: the lower bound is inclusive and the upper bound is exclusive (created_at_or_after <= created < created_before). Naive timestamps are treated as UTC. Filtering happens only after all pages have been retrieved; entries with a missing or unparseable created value remain when unfiltered and are excluded when either bound is supplied.
field_ids filters each retained event's raw items by exact, case-sensitive item["fieldId"]. It never falls back to display field; absent or null fieldId values do not match. Retained events and items keep their raw properties, nulls, and Jira order; events with no matching items are removed. Omit field_ids to preserve the existing unfiltered mappings and behavior.
Supplying result_max_results enables local event pagination after timestamps, field-item filtering, and removal of empty events. Jira's complete history is still fetched first. The result then includes result_page with start_at, max_results, filtered-event total, is_last, and next_start_at; this helper-owned metadata is absent when result pagination is omitted. result_start_at requires result_max_results.
For known IDs, helpers.changelogs.list_by_ids(issue_key, changelog_ids, *, field_ids=None) validates one non-empty sequence of integer IDs and performs one POST request. It applies the same field-item filtering, retains request duplicates and Jira response order, and extracts raw histories from Jira's PageOfChangelogs collection without adding result pagination.
Malformed bounds, IDs, field IDs, and result pagination inputs raise JiraHelperValidationError. Request, response-shape, and non-progressing pagination failures raise JiraHelperOperationError; no partial aggregate is returned.
Search continuation¶
helpers.search.issues() and helpers.filters.run() each make one enhanced-search request and return one raw Jira search page in HelperResult.data. When a page supplies nextPageToken, pass that opaque value unchanged to fetch the next page. Stop when no token is returned; do not use total as the completion condition.
Keep the same JQL and fields for every helpers.search.issues() call:
jql = "project = PROJ ORDER BY created DESC"
fields = ["summary", "status", "assignee"]
issues = []
page = helpers.search.issues(jql, fields=fields)
while True:
issues.extend(page.data["issues"])
next_page_token = page.data.get("nextPageToken")
if not next_page_token:
break
page = helpers.search.issues(
jql,
fields=fields,
next_page_token=next_page_token,
)
For a saved filter, repeat helpers.filters.run() with the same filter ID and fields:
issues = []
page = helpers.filters.run("12345", fields=fields)
while True:
issues.extend(page.data["issues"])
next_page_token = page.data.get("nextPageToken")
if not next_page_token:
break
page = helpers.filters.run(
"12345",
fields=fields,
next_page_token=next_page_token,
)
Helper errors¶
Public helper errors include:
JiraHelperErrorJiraHelperValidationErrorJiraHelperConfigErrorJiraHelperOperationErrorAttachmentErrorAttachmentDownloadError
Public models¶
Common public helper models include:
AttachmentDownloadPlanAttachmentMetaFilterSearchResultIssueTransitionIssueTypeJiraChangelogJiraChangelogItemChangelogPageJiraCommentJiraIssueJiraPriorityJiraProjectJiraStatusJiraUserJiraWorklogProjectSearchResultSearchResultWorklogPageWorklogReportWorklogReportRow
Group reference¶
helpers.auth¶
status()me()
helpers.issues¶
create(project_key, issue_type, summary, *, description=None, fields=None)edit(issue_key, *, summary=None, description=None, fields=None, raw=False)transition(issue_key, transition)validate_create(...)validate_edit(...)
helpers.search¶
issues(jql, *, max_results=20, fields=None, next_page_token=None)
helpers.changelogs¶
list(issue_key, *, created_at_or_after=None, created_before=None, field_ids=None, result_start_at=0, result_max_results=None)list_by_ids(issue_key, changelog_ids, *, field_ids=None)
helpers.comments¶
list(issue_key, *, start_at=0, max_results=50, order_by="created")add(issue_key, body)update(issue_key, comment_id, body)delete(issue_key, comment_id)
helpers.worklogs¶
list(issue_key, *, start_at=0, max_results=50)add(issue_key, time_spent, *, started=None, comment=None)update(issue_key, worklog_id, *, time_spent=None, started=None, comment=None)delete(issue_key, worklog_id)report(*, start_date, end_date, jql, account_id=None, max_issues=100, include_details=False)
helpers.attachments¶
list(issue_key)read(attachment_id)plan_download(attachment_id, *, output_path=None, max_download=...)download(attachment_id, *, output_path=None, max_download=...)upload(issue_key, file_path)delete(attachment_id)
helpers.metadata¶
list_fields(project_key=None, *, query=None, field_ids=None, field_types=None, start_at=0, max_results=20)issue_types(project_key)create_fields(project_key, issue_type)edit_fields(issue_key)transitions(issue_key)project(project_id_or_key)projects(query=None)statuses()priorities()users(query, *, max_results=10)
helpers.links¶
list(issue_key)types()create(link_type, outward_issue_key, inward_issue_key)delete(link_id)
helpers.filters¶
list(*, start_at=0, max_results=50)search(query, *, start_at=0, max_results=50)run(filter_id, *, max_results=20, fields=None, next_page_token=None)
helpers.filters.run() resolves the saved filter's JQL and delegates to the normal search pathway, so its structured output matches helpers.search.issues().
Public/private boundary¶
The following are intentionally not public helper API:
jira2py.helpers._adfjira2py.helpers._text- other private
_*.pymodules - internal formatting and conversion behavior, except public
format_issue