Developer docs

Webhook events reference

DeploySeal delivers signed HTTP POST payloads to your endpoints when campaigns and issues change. Endpoints are configured per organisation under Settings → Integrations → Outbound Webhooks, where each endpoint gets a signing secret (shown once) and an optional event filter — an endpoint subscribed to no specific events receives all of them.

Delivery format

Every delivery is a JSON POST with three headers: X-DeploySeal-Event (the event type), X-DeploySeal-Delivery (a unique id per delivery attempt), and X-DeploySeal-Signature (see verification below). The body is an envelope with a unique event id, the eventType, an occurredAt UTC timestamp, and the event-specific data object:

POST https://example.com/hooks/deployseal
Content-Type: application/json
X-DeploySeal-Event: IssueCreated
X-DeploySeal-Delivery: 0f8fad5b-d9cb-469f-a165-70867728950e
X-DeploySeal-Signature: sha256=4f374d6a1c8f...

{
  "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "eventType": "IssueCreated",
  "occurredAt": "2026-09-03T14:21:07.415Z",
  "data": {
    "issueId": "9b2c5d4e-1f2a-4b3c-8d7e-6f5a4b3c2d1e",
    "campaignId": "1a2b3c4d-5e6f-4a8b-9c0d-1e2f3a4b5c6d",
    "title": "Checkout button unresponsive",
    "severity": "High",
    "type": "Bug",
    "status": "New",
    "pageUrl": "https://staging.example.com/checkout"
  }
}

Event types

These events are emitted today. All data fields are camelCase; status and severity values are the same strings the dashboard shows (e.g. InProgress, RetestRequested).

EventFires whendata fields
CampaignLaunchedA campaign is launched.campaignId, name, status
CampaignClosedA campaign is closed.campaignId, name, status
ReportSealedThe readiness report is sealed — every required signer has stamped or been waived on one record version.campaignId, name, siteId, siteName, sealState, signedCount, waivedCount, requiredCount, gate, readinessLevel, sealedVersion, signedBy, decision
TesterCompletedA tester finishes every required task of a campaign (preview sessions excluded).campaignId, campaignName, campaignTesterId, testerName, verdict, issuesReported
IssueCreatedAn issue is created from the dashboard or the public API.issueId, campaignId, title, severity, type, status, pageUrl
IssueUpdatedAn issue moves to any non-terminal status (Acknowledged, InProgress, Fixed, …).issueId, campaignId, title, oldStatus, newStatus, reason
IssueClosedAn issue is closed — including Won’t Fix and Duplicate.issueId, campaignId, title, oldStatus, newStatus, reason
RetestRequestedA fixed issue is sent back to the reporting tester for retest.issueId, campaignId, title, oldStatus, newStatus, reason
RetestCompletedA retest concludes (RetestFixed or RetestFailed).issueId, campaignId, title, oldStatus, newStatus, reason

The following event types are reserved and selectable in the endpoint filter but are not emitted yet: CampaignCreated, TesterInvited, TesterStarted, TesterApproved, TesterRejected, FeedbackSubmitted. Subscribing to them is safe — they simply won't fire until a future release.

Want to see a full payload before wiring anything up? With an API key, GET /api/v1/events/{event}/sample returns a representative envelope for any emitted event, built by the same code that builds the real deliveries — see the API reference.

Verifying signatures

Each payload is signed with HMAC-SHA256 over the raw request body, using your endpoint's signing secret as the UTF-8 key. The hex-encoded (lowercase) digest is sent as X-DeploySeal-Signature: sha256=<hex>. Always compare in constant time, and verify before parsing.

TypeScript (Node)

import { createHmac, timingSafeEqual } from 'node:crypto'

// rawBody MUST be the exact bytes received — parse JSON only after verifying.
export function verifyDeploySealSignature(
  rawBody: string,
  signatureHeader: string, // value of X-DeploySeal-Signature
  signingSecret: string    // shown once when the endpoint was created
): boolean {
  const expected =
    'sha256=' + createHmac('sha256', signingSecret).update(rawBody, 'utf8').digest('hex')
  const a = Buffer.from(signatureHeader)
  const b = Buffer.from(expected)
  return a.length === b.length && timingSafeEqual(a, b)
}

C#

using System.Security.Cryptography;
using System.Text;

// rawBody MUST be the exact bytes received — parse JSON only after verifying.
static bool VerifyDeploySealSignature(string rawBody, string signatureHeader, string signingSecret)
{
    var hash = HMACSHA256.HashData(
        Encoding.UTF8.GetBytes(signingSecret),
        Encoding.UTF8.GetBytes(rawBody));
    var expected = "sha256=" + Convert.ToHexString(hash).ToLowerInvariant();
    return CryptographicOperations.FixedTimeEquals(
        Encoding.UTF8.GetBytes(signatureHeader),
        Encoding.UTF8.GetBytes(expected));
}

Retries & automatic disabling

  • Any 2xx response counts as delivered. Each attempt times out after 10 seconds.
  • A failed delivery is retried up to 3 attempts with back-off of 1s, then 5s, then 30s between attempts. Each retried attempt carries a fresh X-DeploySeal-Delivery id but the same event id — deduplicate on the body's id if you need exactly-once processing.
  • After 5 consecutive failed deliveries the endpoint is automatically disabled; re-enable it from the webhooks settings page once your receiver is healthy.
  • Events are queued durably server-side (transactional outbox), so an event is never lost between the action happening and delivery being attempted. Delivery attempts, response codes, and the first 2 KB of your response body are visible in the per-endpoint delivery log.
  • Redirects are never followed. Endpoints must be reachable over the public internet.

Connect Zapier or Make

The DeploySeal app for Zapier subscribes to these events for you (as REST hooks) and adds a Create issue action — connect it with an API key from Settings → Integrations → API keys. Endpoints it creates show up here labelled via API. Without the app, both tools can still receive DeploySeal webhooks directly:

  1. In Zapier, create a Zap with the trigger Webhooks by Zapier → Catch Hook (in Make: add a Webhooks → Custom webhook module) and copy the generated URL.
  2. In DeploySeal, go to Settings → Integrations → Outbound Webhooks, add an endpoint with that URL, and pick the events you care about (e.g. only IssueCreated).
  3. Trigger a test event (create an issue) so the tool captures the payload shape, then map fields from the data object into your action — a Slack message, a spreadsheet row, a Jira ticket.

Zapier and Make URLs are unguessable, which is adequate for most teams; for defense in depth, add a signature-verification code step using the snippet above.

Ready to wire something up? Endpoints live in your workspace settings.

Open webhook settings