agentleFS
ShanksgShanksg/clawskills/skills/jira/skill.md

jira

Skill2 starsChanged 3 months agoMITView on GitHub

Last validated: 2026-10-14 | API: Jira Cloud REST API v3 Base URL: https://{your-domain}.atlassian.net/rest/api/3/ Assumed product: Jira Cloud (Software or Service Management). Note differences from Jira Data Center where applicable. ⚠️ Breaking changes in 2026: - GET /rest/api/3/search is deprecated — migrate to POST /rest/api/3/search/jql which uses nextPageToken pagination (see Recipe 2). - Field configuration scheme APIs will be removed July 2026 — avoid building on fieldconfigurationscheme endpoints. - Atlassian Connect reaches end of support December 2026 — migrate to Atlassian…

  • Sends data out

62 other projects use a version of this.

Adopted by

Most recently taken up by

Used with

Projects with it also use confluence, meet and 6 more.

Other files people use alongside this one.

Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
  • SpillwaveSolutionsSkill

    confluence

    Manage Confluence documentation with downloads, uploads, conversions, and diagrams. Use when asked to "download Confluence pages", "upload to Confluence", "convert Wiki Markup", "sync markdown to Confluence", "create Confluence page", or "handle Confluence images".

    Also in 9 projects that use this

  • kv0906Skill

    meet

    Process meeting notes into structured notes with extracted decisions, blockers, and action items. Use for "/meet project type title" then paste notes.

    Also in 3 projects that use this

  • koderoverSkill

    jenkins

    Jenkins CI/CD 操作。用于:(1) 触发 Jenkins 任务构建 (2) 查询任务状态和构建历史 (3) 获取构建日志 (4) 操作 Jenkins 节点和配置

    Also in 3 projects that use this

  • SentrySkill

    datadog

    Query live Datadog telemetry (logs, metrics, traces, spans, monitors, incidents, dashboards, services, hosts) through Datadog's Pup CLI. Use when users ask to investigate production behavior in Datadog, including searching logs, checking monitor status, inspecting traces or spans, looking up incidents, finding services, or correlating metrics. Do not use it for Sentry issues, repository/source-code work, or ticketing.

    Also in 3 projects that use this

  • montymiSkill

    tech-spec

    Parse and review Technical Specification documents for completeness, consistency, and implementation readiness

    Also in 3 projects that use this

  • OpenAISkill

    sentry

    Use when the user asks to inspect Sentry issues or events, summarize recent production errors, or pull basic Sentry health data via the Sentry CLI; perform read-only queries using the `sentry` command.

    Also in 4 projects that use this

The file

Show its rules and text

We split this skill into the rules it gives an agent

18 rules. 0 are common, 13 are distinctive, and 5 are very specific to this project.

  • 0

    Common

    Most of the 56 edited versions we read keep these.

  • 13

    Distinctive

    This author's own choices. Worth a look.

  • 5

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Create, triage, and route issues automatically from external events (alerts, support tickets, form submissions).

    line 23
  2. Distinctive:

    Search and filter issues using JQL (Jira Query Language) with full pagination and incremental sync.

    line 25
  3. Distinctive:

    Goal: Create a Bug in a specific project with description, priority, and component.

    line 178
  4. Very specific:

    Required fields on transition: If a transition requires a resolution or other field, include it in the fields object of the transition …

    line 347
  5. Distinctive:

    Array fields (labels, components): PUT replaces the entire array.

    line 456
  6. Distinctive:

    Goal: Create or update a Jira Bug when a Zendesk ticket meets escalation criteria.

    line 474
  7. Distinctive:

    Search Jira for an existing issue with that external key in a custom field or label.

    line 484
  8. Distinctive:

    If found, update severity, assignee, and status.

    line 485
  9. Distinctive:

    Add reverse Jira -> Zendesk status updates only after you define an explicit state map and conflict rules.

    line 500
  10. Very specific:

    Before creating, search: summary ~ "your-dedup-key" AND project = PROJ — if found, update instead of create.

    line 523
  11. Very specific:

    Use a dedicated text custom field (e.g., External ID) for reliable deduplication.

    line 524
  12. Distinctive:

    Issue creation: Use a custom External ID field.

    line 569
  13. Very specific:

    Comments: No idempotency key. Include a machine-readable marker (e.g., [automation-id: abc123]) in the comment body.

    line 571
  14. Distinctive:

    Webhook stops firing: Check the webhook URL is reachable.

    line 593
  15. Very specific:

    Data minimization: Only request fields= parameters you actually need — reduces response size and accidental PII exposure in logs.

    line 601
  16. Distinctive:

    Admin tokens: Never use site admin credentials for integrations.

    line 603
  17. Distinctive:

    Webhook URL security: Use a secret in the URL and rotate it.

    line 604
  18. Distinctive:

    Create Issue Meta: https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issues/#api-rest-api-3-issue-createmeta-get

    line 633

“Common” is measured: kept by at least 6 in 10 of the 56 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
skill.md

Jira Skill

Last validated: 2026-10-14 | API: Jira Cloud REST API v3 Base URL: https://{your-domain}.atlassian.net/rest/api/3/ Assumed product: Jira Cloud (Software or Service Management). Note differences from Jira Data Center where applicable.

⚠️ Breaking changes in 2026:

  • GET /rest/api/3/search is deprecated — migrate to POST /rest/api/3/search/jql which uses nextPageToken pagination (see Recipe 2).
  • Field configuration scheme APIs will be removed July 2026 — avoid building on fieldconfigurationscheme endpoints.
  • Atlassian Connect reaches end of support December 2026 — migrate to Atlassian Forge for new apps. End of support for Atlassian Connect Express and Connect Spring Boot announced 2026-04-29.
  • Workflow admin APIs removed 2026-04-07: the legacy Get all workflows and Create workflow endpoints have been removed. Use the newer paginated workflow APIs. Issue-level transitions (POST /rest/api/3/issue/{key}/transitions) are unaffected.
  • draft parameters on workflow rule APIs deprecated (2026-04-20).
  • Classic API token access for Jira Product Discovery deprecated (2026-04-30) — migrate to scoped tokens.
  • Workflow transition properties APIs removed 2026-07-13 — the transition-properties endpoint set is gone from the public API. Use Bulk get workflows / Bulk update workflows to read and write transition properties. Issue-level transitions (POST /rest/api/3/issue/{key}/transitions, Recipe 4) are unaffected.
  • Webhook payloads: workflowId deprecated (2026-07-19, sunset 2027-07-31) — a new workflowEntityId field identifies the workflow correctly for the Workflow REST APIs. Applies to Connect post functions and "Trigger a Webhook" post functions. If your handler reads workflowId, switch to workflowEntityId before the sunset.

Additive since 2026-05: projectId filter on the workflow search API (2026-06-22); includeGlobalStatuses on project-scoped status search (2026-06-25, defaults to false).


What this skill enables

  • Create, triage, and route issues automatically from external events (alerts, support tickets, form submissions).
  • Transition issues through workflow states programmatically, respecting required fields per transition.
  • Search and filter issues using JQL (Jira Query Language) with full pagination and incremental sync.
  • Log activity, decisions, and context as structured comments using Atlassian Document Format (ADF).
  • Sync status bidirectionally between Jira and other tools (Zendesk, ServiceNow, Monday.com).
  • Subscribe to real-time issue events via webhooks to drive downstream automation without polling.
  • Manage labels, components, and sprints to keep project metadata current from external systems.

Best-fit use cases

Use caseWhy it mattersPrimary objects/entitiesTypical triggerSuccess criteria
Create bug from monitoring alertAuto-triage production issues without manual workIssue (Bug type)Alert fired (PagerDuty, Datadog, etc.)Issue created with severity label, component, and alert link
Triage unassigned issuesEnsure no issue sits unassigned for >1hIssueScheduled jobIssues older than threshold have assignees set
Workflow transition on external eventKeep Jira status in sync with deployment or CRM eventIssue, TransitionDeploy pipeline success, deal closedIssue moved to correct status with required fields
Sync Zendesk ticket → Jira bugBidirectional link between support and engineeringIssueZendesk ticket escalatedJira bug created; Zendesk ticket ID stored in issue
Sprint managementAuto-add issues to active sprintIssue, SprintIssue created with priority = CriticalIssue added to active sprint on the correct board
Component/label batch updateKeep categorization cleanIssueWeekly cleanup jobAll matching issues have labels/components corrected
Comment with runbook linkProvide context for assigneesIssue CommentIssue assigned to on-callComment posted with runbook and dashboard links
Attachment — attach log fileEvidence trail for bugsIssue AttachmentBug created by automationLog file attached; size <10MB
Webhook-driven issue routingAssign issues to correct team without pollingIssueIssue created eventAssignee and component set within 5s
Report issues updated todayIncremental sync for reporting toolIssueScheduled daily pullAll issues modified in last 24h retrieved and processed
Epic→Story linkingAuto-create stories under the correct epicIssue (Story), EpicEpic created by planning toolStories created with customfield_10014 (Epic Link) set
Failed deploy → incident ticketAuto-create incident from CI/CD failureIssue (Incident type)Pipeline failure webhookIncident created with pipeline URL and failure log

Key concepts & data model

Core objects

ObjectDescriptionIdentified by
ProjectContainer for issues; has a key (e.g., PROJ) and a numeric IDString key (e.g., PROJ) or integer id
IssueThe main work item (Story, Bug, Task, Epic, etc.)String key (e.g., PROJ-42) or string id
Issue TypeCategory of issue (Epic, Story, Task, Bug, Sub-task, etc.)Integer id; string name
StatusCurrent workflow state (To Do, In Progress, Done)Integer id; string name
TransitionAllowed move from one status to anotherInteger id; string name
PriorityUrgency level (Highest, High, Medium, Low, Lowest)String name or integer id
ComponentSub-area of a project (e.g., "API", "Frontend")Integer id; string name
LabelFree-form tag on an issueString
SprintTime-boxed iteration (Jira Software)Integer id; stored in custom field customfield_10020
CommentActivity entry on an issueInteger id
AttachmentFile linked to an issueInteger id
UserJira accountString accountId (not username)

Issue relationships

Project
  └── Issue (many)
        ├── Issue Type (Epic, Story, Bug, Task, Sub-task)
        ├── Status (from project workflow)
        ├── Priority
        ├── Assignee (User)
        ├── Reporter (User)
        ├── Components []
        ├── Labels []
        ├── Fix Versions []
        ├── Comment []
        ├── Attachment []
        ├── Sub-tasks [] (child issues)
        └── Epic Link (parent Epic, via customfield_10014 or "parent")

Custom fields

Jira custom fields use IDs like customfield_10014. The mapping varies per Jira instance. Always introspect:

# List all fields with their IDs
GET /rest/api/3/field

Common custom field IDs (Jira Cloud defaults — verify for your instance):

  • customfield_10014: Epic Link (on Story/Task)
  • customfield_10020: Sprint
  • customfield_10016: Story Points (may be story_points or customfield_10028)

ADF (Atlassian Document Format)

All description and comment bodies use ADF JSON (not plain text or Markdown):

{
  "type": "doc",
  "version": 1,
  "content": [
    {
      "type": "paragraph",
      "content": [
        { "type": "text", "text": "This is a bug reproduced at " },
        { "type": "text", "text": "https://example.com/error", "marks": [{ "type": "link", "attrs": { "href": "https://example.com/error" } }] }
      ]
    }
  ]
}

Authentication & permissions

Supported auth methods

MethodUse caseNotes
API Token + Basic AuthServer-to-server integrationsRecommended for automation. Token acts as password.
OAuth 2.0 (3LO)User-context appsRequired for acting on behalf of users.
OAuth 2.0 (2LO / Client Credentials)App-level, no user contextOnly for Forge apps currently.

API Token + Basic Auth (most common for automation)

  1. Generate an API token: https://id.atlassian.com/manage-profile/security/api-tokens
  2. Base64-encode {email}:{api_token}.
TOKEN=$(echo -n "user@example.com:your_api_token" | base64)

curl -s "https://yourdomain.atlassian.net/rest/api/3/myself" \
  -H "Authorization: Basic $TOKEN" \
  -H "Accept: application/json"

OAuth 2.0 (3LO) scopes

ScopePurpose
read:jira-workRead issues, projects, comments
write:jira-workCreate/update issues, comments
manage:jira-projectManage project settings, components
manage:jira-configurationManage Jira configuration (webhooks, etc.)
offline_accessObtain refresh tokens

Least privilege for automation: read:jira-work write:jira-work offline_access

Token storage

  • API tokens do not expire but can be revoked. Rotate periodically or immediately after personnel changes.
  • OAuth access tokens expire in ~1 hour. Use refresh tokens for long-running services.
  • Store tokens in environment variables or secrets manager — never in code or logs.

Multi-tenant

  • Each Jira Cloud site has a unique subdomain (yourdomain.atlassian.net). OAuth cloudId identifies the specific site.
  • For multi-site apps, store cloudId + tokens per site.

Common workflows (recipes)

Recipe 1: Create an issue

Goal: Create a Bug in a specific project with description, priority, and component.

BASE="https://yourdomain.atlassian.net"
AUTH="Basic $(echo -n 'user@example.com:api_token' | base64)"

curl -s -X POST "$BASE/rest/api/3/issue" \
  -H "Authorization: $AUTH" \
  -H "Content-Type: application/json" \
  -d '{
    "fields": {
      "project": { "key": "PROJ" },
      "summary": "NPE in UserService.getById() under high load",
      "issuetype": { "name": "Bug" },
      "priority": { "name": "High" },
      "description": {
        "type": "doc", "version": 1,
        "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Reproduced 3x in production. See attached log." }] }]
      },
      "components": [{ "name": "API" }],
      "labels": ["production", "critical"],
      "assignee": { "accountId": "5b10a2844c20165700ede21g" }
    }
  }'
# Response: {"id": "10023", "key": "PROJ-42", "self": "https://..."}

Edge cases:

  • Issue type name must match exactly what exists in the project. Verify via GET /rest/api/3/issuetype.
  • Component must already exist in the project. Creating a new component requires POST /rest/api/3/component.
  • Required custom fields vary per project/issue type. If you get a 400 with "Field required", introspect via GET /rest/api/3/issue/createmeta?projectKeys=PROJ&expand=projects.issuetypes.fields.

Recipe 2: Search issues with JQL and paginate

Goal: Find all open Bugs in PROJ modified in the last 24 hours.

⚠️ Deprecation notice: GET /rest/api/3/search is deprecated. Use the new POST /rest/api/3/search/jql endpoint with nextPageToken cursor-based pagination. The old endpoint still works but will be removed — migrate now.

New recommended approach (POST + nextPageToken):

# Initial request — no nextPageToken on first call
curl -s -X POST "$BASE/rest/api/3/search/jql" \
  -H "Authorization: $AUTH" \
  -H "Content-Type: application/json" \
  -d '{
    "jql": "project = PROJ AND issuetype = Bug AND status != Done AND updated >= -1d ORDER BY updated DESC",
    "fields": ["id", "key", "summary", "status", "assignee", "priority", "updated"],
    "maxResults": 50
  }'

Response (new format):

{
  "total": 312,
  "isLast": false,
  "nextPageToken": "eyJhbGciOiJIUzI1NiJ9...",
  "issues": [...]
}

Pagination loop (Python) — new cursor approach:

import requests

all_issues = []
next_page_token = None

while True:
    body = {
        "jql": "project = PROJ AND status != Done ORDER BY updated ASC",
        "fields": ["id", "key", "summary", "status", "updated"],
        "maxResults": 50
    }
    if next_page_token:
        body["nextPageToken"] = next_page_token

    resp = requests.post(
        f"{BASE}/rest/api/3/search/jql",
        headers={"Authorization": AUTH, "Content-Type": "application/json"},
        json=body
    )
    data = resp.json()
    all_issues.extend(data.get("issues", []))

    if data.get("isLast", True) or not data.get("nextPageToken"):
        break
    next_page_token = data["nextPageToken"]

Legacy approach (still works, deprecated):

# Use only if you haven't migrated yet
curl -s -G "$BASE/rest/api/3/search" \
  --data-urlencode 'jql=project = PROJ AND issuetype = Bug AND status != Done' \
  --data-urlencode 'fields=id,key,summary,status,assignee,priority,updated' \
  --data-urlencode 'maxResults=50' \
  --data-urlencode 'startAt=0' \
  -H "Authorization: $AUTH"
# Response: {"total": 312, "startAt": 0, "maxResults": 50, "issues": [...]}

JQL quick reference:

-- Issues assigned to me
assignee = currentUser()

-- Updated in last N days
updated >= -7d

-- Specific status
status in ("In Progress", "In Review")

-- Priority
priority in (High, Highest)

-- Label
labels in ("production")

-- Epic children
"Epic Link" = PROJ-10

-- Sprint
sprint in openSprints()

-- Unassigned
assignee is EMPTY

Recipe 3: Transition an issue to a new status

Goal: Move issue PROJ-42 from "In Progress" to "In Review".

Step 1: Get available transitions:

curl -s "$BASE/rest/api/3/issue/PROJ-42/transitions" \
  -H "Authorization: $AUTH"
# Response: {"transitions": [{"id": "21", "name": "Start Review", "to": {"name": "In Review"}}, ...]}

Step 2: Execute the transition:

curl -s -X POST "$BASE/rest/api/3/issue/PROJ-42/transitions" \
  -H "Authorization: $AUTH" \
  -H "Content-Type: application/json" \
  -d '{
    "transition": { "id": "21" },
    "update": {
      "comment": [{
        "add": {
          "body": {
            "type": "doc", "version": 1,
            "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Moving to review after fix deployed to staging." }] }]
          }
        }
      }]
    }
  }'
# 204 No Content on success

Pitfall: Transition IDs are project-specific and change if the workflow is edited. Never hardcode transition IDs — always fetch them dynamically. Cache them for a short period (minutes) if you need performance.

Required fields on transition: If a transition requires a resolution or other field, include it in the fields object of the transition request. If required fields are missing, Jira returns 400 with specific field errors.


Recipe 4: Add a comment to an issue

Goal: Post a structured internal note with context.

curl -s -X POST "$BASE/rest/api/3/issue/PROJ-42/comment" \
  -H "Authorization: $AUTH" \
  -H "Content-Type: application/json" \
  -d '{
    "body": {
      "type": "doc",
      "version": 1,
      "content": [
        {
          "type": "paragraph",
          "content": [
            { "type": "text", "text": "Linked from Zendesk ticket " },
            { "type": "text", "text": "#ZD-7891", "marks": [{ "type": "strong" }] },
            { "type": "text", "text": ". Customer affected: Acme Corp. Priority escalated." }
          ]
        }
      ]
    },
    "visibility": {
      "type": "role",
      "value": "Service Desk Team"
    }
  }'

Omit visibility for public comments (visible to all project members).


Recipe 5: Attach a file to an issue

curl -s -X POST "$BASE/rest/api/3/issue/PROJ-42/attachments" \
  -H "Authorization: $AUTH" \
  -H "X-Atlassian-Token: no-check" \
  -F "file=@/path/to/error.log"
# Response: [{"id": "10044", "filename": "error.log", "size": 4096, ...}]

Required: X-Atlassian-Token: no-check header must be present. Omitting it returns 403.

File size limit: 10 MB by default on Jira Cloud. Check your instance limits.


Recipe 6: Register a webhook for issue events

curl -s -X POST "$BASE/rest/webhooks/1.0/webhook" \
  -H "Authorization: $AUTH" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My Integration Webhook",
    "url": "https://your-server.example.com/jira-webhook",
    "events": ["jira:issue_created", "jira:issue_updated", "jira:issue_deleted"],
    "filters": {
      "issue-related-events-section": "project = PROJ AND issuetype in (Bug, Incident)"
    },
    "excludeBody": false
  }'

Available events: jira:issue_created, jira:issue_updated, jira:issue_deleted, comment_created, comment_updated, jira:worklog_updated, sprint_created, sprint_closed.

Webhook payload (issue_updated excerpt):

{
  "timestamp": 1708357200000,
  "webhookEvent": "jira:issue_updated",
  "user": { "accountId": "5b10a..." },
  "issue": { "id": "10023", "key": "PROJ-42", "fields": { "status": {...}, "summary": "..." } },
  "changelog": {
    "items": [
      { "field": "status", "fromString": "In Progress", "toString": "Done" }
    ]
  }
}

Webhook security: Jira Cloud webhooks do not send a signature by default. Use a secret token in the URL path or query string (e.g., ?secret=abc123) and verify it in your handler.


Recipe 7: Update an issue (labels, assignee, custom fields)

curl -s -X PUT "$BASE/rest/api/3/issue/PROJ-42" \
  -H "Authorization: $AUTH" \
  -H "Content-Type: application/json" \
  -d '{
    "fields": {
      "assignee": { "accountId": "5b10a2844c20165700ede21g" },
      "labels": ["production", "critical", "needs-hotfix"],
      "priority": { "name": "Highest" },
      "customfield_10014": "PROJ-10"
    }
  }'
# 204 No Content on success

Array fields (labels, components): PUT replaces the entire array. To append, first GET the current values, then PUT the merged array.


Recipe 8: Get all transitions and validate workflow

# Useful for building a status-to-transition-ID map
curl -s "$BASE/rest/api/3/issue/PROJ-42/transitions?expand=transitions.fields" \
  -H "Authorization: $AUTH"

The expand=transitions.fields parameter returns the fields required for each transition — use this to avoid 400 errors when transitioning.


Cross-tool recipe: Zendesk Ticket -> Jira Bug escalation

Goal: Create or update a Jira Bug when a Zendesk ticket meets escalation criteria.

Escalation trigger examples:

  • Zendesk ticket priority becomes urgent
  • Tag includes engineering
  • Custom field indicates product defect

Flow:

  1. Receive the Zendesk webhook or poll Incremental Export.
  2. Build a deterministic external key, for example zendesk:{ticket_id}.
  3. Search Jira for an existing issue with that external key in a custom field or label.
  4. If found, update severity, assignee, and status. If not found, create a Bug with the Zendesk URL in the description.
  5. Post the Jira issue key back to Zendesk as an internal note or custom field.

Minimum field mapping:

ZendeskJira
subjectsummary
description / latest public commentdescription (ADF)
prioritypriority
ticket idcustom external ID field
tagslabels

Status sync recommendation:

  • One-way sync into Jira is safer initially.
  • Add reverse Jira -> Zendesk status updates only after you define an explicit state map and conflict rules.

Query patterns & filtering

JQL incremental sync

-- Changed in last sync window (store max updatedDate as next boundary)
updated >= "2026-02-18 14:00" ORDER BY updated ASC

-- Using UNIX timestamp (milliseconds not supported — use date string)
updated >= "2026-02-18"

Pagination limits

  • New endpoint: POST /rest/api/3/search/jql with nextPageToken cursor — preferred, no offset degradation.
  • Legacy endpoint: GET /rest/api/3/search with startAt/maxResults — deprecated; max maxResults = 100 (default 50).
  • For very large result sets (>10,000), cursor-based nextPageToken is more reliable than high startAt offsets.

Handling duplicates

  • Issue keys (e.g., PROJ-42) are globally unique within a project. Store the key in your external system as the canonical reference.
  • Before creating, search: summary ~ "your-dedup-key" AND project = PROJ — if found, update instead of create.
  • Use a dedicated text custom field (e.g., External ID) for reliable deduplication.

⚠️ Changed 2026-10-06: JQL =, IN, and NOT IN on text fields now do an exact phrase search: the phrase can appear anywhere in the field, not only as the whole value. "External ID" = "zendesk:123" can now also match a longer value that contains that phrase. After the search, check that the field value equals your key exactly before you treat a hit as a duplicate. Source: https://developer.atlassian.com/cloud/jira/platform/changelog/


Reliability: rate limits, retries, idempotency

Rate limits (Jira Cloud)

Jira Cloud does not publish hard req/s limits. The platform uses adaptive throttling. Practical guidance:

  • Stay under ~10 req/s per token for sustained operations.
  • Burst up to ~50 req/s briefly without issues.
  • 429 responses include a Retry-After header.
import time
import requests

def jira_request(method, url, **kwargs):
    for attempt in range(5):
        resp = requests.request(method, url, **kwargs)
        if resp.status_code == 429:
            retry_after = int(resp.headers.get("Retry-After", 30))
            time.sleep(retry_after)
            continue
        resp.raise_for_status()
        return resp
    raise Exception("Max retries exceeded")

Retry vs fail-fast

StatusAction
200/201/204Success
400 Bad RequestFail fast — fix payload
401 UnauthorizedRefresh token and retry once
403 ForbiddenFail fast — fix permissions
404 Not FoundFail fast — check issue key/ID
429 Too Many RequestsRetry after Retry-After seconds
503 Service UnavailableRetry with exponential backoff

Idempotency

  • Issue creation: Use a custom External ID field. Query before creating — if found, update; if not, create.
  • Transitions: Attempting a transition that's already in the target state returns a 400 — treat this as "already done" (idempotent success).
  • Comments: No idempotency key. Include a machine-readable marker (e.g., [automation-id: abc123]) in the comment body; check for it before posting.

Error handling & troubleshooting

Common errors

HTTP Status + MessageMeaningFix
400 Field 'X' is requiredRequired field missing on create/transitionAdd the field; use createmeta to discover required fields
400 The issue type 'X' is not validIssue type doesn't exist in projectVerify via GET /rest/api/3/issuetype for the project
400 Transition is not validTransition not available from current statusFetch transitions first; verify current status
401Token invalid or expiredRegenerate API token or refresh OAuth token
403User lacks project permissionCheck project role in Jira UI
404Issue key/ID doesn't existVerify the key; check if issue is in a different project
429Rate limitedBack off per Retry-After header

"If you see X, do Y" playbook

  • ADF validation error (400 with "Content must be a doc"): Your description/comment body is plain text, not ADF. Wrap it.
  • accountId not found (400): User is not a member of the Jira site. Use GET /rest/api/3/user/search?query=email to look up the correct accountId.
  • Webhook stops firing: Check the webhook URL is reachable. Jira disables webhooks that fail 3+ times. Re-create via API if disabled.
  • Custom field 400 (Field X cannot be set): Field is read-only or managed by automation. Remove it from the request.

Security, privacy, compliance

  • PII in issues: Issue descriptions/comments may contain PII (customer names, emails). Log field names, not values.
  • Data minimization: Only request fields= parameters you actually need — reduces response size and accidental PII exposure in logs.
  • Audit trail: Jira issue history provides a full audit trail of field changes. For compliance, avoid bulk-deleting issues — use status changes instead.
  • Admin tokens: Never use site admin credentials for integrations. Use a dedicated service account with the minimum project roles (Jira Software: "Developer" role is sufficient for CRUD; "Administrators" role only for project config).
  • Webhook URL security: Use a secret in the URL and rotate it. Jira Cloud does not sign webhook payloads natively.
  • OAuth tokens: Restrict scopes to read:jira-work write:jira-work. Do not request manage:jira-configuration unless you need to modify Jira settings.

Testing checklist

  • Auth test: GET /rest/api/3/myself returns the integration user's account details.
  • CRUD test: Create issue → read by key → update assignee → add comment → transition status → verify at each step.
  • Pagination test: JQL query returning >100 issues; loop through all pages; count matches total.
  • Transition test: Fetch transitions for an issue; execute a valid transition; verify status changed; attempt transition from incorrect state and confirm 400.
  • Attachment test: Upload a file; verify it appears in issue; download it.
  • Webhook test: Register webhook; create an issue; verify payload received within 5s; confirm changelog contains expected fields.
  • Permission test: Attempt to create an issue in a project the service account doesn't have access to — confirm 403.
  • Negative tests:
    • Invalid issue key → 404
    • Missing required field → 400 with field name in error
    • Invalid ADF in description → 400
    • Revoked token → 401

Sources

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.