Skip to main content

Jira connector

Resolves three things against the Jira Cloud REST API v3: exact issue keys, vague "my last task" references, and Jira links the message already carries.

MessageResolves to
EVER-123 is blockedthat issue, by key
finished my last task, what's next?the author's assigned issues, in-progress first
https://yourorg.atlassian.net/browse/EVER-123that issue, enriched with status and assignee

A pasted link is the unambiguous case — it names exactly one issue — so it outranks every inferred match.

Configuration​

[connectors.jira]
base_url = "https://yourorg.atlassian.net" # required
email = "bot@yourorg.com" # required — the account the token belongs to
api_token_env = "JIRA_API_TOKEN" # required — the token lives ONLY in the env var

# chat user id -> Jira accountId (NOT a username, NOT an email)
[connectors.jira.user_map]
U0123ABC = "5b10ac8d82e05b22cc7d4ef5"
KeyRequiredMeaning
base_url✅Site root; trailing slash trimmed. Also the link matcher's authority
email✅Atlassian account email the API token belongs to
api_token_env✅Env var holding the API token
[…user_map]—Chat user id → Jira accountId

Auth is Jira's standard HTTP Basic: base64(email:token). The header is built once at startup and never logged.

Getting the API token​

id.atlassian.com/manage-profile/security/api-tokens → Create API token. Then:

export JIRA_API_TOKEN=...

The token inherits the permissions of the account it belongs to, so give it a service account with read access to the projects you want resolved — not someone's personal admin account.

The accountId trap​

user_map values must be Jira accountIds, not usernames and not emails.

Assignee searches are keyed on accountId; anything else silently matches nothing. Since GDPR, Atlassian's APIs stopped accepting usernames at all.

Find an accountId from the profile URL:

https://yourorg.atlassian.net/jira/people/5b10ac8d82e05b22cc7d4ef5
^^^^^^^^^^^^^^^^^^^^^^^^

or with the API:

curl -s -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \
"https://yourorg.atlassian.net/rest/api/3/myself" | jq -r .accountId

An unmapped author resolves to nothing — a normal answer, not an error. Exact keys (EVER-123) still work without a mapping, because they need no author inference.

Confidence​

SituationConfidence
Named by a URL in the message0.99
Fetched by an exact key (EVER-123)0.95
The author's sole in-progress issue0.80
Any other assignee-search match≤ 0.70

The inferred cap sits right at the default [policy] min_confidence of 0.7 on purpose: a guess among several tickets is at the bar, not comfortably over it. Raise min_confidence to 0.8 and only links, exact keys and sole in-progress issues will ever be posted publicly.

Vague references run one assignee search (max 5 results, in-progress ranked first) and are scored by title-token overlap with the message text.

With [policy] unfurl_links = true, base_url doubles as the link matcher's authority: only URLs on that host — and under its path prefix, which is what makes Jira Server installs mounted at /jira work — are claimed. Everything else is silently left to the other connectors.

# Jira Server / Data Center mounted under a path
[connectors.jira]
base_url = "https://tools.acme.internal/jira"

Host comparison is normalized: letter case, a leading www., and a default port are all ignored.

Health check​

everasync doctor

Calls GET /rest/api/3/myself. Two useful signals:

  • 401/403 — the email + token pair is wrong, or the token was revoked.
  • 404 — base_url is not a Jira site root. This is the common one: people paste a board URL (.../jira/software/projects/EVER/boards/1) instead of the site root.

Behaviour and limits​

  • Jira Cloud REST v3. Jira Server exposes a compatible surface at the same paths for these endpoints; set base_url to the mount point.
  • Read-only. Ever Async never transitions, comments on, or assigns an issue.
  • 6-second timeout per resolve, counted as ever_async_connector_calls_total{connector="jira",outcome="timeout"}.
  • Issue-key extraction is generic ([A-Z][A-Z0-9]{1,9}-\d{1,6}), so a string like AWS-123 in prose is offered to Jira as a candidate. If it is not a real key, Jira answers nothing and the message is unaffected.