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).
| Event | Fires when | data fields |
|---|---|---|
| CampaignLaunched | A campaign is launched. | campaignId, name, status |
| CampaignClosed | A campaign is closed. | campaignId, name, status |
| ReportSealed | The 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 |
| TesterCompleted | A tester finishes every required task of a campaign (preview sessions excluded). | campaignId, campaignName, campaignTesterId, testerName, verdict, issuesReported |
| IssueCreated | An issue is created from the dashboard or the public API. | issueId, campaignId, title, severity, type, status, pageUrl |
| IssueUpdated | An issue moves to any non-terminal status (Acknowledged, InProgress, Fixed, …). | issueId, campaignId, title, oldStatus, newStatus, reason |
| IssueClosed | An issue is closed — including Won’t Fix and Duplicate. | issueId, campaignId, title, oldStatus, newStatus, reason |
| RetestRequested | A fixed issue is sent back to the reporting tester for retest. | issueId, campaignId, title, oldStatus, newStatus, reason |
| RetestCompleted | A 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:
- 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.
- 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).
- 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