Webhooks
Hamstik outgoing Webhooks deliver Work Item events to your own HTTPS endpoints.
Each delivery is signed with a per-endpoint secret so your receiver can verify
that the request came from Hamstik and was not modified in transit. Webhooks
are an Organization capability: Organization owners and administrators
configure them in Organization settings → Webhooks, or through the Public
API with a Personal Access Token granted the webhook:write scope.
Event types
Webhook endpoints subscribe to an explicit registry of event types. New event types join the registry deliberately; endpoints never receive events they did not subscribe to.
| Event type | Fires when |
|---|---|
work_item.created |
A Work Item is created. |
work_item.updated |
A Work Item changes without a status change. |
work_item.transitioned |
A Work Item status changes (including a mixed update that also changes status). |
work_item.commented |
A comment is added to a Work Item. |
A mixed mutation that changes status and other fields produces exactly one
work_item.transitioned event whose changes object carries every field
change, so subscribers never see duplicate notifications for one save.
Payload envelope
Every delivery is a JSON POST with this versioned envelope:
{ "id": "01987c1e-...", "type": "work_item.transitioned", "version": 1, "occurredAt": "2026-09-24T12:00:00.000Z", "organization": {"id": "..."}, "project": {"id": "...", "key": "HAM"}, "actor": {"id": "...", "publicId": "usr_...", "name": "Steven Hildreth"}, "data": { "workItem": { "id": "...", "key": "HAM-12", "title": "Ship it", "type": "task", "status": "in_progress", "priority": "high", "storyPoints": 3, "dueDate": null, "revision": 4 }, "changes": {"status": {"from": "todo", "to": "in_progress"}} }}idis the stable logical event id. Redeliveries preserve it.versionis the payload schema version. New fields may be added within a version; breaking changes increment it and are documented.occurredAtis when the domain change happened, not when the delivery was attempted.- Work Item summaries use the same public fields as the Public API. Consumers
that need more detail resolve the Work Item through
GET /api/v1/organizations/{organizationSlug}/projects/{projectKey}/work-items/{key}. - Deleted sources are never disclosed: a Work Item deleted before delivery yields no assignee resolution and no protected content.
Test deliveries use the reserved event type webhook.test with a
data.message that states it is a test; this type is never subscribable.
Request headers
| Header | Value |
|---|---|
X-Hamstik-Event |
The event type, for example work_item.created. |
X-Hamstik-Event-Id |
The logical event id (matches the envelope id). |
X-Hamstik-Delivery |
A unique id for this delivery attempt. |
X-Hamstik-Timestamp |
Unix timestamp of the attempt. |
X-Hamstik-Signature-256 |
HMAC-SHA256=<hex digest>. |
Verifying signatures
Compute HMAC-SHA256 over exactly this concatenation, using the signing
secret from creation or rotation:
{timestamp}.{deliveryId}.{rawRequestBody}Compare the hex digest against the value after HMAC-SHA256= in
X-Hamstik-Signature-256 using a constant-time comparison. Reject requests
whose X-Hamstik-Timestamp is older than your tolerance (five minutes is a
reasonable default) to bound replay. The signed material covers the timestamp,
the delivery id, and the exact body bytes.
Retries, failures, and redelivery
- A delivery succeeds when your endpoint answers
2xx. 408,425,429, and5xxresponses and transport timeouts are retried automatically: 8 attempts with exponential backoff starting at 15 seconds and capped at 1 hour. ARetry-Afterheader on429is honored.- Redirects are never followed; a
3xxis a terminal failure. Other4xxresponses are terminal as well. - After the attempt budget is exhausted the delivery is marked
failed(rejected) ordead(budget exhausted) in the delivery history — failures stay visible instead of being silently discarded. - Administrators can redeliver any event from the delivery history or the Public API. A redelivery preserves the original event id, grants a fresh attempt budget, and is marked as a manual delivery in the history.
Delivery history is retained for 30 days.
Secrets, rotation, and storage
- Each endpoint has an independent
whsec_…signing secret generated by Hamstik, shown exactly once at creation or rotation, encrypted at rest with Hamstik’s AES-256-GCM secret protection, and never returned by reads. - Rotation generates a new secret immediately. The previous secret keeps validating deliveries for 24 hours so you can update your receiver without dropped events.
- Webhook administrative operations — created, updated, enabled/disabled, deleted, secret rotated, test delivery requested, manual redelivery — are captured by the audit trail. Audit details never contain secrets.
Endpoint rules
- Only HTTPS destinations are accepted.
- Destinations must not embed credentials.
- The shared outbound policy applies at delivery time: loopback, link-local, private, and cloud metadata ranges are resolved and revalidated per attempt, with DNS pinning, so a hostname cannot later redirect deliveries into your internal network.
- An Organization can have at most 20 endpoints; each endpoint may subscribe to at most 20 event types.
Reliability model
Webhook events are recorded in the same transaction as the Work Item or
comment change that produced them, then fanned out to subscribed endpoints by
a dedicated delivery worker
(pnpm worker:webhooks; the production image runs it as the webhook-worker
service). At-least-once semantics mean your endpoint may occasionally see the
same event id twice — deduplicate on X-Hamstik-Event-Id plus
X-Hamstik-Delivery. A failing endpoint cannot delay any other integration:
deliveries are processed independently, and the event outbox stays open until
every delivery of the event reaches a terminal state.
