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, and a generic HMAC-signed webhook, Jira, and ServiceNow sit behind the same interface for real deployments.
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/ServiceNow/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:
webhook—TICKET_WEBHOOK_URL+TICKET_WEBHOOK_SECRETjira—JIRA_BASE_URL+JIRA_API_TOKENservicenow—SERVICENOW_BASE_URL+SERVICENOW_API_TOKEN
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, Jira, or ServiceNow.
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.
- Open the evidence request and find the Ticket Sync section.
- Create ticket — the provider mints the external ticket and the link appears with a deep link to it.
- 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:
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 behind the same interface. | You run Jira and supply its base URL + API token. |
| ServiceNow | servicenow |
Real ServiceNow integration behind the same interface. | You run ServiceNow and supply its instance URL + API token. |
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 and ServiceNow are configure-your-own
Jira and ServiceNow are implemented behind the same interface but are not active on
the demo. Until their credentials are set, every ticket operation raises
ProviderNotConfigured, which the endpoints surface as HTTP 503 (with a message
naming the keys to set) rather than crashing the request.
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, jira, or servicenow |
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 |
JIRA_API_TOKEN |
Jira API token |
SERVICENOW_BASE_URL |
ServiceNow instance base URL |
SERVICENOW_API_TOKEN |
ServiceNow API token |
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.