Skip to content

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"}}
}
}
  • id is the stable logical event id. Redeliveries preserve it.
  • version is the payload schema version. New fields may be added within a version; breaking changes increment it and are documented.
  • occurredAt is 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, and 5xx responses and transport timeouts are retried automatically: 8 attempts with exponential backoff starting at 15 seconds and capped at 1 hour. A Retry-After header on 429 is honored.
  • Redirects are never followed; a 3xx is a terminal failure. Other 4xx responses are terminal as well.
  • After the attempt budget is exhausted the delivery is marked failed (rejected) or dead (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.