Jira Service Management Operations — Ingest Setup

This guide shows how to send JSM Operations alerts to Culprit in real time using a JSM Webhook integration.

JSM Operations inherited Opsgenie's alerting and webhook engine, so the payload Culprit receives is identical to Opsgenie's — only the source attribution and the setup menu path differ. Teams migrating off Opsgenie (Atlassian ends Opsgenie support on April 5, 2027) can move their Culprit integration by re-creating the webhook in JSM and pointing it at the jsm-ops endpoint below; no other change is needed.

Plan requirement: webhook (outgoing) integrations in JSM Operations require a JSM Premium or Enterprise plan.


Endpoint

POST https://ingest.theculprit.ai/ingest/<tenant_id>/<service_name>/jsm-ops

Replace <tenant_id> and <service_name> with the values shown on your service's settings page.

Authentication is a Bearer header (JSM webhook integrations can send custom headers):

Authorization: Bearer <ingest_token>

Setup in JSM Operations

  1. In Jira Service Management, open Operations → Settings → Integrations.
  2. Click Add integration and choose Webhook.
  3. Give the integration a name (e.g. Culprit).
  4. Paste your Culprit endpoint URL into the Webhook URL field.
  5. Add a custom header named Authorization with the value Bearer <ingest_token>.
  6. In the action mapping, enable only "alert is created" and "alert is closed". Other actions (acknowledge, add note, etc.) are workflow chatter — Culprit rejects them with 400 unrecognized_payload, so leaving them enabled only produces failed deliveries in the webhook log.
  7. Tick "add alert description" so the alert description is included in the payload (Culprit uses it as the message fallback).
  8. Save the integration.

Getting your ingest token

  1. Open the Culprit dashboard → Services → select the service.
  2. In the Ingest token section, click Issue.
  3. Copy the token shown — it is displayed once. After you navigate away, only the token prefix is visible.

To rotate a compromised token, click Rotate. The previous token remains valid for the grace period shown, giving you time to update the JSM webhook header without an interruption.


How Culprit maps JSM Operations webhooks

One webhook becomes one Culprit event. Only the Create and Close actions are ingested. The mapping is identical to the Opsgenie adapter except the event source is jsm-ops.

| JSM field | Culprit event field | Notes | |---|---|---| | alert.message | event_type | Falls back to opsgenie.alert | | alert.priority | severity | P1 → CRITICAL; P2 → HIGH; P3 → MEDIUM; P4/P5 → LOW. When absent: Close → LOW, Create → MEDIUM | | alert.message | message | Falls back to alert.description, then Opsgenie alert | | action, alert.*, integrationName | context | action, alias, alertId, tinyId, entity, tags, integrationName, the alert's own source field, createdAt/updatedAt, and the full original body are preserved |

Other actions (Acknowledge, AddNote, AssignOwnership, ...) and unknown shapes are rejected with 400 unrecognized_payload — they are never vaulted. Alert state, not workflow activity, is what Culprit correlates.

Note on fidelity: JSM Operations de-duplicates alerts by alias before its webhook fires, so Culprit sees the JSM alert stream, not the raw monitor signals behind it. For the highest-fidelity correlation, point your underlying monitoring tools (Prometheus, Datadog, ...) at their native Culprit adapters directly where practical.

PII in alert messages, descriptions, entities, or tags (email addresses, IP addresses, API keys) is detected and tokenized before any storage or notification. The original values are preserved encrypted and are visible only in the incident detail view after authentication.


Worked example — alert closed

JSM Operations sends:

{
  "action": "Close",
  "alert": {
    "alertId": "052652ac-5d1c-464a-812a-7dd18bbfba8c",
    "alias": "checkout-db-conn-pool",
    "message": "Connection pool exhausted on checkout-db"
  }
}

Culprit event (before tokenization):

{
  "event_type": "Connection pool exhausted on checkout-db",
  "severity": "low",
  "source": "jsm-ops",
  "message": "Connection pool exhausted on checkout-db",
  "context": {
    "action": "Close",
    "alias": "checkout-db-conn-pool",
    "alertId": "052652ac-5d1c-464a-812a-7dd18bbfba8c",
    "raw": { ... }
  }
}

This event flows through the standard pipeline: encrypted vault storage → PII tokenization → storm check → embedding → correlation → root-cause analysis.


Successful response

{ "accepted": true, "ids": ["<vault_id>"] }

Error responses

| Status | Body | Meaning | |---|---|---| | 401 | { "error": "missing_auth" } | No Authorization header | | 401 | { "error": "invalid_ingest_token" } | Token is incorrect or has been rotated/revoked | | 400 | { "error": "unrecognized_payload" } | Body is not valid JSON, has no alert object, or the action is not Create/Close | | 403 | { "error": "service_disabled" } | The service exists but monitoring is paused | | 404 | { "error": "service_deleted_or_not_found" } | The service does not exist | | 429 | { "error": "rate_limited" } | Storm protection active (sustained high volume) |