@statewavedev/connectors-jira
v0.4.2
Published
Jira Cloud source connector for Statewave — pulls issues and comments (and a real-time webhook receiver) into agent memory (preview)
Downloads
68
Maintainers
Readme
@statewavedev/connectors-jira
Preview Jira Cloud source connector for Statewave — turns Jira issues (and
optionally their comments) into normalized episodes under project:<KEY>.
Part of the Statewave Connectors ecosystem. It ingests external Jira records into Statewave memory; it is not a Statewave storage backend.
Install
# the connector + the unified CLI to run it
npm install -g @statewavedev/connectors-cli
npm install @statewavedev/connectors-jiraThe CLI (statewave-connectors) discovers the connector by name (sync jira). You can also import createJiraConnector from @statewavedev/connectors-jira directly in your own code.
Scope (preview)
- Jira Cloud (REST v3, ADF) and Jira Server / Data Center (REST v2, plain-text) — pick with
--deployment. See Jira Server / Data Center. - Auth: Cloud → email + API token (Basic). Server/DC → personal access token (Bearer) or username + password (Basic).
- Pull mode + a real-time webhook receiver (
listen jira) — see Webhook receiver. - Read-only — issues, opt-in comments + status transitions. Never writes to Jira.
- Project allowlist required in pull mode (optional, recommended in webhook mode) — a connector instance only ingests the projects you name.
- No email addresses — users are recorded by display name / username / accountId.
What it ingests
| Source | Episode kind |
|---|---|
| Issue (open) | jira.issue.created |
| Issue (status category "done") | jira.issue.resolved |
| Comment (opt-in via --include comments) | jira.comment.created |
| Status change (opt-in via --include transitions) | jira.issue.transition |
Each episode carries source.url (a /browse/<KEY> link) for provenance, plus
status, labels, assignee/reporter (display names), and timestamps in
metadata.
Quickstart
export JIRA_EMAIL="[email protected]"
export JIRA_API_TOKEN="…" # https://id.atlassian.com/manage-profile/security/api-tokens
statewave-connectors sync jira \
--host https://myorg.atlassian.net \
--projects ENG,PLATFORM \
--dry-runAdd comments and redact PII from free text:
statewave-connectors sync jira \
--host https://myorg.atlassian.net \
--projects ENG \
--include issues,comments \
--redact-email --redact-phone --redact-secrets \
--dry-run--dry-run maps and prints episodes without ingesting. To actually ingest, drop --dry-run and point at your Statewave instance:
export STATEWAVE_URL="http://localhost:8100"
export STATEWAVE_API_KEY="…" # if your instance requires one
statewave-connectors sync jira \
--host https://myorg.atlassian.net \
--projects ENG \
--since 2026-01-01Jira Server / Data Center
On-prem Jira differs from Cloud in three ways the connector handles via
--deployment server:
| | Cloud (--deployment cloud, default) | Server / Data Center (--deployment server) |
|---|---|---|
| REST API | /rest/api/3 | /rest/api/2 |
| Rich text | ADF (flattened to text) | plain text / wiki markup (used as-is) |
| Auth | email + API token (Basic) | personal access token (Bearer) or username + password (Basic) |
export JIRA_BASE_URL="https://jira.your-company.com"
export JIRA_PAT="…" # Data Center personal access token
statewave-connectors sync jira \
--deployment server \
--projects ENG,OPS \
--dry-runEverything else is shared: the same project:<KEY> subjects, ADF/plain-text →
text normalization, no-email user fields (Server users resolve by displayName →
username), redaction, project allowlist, and the issue/comment/transition kinds.
Verification status: the Server / Data Center path (v2 routing, Bearer-PAT and Basic auth, plain-text bodies, username display fields) is implemented against Atlassian's documented Server/DC API differences and covered by unit tests, but has not yet been validated against a live Jira Server / Data Center instance. Treat it as unverified until that round-trip is done; the Cloud path is exercised end-to-end. Tracking: statewave#193.
Webhook receiver (listen jira)
For real-time updates instead of (or alongside) polling, run the receiver — a
pure (Request) => Promise<Response> handler, mountable on the built-in daemon,
Vercel, Cloudflare, or Express. It dispatches the same jira.* kinds as the
pull connector.
export JIRA_WEBHOOK_SECRET="…" # the secret you set on the Jira admin webhook
export JIRA_BASE_URL="https://myorg.atlassian.net"
export STATEWAVE_URL="http://localhost:8100"
export STATEWAVE_API_KEY="…"
statewave-connectors listen jira --port 3000 --projects ENG,PLATFORM
# → http://0.0.0.0:3000/jira/eventsThen register a Jira admin webhook (Jira Settings → System → Webhooks, or
POST /rest/webhooks/1.0/webhook) pointing at the public address, set its
secret to the same value, and subscribe to the issue/comment events. Expose
the daemon publicly with a tunnel (ngrok / Cloudflare Tunnel) or your ingress.
Authentication — verified, not assumed. Jira admin webhooks sign every
callback: they compute an HMAC over the raw body using your secret and send it
as X-Hub-Signature: sha256=<hex>
(Atlassian docs).
The receiver recomputes that MAC with HMAC-SHA256 and rejects any mismatch in
constant time before parsing or ingesting — there is no unauthenticated code
path. It then dedups Jira's at-least-once retries, applies the optional
--projects allowlist, normalizes with the same ADF→text / no-email path the
pull connector uses, and ingests.
| Inbound webhookEvent | Episode kind |
|---|---|
| jira:issue_created, jira:issue_updated (open) | jira.issue.created |
| jira:issue_updated (status category "done") | jira.issue.resolved |
| comment_created, comment_updated | jira.comment.created |
jira:issue_deleted / comment_deleted and unrecognized events are acked
(HTTP 200) and skipped — there is no delete episode kind.
Programmatic use:
import { createJiraWebhookHandler } from "@statewavedev/connectors-jira";
const handler = createJiraWebhookHandler({
signingSecret: process.env.JIRA_WEBHOOK_SECRET!,
baseUrl: "https://myorg.atlassian.net",
projects: ["ENG"], // optional allowlist
redaction: { email: true }, // optional, parity with pull mode
statewaveUrl: process.env.STATEWAVE_URL!,
statewaveApiKey: process.env.STATEWAVE_API_KEY,
});
// export default handler; // Vercel / CloudflareTransitions & sprint context (opt-in)
Both are off by default — the default stays the lightweight issue/comment mapping — and neither adds extra API calls or crawls boards/sprints.
Status transitions (--include transitions) request expand=changelog on
the same issue search and emit one jira.issue.transition per status change
(from/to status, actor, timestamp):
statewave-connectors sync jira \
--host https://myorg.atlassian.net --projects ENG \
--include issues,transitions --dry-runThe webhook receiver does the same automatically: a jira:issue_updated
carrying a status change in its changelog emits both the issue snapshot and a
jira.issue.transition (actor + time taken from the event).
Sprint context (--sprint-field <id>) names your Jira Sprint custom field
(e.g. customfield_10020 — find it in the field's admin page or
GET /rest/api/3/field). When set, that one field is added to the same search;
the connector parses the array-of-objects sprint value into each issue's
metadata.sprints and a sprint:<name> related subject. There is no Agile
board/sprint enumeration — only the sprint already attached to the issue. The
legacy serialized-string sprint format is not parsed.
statewave-connectors sync jira \
--host https://myorg.atlassian.net --projects ENG \
--sprint-field customfield_10020 --dry-runSubject strategy
Each issue/comment lands under project:<KEY> (e.g. project:ENG) — the project the issue belongs to, so an agent can ask about a project's history. Override with --subject <value> to pin every episode to one subject instead.
Example episode
{
"subject": "project:ENG",
"kind": "jira.issue.resolved",
"text": "Ada L resolved issue ENG-128: Login fails on Safari\n\nUsers on Safari 17 hit a redirect loop after SSO.",
"occurred_at": "2026-05-20T14:03:00.000Z",
"source": {
"type": "jira.issue",
"id": "ENG-128",
"url": "https://myorg.atlassian.net/browse/ENG-128"
},
"metadata": {
"issue_key": "ENG-128",
"project_key": "ENG",
"status": "Done",
"status_category": "done",
"issue_type": "Bug",
"priority": "High",
"labels": ["auth", "safari"],
"assignee": "Ada L",
"reporter": "Bob R",
"related_subjects": ["issue:ENG-128", "assignee:Ada L"]
},
"idempotency_key": "…"
}To get this exact shape, run the quickstart above with --dry-run --json.
Status
Preview. Pull mode (issues, comments, opt-in status transitions, opt-in
sprint context) plus a webhook receiver (listen jira) with verified
X-Hub-Signature HMAC-SHA256. Jira Cloud is exercised end-to-end. Jira
Server / Data Center (--deployment server) is implemented + unit-tested but
not yet validated against a live on-prem instance — see Jira Server / Data
Center.
