Skip to content

Ticket Sync

Path: Evidence request detail (the Ticket Sync section)

Ticket Sync gives every evidence request an AuditBoard-style two-way link to an external ticketing system. From the evidence request you can open a ticket in your tracker, close it, and hold a single comment thread that stays in sync in both directions: an internal comment is pushed out to the ticket, and an external reply posted on the ticket flows back in as an inbound webhook and appears in the same thread. The provider is pluggable — the demo runs a self-contained mock; for real deployments a generic HMAC-signed webhook and a real Jira provider (Jira Cloud / Server-DC, REST v3) sit behind the same interface. ServiceNow is not yet supported (see below).

Prerequisites — a real provider is buyer-configured

Two-way ticket sync against a real tracker is not turnkey out of the box. It needs a provider that you configure. A stock Community Edition install does not have Jira/webhook credentials set, so the only out-of-the-box behavior is the mock provider (a self-contained demonstration that mints a synthetic mock-TCK-<n> and echoes a reply — it contacts no external system). The demo's working ticket flow is the mock, not a live integration.

To enable a real integration, set TICKET_PROVIDER and the matching credentials:

  • webhookTICKET_WEBHOOK_URL + TICKET_WEBHOOK_SECRET
  • jiraJIRA_BASE_URL + JIRA_API_TOKEN + JIRA_PROJECT_KEY (plus JIRA_USER_EMAIL for Jira Cloud Basic auth; leave it empty to send the token as a Bearer PAT on Jira Server / Data Center)

servicenow is not yet supported — selecting it always returns 503, even with SERVICENOW_* set (those settings are reserved for a future implementation).

Without credentials, selecting a real provider leaves it gated: every ticket operation raises ProviderNotConfigured, surfaced as HTTP 503 naming the keys to set (the inbound webhook similarly stays closed without TICKET_WEBHOOK_SECRET). The in-app Setup readiness checklist flags Ticket Sync accordingly. Full key list in Configuration below.

Key Elements

  • Ticket Sync section — On the evidence request detail modal. Shows the linked ticket (provider, external id, deep link, sync state) and the comment thread.
  • Comment thread — Interleaves internal comments (left in DefendFlow) and external comments (arrived from the ticket), each labelled by source.
  • Provider — The org-configured ticketing backend. The demo default is the mock provider; real deployments point at a webhook or Jira.

Creating and Closing a Ticket

A ticket is created from one evidence request, using the org-configured provider:

POST   /api/v1/orgs/{org_id}/evidence-requests/{request_id}/ticket        # create + link
GET    /api/v1/orgs/{org_id}/evidence-requests/{request_id}/ticket        # read the link
POST   /api/v1/orgs/{org_id}/evidence-requests/{request_id}/ticket/close  # close it
DELETE /api/v1/orgs/{org_id}/evidence-requests/{request_id}/ticket        # unlink it

Create takes no body — the provider derives the ticket title/description from the evidence request. The response is the ticket link: its provider, external_id, the external_url deep link, the sync_state (synced / pending / error), and timestamps.

  1. Open the evidence request and find the Ticket Sync section.
  2. Create ticket — the provider mints the external ticket and the link appears with a deep link to it.
  3. Close when the work is done, or Unlink to detach without closing.

An evidence request holds one ticket link at a time; creating a second returns 409 Conflict.

Two-Way Comment Sync

The comment thread is the heart of the integration. Both endpoints live under the evidence request:

GET  /api/v1/orgs/{org_id}/evidence-requests/{request_id}/comments  # the thread
POST /api/v1/orgs/{org_id}/evidence-requests/{request_id}/comments  # add an internal comment

Outbound — Posting a comment (body: { "body": "..." }) records it as an internal comment. If a ticket is linked, the same comment is pushed to the provider, and the provider's external comment id is stamped onto the internal comment for de-duplication. With the mock provider the call also returns an echo: a synthetic external reply, so you can see the round trip on the demo without any external system.

Inbound — When someone comments on the real ticket, the provider calls back:

POST /api/v1/webhooks/tickets/inbound

The handler resolves the event to a request via the external_id of a known ticket link (the wire never supplies a raw org or request id), then records the external comment as a external comment in the same thread and can apply an optional PBC status update. Inbound comments are de-duplicated on external_comment_id, so a redelivered webhook does not double-post.

Provider Matrix

The active provider is chosen by the ticket_provider setting; an unknown value falls back to the mock provider.

Provider Value Behavior Use when
Mock mock Mints a synthetic mock-TCK-<n> ticket and echoes an external reply when you comment, so the two-way flow is visible end-to-end. Contacts no external system. The default on the demo and any offline environment.
Webhook webhook Generic provider: outbound events are HMAC-signed POSTs to your endpoint; inbound replies arrive via the inbound webhook. You want to integrate any tracker via a small adapter you host.
Jira jira Real Jira integration (REST API v3): creates issues in your project, transitions them to a Done status on close, and posts comments. You run Jira Cloud (email + API token) or Jira Server/DC (PAT).
ServiceNow servicenow Not yet supported. Always returns 503 with an honest "not yet supported" message — even when SERVICENOW_* credentials are set. Don't — use jira, or bridge ServiceNow yourself via the webhook provider.

Demo default is Mock

The demo runs the mock provider. Creating a ticket returns a synthetic mock-TCK-<n> id, and posting a comment immediately echoes an external reply into the thread — so the round-trip sync is demonstrable with no external tracker wired up.

Jira is configure-your-own; ServiceNow is not implemented

The Jira provider is real but not active on the demo: until JIRA_BASE_URL, JIRA_API_TOKEN and JIRA_PROJECT_KEY are set, every ticket operation raises ProviderNotConfigured, surfaced as HTTP 503 with a message naming the keys to set. If Jira itself fails or rejects a call, the endpoints return HTTP 502 with the upstream error rather than fabricating success.

ServiceNow has no working implementation in this build. Selecting it always returns 503; setting its credentials does not change that.

The Jira Provider

With TICKET_PROVIDER=jira configured, the three outbound operations call the Jira REST API v3:

  • CreatePOST /rest/api/3/issue in JIRA_PROJECT_KEY with issue type JIRA_ISSUE_TYPE (default Task). The evidence-request title becomes the summary; the description carries the request description plus control / framework / due-date / owner context. The returned issue key (e.g. GRC-42) is the external_id and the deep link is <base>/browse/<key>. The due date is recorded in the description text only — the duedate field is not set because many Jira create screens do not include it (setting it would fail the whole create).
  • Close — reads the issue's available transitions and applies the first one whose target status category is Done (falling back to well-known names such as Done / Closed / Resolved). If the workflow has no such transition, the close fails with an explicit 502 naming the transitions that do exist.
  • CommentPOST /rest/api/3/issue/{key}/comment; the Jira comment id is stamped onto the internal comment for de-duplication.

Authentication: with JIRA_USER_EMAIL set, the token is sent as Basic auth (email:api_token — Jira Cloud). With it empty, the token is sent as a Bearer personal access token (Jira Server / Data Center).

Inbound replies from Jira are not polled: point a Jira automation rule / outgoing webhook at POST /api/v1/webhooks/tickets/inbound using the shared TICKET_WEBHOOK_SECRET HMAC scheme described below.

The Generic Webhook Provider

The webhook provider lets you integrate any tracker without a bespoke backend. Outbound ticket events (ticket.create, ticket.close, ticket.comment) are sent as JSON POSTs to your ticket_webhook_url, and your system sends external comments and status changes back to the inbound webhook.

HMAC signature scheme

Both directions are authenticated with an HMAC over the raw request body:

  • Header: X-Signature
  • Algorithm: HMAC-SHA256, formatted as sha256=<hex digest>
  • Signed value: the exact raw body bytes (outbound events serialize the JSON deterministically — compact separators, sorted keys — so the signature is stable)
  • Secret: the shared ticket_webhook_secret

Outbound, the provider sets X-Signature on every POST it sends you. Inbound, the webhook handler reads X-Signature, recomputes the expected signature over the raw body, and compares it in constant time. A missing or mismatched signature is rejected with 401. If no shared secret is configured, inbound sync is closed by default and the endpoint returns 503.

Inbound payload

The inbound webhook body is a JSON object keyed by the ticket's external_id:

{
  "external_id": "mock-TCK-1",
  "comment": {
    "external_comment_id": "ext-42",
    "author": "Jane (ext)",
    "body": "Attached the SOC 2 report."
  },
  "status": "submitted"
}

external_id is required and resolves the event to a linked ticket. The optional comment becomes an external comment (de-duplicated on external_comment_id), and the optional status updates the PBC request. The response is { "success": true, "applied": ... }.

Configuration

All keys are environment-driven settings:

Key Purpose
TICKET_PROVIDER Active provider: mock (default), webhook, or jira (servicenow is accepted but not yet supported — always 503)
TICKET_WEBHOOK_URL Outbound endpoint for the generic webhook provider
TICKET_WEBHOOK_SECRET Shared HMAC secret for outbound signing and inbound verification
JIRA_BASE_URL Jira instance base URL, e.g. https://your-org.atlassian.net
JIRA_API_TOKEN Jira API token (Cloud) or personal access token (Server/DC)
JIRA_USER_EMAIL Atlassian account email for Jira Cloud Basic auth; leave empty on Server/DC (token sent as Bearer)
JIRA_PROJECT_KEY Project key tickets are created in, e.g. GRC
JIRA_ISSUE_TYPE Issue type name for created tickets (default Task)
SERVICENOW_BASE_URL Reserved — ServiceNow sync is not yet supported; setting this activates nothing
SERVICENOW_API_TOKEN Reserved — ServiceNow sync is not yet supported; setting this activates nothing

A provider is considered configured only when its credentials are present; until then it stays gated and any ticket operation returns 503. The inbound webhook is similarly gated on TICKET_WEBHOOK_SECRET — without it, inbound sync stays closed.

Who Can Do What

Action Required permission
View the ticket link and comment thread Org read
Add a comment Org read
Create / close / unlink a ticket Org configure
Inbound webhook None — authorized by the HMAC X-Signature, not an org login

Reading the thread and leaving a comment need only read access, so anyone working the evidence request can participate. Creating, closing, or unlinking the ticket requires the org configure permission. The inbound webhook is a separate, unauthenticated-by-login path: it is authorized solely by the HMAC signature over the raw body.