Jira¶
Jira works with pg_relay through the webhook transport, posting to Jira's REST create-issue endpoint — the same zero-Processor-code pattern as Slack. This chapter is about creating issues (tickets, tasks, bugs) in Jira Software or Jira Service Management projects; paging through JSM's Operations alerting is a different API with its own recipe in Incident Alerting Platforms.
The headline for a ticketing integration: the created issue's key (OPS-123) comes back in the response body, classify_webhook_response returns it as the provider reference, and it lands on the notification's status row via set_status — so the application that queued the event can read the ticket key back, correlated by the notifier pk. The reference is asynchronous by nature: pgrelay.notify() returns the queue row id immediately; the ticket key appears on the status row after the Processor's tick and the HTTP round trip.
One-time Jira setup¶
- Create (or pick) a service account for the integration and, signed in as it, create an API token at
id.atlassian.com→ Security → API tokens. Jira Cloud's Basic authentication is the account's email plus this token — never the account password. - Note the project key (the prefix in issue keys, e.g.
OPS) and the exact issue type names available in that project (Task,Bug,Incident, …) — the create call names both. - If tickets should be assigned on creation, collect the assignees' account IDs. Jira Cloud identifies people only by opaque
accountId(a GDPR-driven change) — an email address or username in the assignee field is rejected.
Export the token on the Processor host (for a service, in its environment file — see Keeping Secrets Out of the Database):
Profile and message¶
The example uses REST API v2, which accepts the description as a plain string. v3 is identical except the description must be an Atlassian Document Format (ADF) object — see v2 or v3 below.
// profile
{
"url": "https://your-site.atlassian.net/rest/api/2/issue",
"auth": {"style": "basic_auth", "username": "[email protected]", "secret": "_env:JIRA_API_TOKEN"},
"timeout_seconds": 30
}
// message — Jira's own create-issue shape, sent verbatim
{
"fields": {
"project": {"key": "OPS"},
"issuetype": {"name": "Task"},
"summary": "disk alert: volume is filling",
"description": "relay_dev tablespace at 91% and growing.",
"labels": ["pgrelay-33"],
"priority": {"name": "High"}
}
}
Because message is sent verbatim, anything the create-issue endpoint accepts works. Everything lives under the one top-level fields object:
| Ticket concept | message field |
Format and notes |
|---|---|---|
| Project | fields.project |
{"key": "OPS"} — the project key |
| Issue type | fields.issuetype |
{"name": "Task"} — must exist in that project's issue type scheme |
| Title | fields.summary |
Plain text, one line, max 255 characters |
| Body | fields.description |
v2: a plain string (wiki markup allowed); v3: an ADF document object |
| Priority | fields.priority |
{"name": "High"} or {"id": "2"} — names from your priority scheme |
| Labels | fields.labels |
Array of strings; a label cannot contain spaces — the natural home for the pgrelay-{pk} dedup marker |
| Assignee | fields.assignee |
Cloud: {"id": "<accountId>"} — never an email or username; Data Center: {"name": "<username>"} |
| Components | fields.components |
[{"name": "database"}] — must already exist in the project |
| Due date | fields.duedate |
"YYYY-MM-DD" |
| Custom fields | fields.customfield_10xxx |
Numeric id from the field's settings page; the value's shape depends on the field type (text is a string, select is {"value": ...}) |
Fields not listed on the project's create screen are rejected with a 400 naming the field — Jira validates against the screen configuration, not just the field's existence.
Interpreting the response¶
201is success. The body is small and exact:{"id": "10000", "key": "OPS-123", "self": "https://..."}— returnkeyas the provider reference; it is the human-facing ticket identifier everything downstream will want.400asfailed— validation. The body'serrorsobject maps each offending field name to a message ("issuetype": "issue type is required") and itserrorMessagesarray carries screen-level complaints; either makes ideal detail text.401(bad email/token pair) and403(the account lacks Create Issue permission in that project) asfailed.404asfailed— almost always a wrong site URL or a project key that doesn't exist.429, any5xx, andhttp_status = 0(no response) asretry.
Duplicate tickets¶
pg_relay's delivery guarantee is at-least-once: a crash between Jira creating the issue and pg_relay's own commit re-sends the event. Unlike the alerting platforms, whose ingestion APIs are designed idempotent, Jira's create-issue endpoint has no idempotency key — a retried send creates a second issue. The mitigation is the pgrelay-{pk} label in the example: it makes duplicates identifiable (labels = "pgrelay-33" in JQL finds both copies, and an automation rule or a periodic query can close the younger one) without being able to prevent them. If duplicate tickets are operationally unacceptable, front Jira with an automation rule keyed on that label, or accept the alerting-platform pattern instead.
v2 or v3: the description field¶
Jira Cloud serves both API versions today. The only difference that matters here is description (and other rich-text fields): v2 takes a string, v3 requires an ADF document:
"description": {
"type": "doc", "version": 1,
"content": [{"type": "paragraph", "content": [{"type": "text", "text": "relay_dev tablespace at 91%"}]}]
}
For machine-generated tickets the v2 string is simpler and renders fine; choose v3 only if the tickets need rich formatting worth the ADF ceremony. Everything else in this chapter — auth, field names, response shape, classification — is identical across both.
Jira Data Center / Server¶
Self-hosted Jira swaps two things: authentication is a personal access token sent as a Bearer token ("auth": {"style": "bearer_header", "secret": "_env:JIRA_PAT"} — Basic with username/password also still works), and people are identified by name (username) rather than accountId. The endpoint is https://jira.example.com/rest/api/2/issue — Data Center has no v3.
Documentation-verified, not live-verified
Endpoints, auth mechanics, and response shapes in this chapter come from Atlassian's current documentation, not from live sends through pg_relay. Create one real ticket in a test project before relying on a profile in production.
Continue to ServiceNow — the same create-a-ticket pattern against the incident table, with a genuine dedup field Jira lacks.