Webhooks
Swake pushes real-time HTTP POST notifications to any URL you register whenever feedback lifecycle events occur. Use webhooks to build custom automations, connect to Zapier or Make, or integrate with any system that accepts HTTP.
Overview
Every webhook delivery is an HTTP POST to your endpoint with a JSON body, four signature headers, and a 10-second timeout. Deliveries are retried up to five times with exponential backoff on failure.
Signing secret — shown once at creation time and never again. Store it securely in an environment variable. You can rotate it at any time from Settings → Webhooks.
Plan Availability
| Plan | Endpoints per project |
|---|---|
| Free | 0 — not available |
| Pro | 2 |
| Business | 10 |
| Enterprise | Unlimited |
Registering an Endpoint
Open Project → Settings → Webhooks and click Add endpoint. Fill in:
| Field | Notes |
|---|---|
URL | Must be a public https:// address. HTTP, localhost, and private IP ranges are rejected. |
Description | Optional human-readable label for the endpoint. |
Events | One or more events to subscribe to (see Event Catalog). |
The signing secret is shown exactly once when the endpoint is created. Copy it immediately and store it in an environment variable — it cannot be retrieved again.
Payload Envelope
Every delivery POSTs a JSON body with this shape:
{
"id": "evt_01HY...",
"event": "feedback.created",
"timestamp": "2026-03-25T10:00:00Z",
"workspace_id": "01HY...",
"project_id": "01HY...",
"data": { }
}
| Field | Description |
|---|---|
id | Unique delivery ID (evt_<ULID>) |
event | Event name (see catalog below) |
timestamp | ISO-8601 UTC timestamp of when the event occurred |
workspace_id | Your workspace ULID |
project_id | Project ULID the event belongs to |
data | Event-specific payload (see each event below) |
Event Catalog
| Event | Trigger |
|---|---|
feedback.created | New feedback submitted via SDK or Portal |
feedback.status_changed | Status updated (open → in_progress → resolved → closed) |
feedback.assigned | Assignee changed |
feedback.commented | New team comment added |
feedback.tagged | Tag added or removed |
vote.cast | Vote cast on a voting board item |
vote.removed | Vote removed from a voting board item |
survey.response_completed | Survey / NPS response submitted |
roadmap.item_moved | Roadmap item moved between columns |
changelog.published | Changelog entry published |
test.ping | Manual test delivery from the portal |
Example Payloads
feedback.created
{
"id": "evt_01HY...",
"event": "feedback.created",
"timestamp": "2026-03-25T10:00:00Z",
"workspace_id": "01HY...",
"project_id": "01HY...",
"data": {
"id": "01HY...",
"type": "bug",
"title": "App crashes on login screen",
"description": "When I tap the login button...",
"status": "open",
"priority": "medium",
"created_at": "2026-03-25T10:00:00Z"
}
}
feedback.status_changed
{
"data": {
"id": "01HY...",
"old_status": "open",
"new_status": "in_progress",
"changed_by": "tm_01HY..."
}
}
Signature Verification
Every delivery includes four headers for verification:
X-Swake-Signature: sha256=a1b2c3d4e5f6...
X-Swake-Timestamp: 1711353600
X-Swake-Event: feedback.created
X-Swake-Delivery-Id: evt_01HY...
The signature is computed as HMAC-SHA256(secret, "{timestamp}.{rawBody}"). The payload is the Unix timestamp joined with the raw request body by a . separator.
Replay attack prevention: reject deliveries where abs(now − X-Swake-Timestamp) > 300 (5 minutes). Always use a timing-safe comparison function when comparing signatures.
Delivery Semantics
Swake considers any 2xx response a success. Non-2xx responses and network errors trigger the retry schedule below. Each attempt has a 10-second timeout.
Auto-disable: if an endpoint accumulates 50 consecutive failures across all event types, Swake automatically sets it to inactive and emails the workspace owner. Re-enable it from Settings → Webhooks once the issue is resolved.
Testing Webhooks
Click Test on any endpoint in the portal to send a test.ping delivery. The portal shows the HTTP status and response time of the test request.
{
"id": "evt_test_01HY...",
"event": "test.ping",
"timestamp": "2026-03-25T10:00:00Z",
"workspace_id": "01HY...",
"project_id": "01HY...",
"data": { "message": "Webhook is working!" }
}
SSRF Prevention
To protect against Server-Side Request Forgery, the following endpoint URLs are always rejected:
| Rejected pattern | Reason |
|---|---|
http://, file://, etc. | Non-HTTPS schemes |
localhost, *.local, 0.0.0.0 | Local hostnames |
127.x.x.x, 10.x.x.x, 172.16-31.x.x | IPv4 private ranges |
192.168.x.x, 169.254.x.x | Private / link-local IPv4 |
::1 | IPv6 loopback |
Delivery Log
Open Settings → Webhooks → {endpoint} → Deliveries to see a paginated log of all delivery attempts, including:
| Column | Description |
|---|---|
| Event name | The event type that triggered the delivery |
| HTTP status | Response status code returned by your server |
| Attempt | Which attempt number (1–5) |
| Timestamp / next retry | When it was delivered, or when the next retry is scheduled |
| Request payload | Full JSON body sent to your endpoint |
| Response body | First 1 KB of your server's response (on failure) |
Rotating the Signing Secret
If your secret is compromised:
| Step | Action |
|---|---|
| 1 | Go to Settings → Webhooks and click Rotate secret on the affected endpoint. |
| 2 | A new secret is generated. Copy it immediately — it is shown only once. |
| 3 | Update your server to use the new secret as quickly as possible. |
| 4 | Old secrets are immediately invalidated after rotation. |
There is a brief window after rotation where old deliveries signed with the previous secret may arrive. Update your server as quickly as possible after rotating.
API Reference
All endpoints are under /v1/portal/projects/:projectId/webhooks and require a valid JWT session cookie.
| Method | Path | Description |
|---|---|---|
| GET | /projects/:projectId/webhooks | List endpoints for a project |
| POST | /projects/:projectId/webhooks | Create endpoint (secret shown once) |
| PATCH | /projects/:projectId/webhooks/:id | Update URL / description / events / active |
| POST | /projects/:projectId/webhooks/:id/regenerate-secret | Rotate signing secret |
| POST | /projects/:projectId/webhooks/:id/test | Send test.ping delivery |
| DELETE | /projects/:projectId/webhooks/:id | Delete endpoint (cascades deliveries) |
| GET | /projects/:projectId/webhooks/:id/deliveries | Paginated delivery log |
| GET | /projects/:projectId/webhooks/:id/deliveries/:dId | Full delivery detail with payload |