13.2 Jira Service Management
integrations:jira-service-management
After this you can: register a JSM connection, take tickets as triggers, bind changes to tickets, write back through the audited door and resolve with evidence.
Before you start — tick each one:
- Lesson 5.2 — Triggers (
opschain learn 5.2)
In the GUI: the ticket shows as a typed external reference on the bound change — on its Inbox → Approvals row, in its governance block and on the change page's Service desk tab, which also carries the comment, transition, set-fields and resolve-with-evidence verbs — every JSM write is a row on the Operations area's Activity panel, and the connection is administered from Administration → Operate → JSM (create, edit, test, delete and webhook provisioning; the token is write-only).
Jira Service Management holds the ticket; OpsChain executes and governs
the change — and proves it. The integration binds the two: tickets
become typed references on governed changes, ticket events become
triggers, every OpsChain-side JSM write goes through one audited door,
and a request resolved via OpsChain closes with the evidence links
attached. This lesson takes one ticket, SUP-123, from arrival to
resolution. The guide opschain docs getting-started/integrations-jsm
is the day-to-day reference; the component reference is
opschain docs integrations-jsm.
Licence entitlement. The server side is gated on
integrations:jira-service-management. Binding a ticket as a typed reference on a change (lesson 10.1's governance block) is base — the gate is on the connection, the ingestion and the write door.
You need an Atlassian cloud site (an account email plus API token) or a
Jira Data Center instance (a personal access token), admin rights on
the OpsChain server, and a service account for the firings to run as
(opschain admin service-accounts create jsm-bot, lesson 7.3).
1. Register the connection
An Atlassian cloud site authenticates as an account (email + API
token); Jira Data Center uses a personal access token as a bearer
(omit --auth-email). The token never rides the command line and no
read ever returns it:
JSM_TOKEN=... opschain admin jsm-connections create limepoint-jsm \
--base-url https://yoursite.atlassian.net \
--auth-email support-bot@example.com \
--project-keys SUP \
--api-token-env JSM_TOKEN
Created JSM connection limepoint-jsm for https://yoursite.atlassian.net (api_token_ref secret-vault://jsm-connections/limepoint-jsm/credential)
opschain admin jsm-connections test limepoint-jsm
JSM connection limepoint-jsm: record validation succeeded
--project-keys scopes the connection: a write to a request outside
those projects refuses by name. test validates the record without
calling JSM.
2. Events in — tickets as triggers
Provision webhook ingestion on the signed inbound lane and point the Jira webhook (issue created/updated, comment created) at the printed path:
opschain admin jsm-connections provision-webhook limepoint-jsm --service-account jsm-bot
Provisioned Jira webhook ingestion for limepoint-jsm as inbound connection jsm-limepoint-jsm
Point the Jira webhook at <server URL>/api/hooks/jsm-limepoint-jsm
Signing secret (shown ONCE - configure it as the Jira webhook secret): 3f9c…
Jira Data Center signs natively — configure the shown secret as the
webhook secret and Jira sends a sha256 X-Hub-Signature. Each accepted
delivery lands one external:webhook:received event with typed fields
under event.data.jira (key, project, status, summary, priority,
labels, comment id), so a trigger reacts to, say, every new support
request:
# triage_new_request.yaml
code: triage_new_request
subject: external
events: [external:webhook:received]
conditions:
- event.data.jira.event eq jira:issue_created
- event.data.jira.project eq SUP
action_type: workflow_run
action_params:
workflow: triage
inputs:
request_key: ${{ event.data.jira.key }}
opschain triggers create support -f triage_new_request.yaml
Every firing executes as the connection's service account — never with ambient authority (the rule lesson 5.3 set for the generic lane).
3. Bind the change to the ticket
A governed change carries the ticket as a typed reference on its governance metadata block (lesson 10.1):
opschain approvals set-metadata <run-id> --ref jsm:request:SUP-123
governance block updated on 01a2fb10-… — owner jessica, category normal, 1 external reference(s)
opschain admin jsm-requests links --request SUP-123
REQUEST SYSTEM RELATIONSHIP RUN KIND TARGET
SUP-123 jsm - 01a2fb10-… change /projects/demo/environments/prod
links answers the other direction — which OpsChain subjects carry
this ticket — filtered to the subjects you may read. Approval policies
with require_change_reference count these references, so "no change
without a ticket" works for Atlassian shops out of the box.
4. Write back through the audited door
All OpsChain-side JSM writes go through one entitlement-gated, project-scoped, evented door:
opschain admin jsm-requests comment SUP-123 --body "Change scheduled for tonight's window."
Posted internal comment 10042 on SUP-123
opschain admin jsm-requests transition SUP-123 "In Progress"
opschain admin jsm-requests set-fields SUP-123 --add-label opschain
Updated fields on SUP-123
Comments are always JSM internal notes — there is deliberately no customer-visible-comment capability on this surface, so drafted customer replies stay internal until a human posts them as themselves. An unknown transition refuses by name and lists what is available.
5. Resolve with evidence
When the change lands, close the ticket with the proof attached:
opschain admin jsm-requests resolve SUP-123 \
--run <run-id> --transition Resolve \
--evidence-url https://opschain.example/changes/<run-id>
Resolved SUP-123 via 01a2fb10-… (Resolve) — typed reference recorded, evidence comment 10043 posted
One audited write records the jsm:request reference (relationship
resolves) on the change's governance block, posts the internal
evidence comment, and performs the resolving transition. The OpsChain
ledger stays the source of truth; JSM holds a copy with provenance.
Read the story back:
opschain events --type-search jsm --tail 4
WHEN ID TYPE NODE USER CORRELATION DETAIL
2026-10-27T13:10:02.118440Z 01a2fb20-… info:jsm:internal_comment - admin 01a2fb20-… SUP-123 · comment 10042 (41 chars)
2026-10-27T13:10:40.902113Z 01a2fb21-… info:jsm:transition - admin 01a2fb21-… SUP-123 · In Progress
2026-10-27T13:11:05.337208Z 01a2fb22-… info:jsm:fields - admin 01a2fb22-… SUP-123 · labels
2026-10-27T14:02:51.771004Z 01a2fb30-… info:jsm:resolution /projects/demo/environments/prod admin 01a2fb10-… SUP-123 · Resolve · comment 10043
Note what is not there: a comment event carries the comment id and length, never the note's content; a fields event carries field names, never values.
6. When something refuses
Everything refuses by name: the missing entitlement, the project scope,
a disabled connection, an unknown transition (listing what is
available), an unresolvable credential. Every write and every refusal
that matters is on the event stream — opschain events --type-search jsm tells the story.
Check yourself
- What may OpsChain post onto a ticket — internal notes, customer-visible comments, or both? Then: what three things does one
resolvewrite do, and which relationship does the recorded reference carry?
What you learned
- A JSM connection holds its token encrypted at rest (or pins a vault reference), is scoped to project keys, and serves a signed inbound lane whose firings run as a service account.
- A ticket is a typed
jsm:requestreference on a change's governance block, countable byrequire_change_referencepolicies. - Every OpsChain-side write goes through one audited door — internal
notes only — and
resolverecords the reference, posts the evidence comment and transitions the request in one write.
Next: lesson 13.3 — SIEM export (opschain learn 13.3), or back to your track (opschain learn).