Search docs ⌘K
esc
Type to search across all documentation pages
Webhooks

Webhooks

Webhook subscription management and event fan-out. Register HTTPS endpoints to receive real-time notifications when events happen in your project — KV changes, channel messages, job completions, and more.

How it works: Create a webhook with one or more topics. When a matching event fires, SaaSignal POSTs a signed JSON payload to your url. Failed deliveries are retried with exponential backoff. You can inspect delivery history and manually retry failures.

Available Topics

Topics support exact match, * wildcard (all events), and prefix wildcards (e.g. jobs.* matches jobs.completed, jobs.failed, jobs.dead).

Topic
Fired when
Payload fields
kv.set
A key-value pair is created or updated
key, value, ttl, expires_at
kv.delete
A key is deleted
key
channels.published
A message is published to a channel
channel, event, event_id, delivered
jobs.completed
A job finishes successfully
job_id, name, run_id, attempt, result
jobs.failed
A job fails but has retries remaining
job_id, name, run_id, attempt, error, retry_at
jobs.dead
A job exhausts all retry attempts
job_id, name, run_id, attempt, error, max_attempts
ai.operation.completed
An async AI operation finishes successfully
operation_id, model, kind
ai.operation.failed
An async AI operation fails
operation_id, model, reason
ai.index.created
A managed retrieval index is created
index_id, name, dimensions
ai.index.deleted
A managed retrieval index is deleted
index_id
logistics.geo.created
A geo entity is created
entity_id, type, name
logistics.geo.updated
A geo entity is updated
entity_id
logistics.geo.deactivated
A geo entity is deactivated
entity_id
logistics.geofence.created
A geofence is created
fence_id, name, type
logistics.geofence.deleted
A geofence is deleted
fence_id
logistics.geofence.entered
A tracked entity enters a geofence
entity_id, fence_id, fence_name, lat, lng
logistics.geofence.exited
A tracked entity exits a geofence
entity_id, fence_id, fence_name, lat, lng
logistics.eta_fence.created
An ETA fence is created
fence_id, name, dest_lat, dest_lng, threshold_min
logistics.eta_fence.deleted
An ETA fence is deleted
fence_id
logistics.eta_fence.breach
Entity's driving ETA drops below threshold
entity_id, fence_id, fence_name, eta_min, threshold_min, lat, lng, dest_lat, dest_lng
logistics.eta_fence.cleared
Entity's driving ETA rises above threshold
entity_id, fence_id, fence_name, eta_min, threshold_min, lat, lng
logistics.ping
A tracking ping is received
entity_id, lat, lng, speed, heading
delivery.driver.online
Driver goes online
driver_id, name
delivery.driver.offline
Driver goes offline
driver_id, name
delivery.order.assigned
Order assigned to driver
order_id, driver_id
delivery.order.accepted
Driver accepted order
order_id, driver_id
delivery.order.in_transit
Driver in transit to pickup
order_id, driver_id
delivery.order.picked_up
Order picked up
order_id, driver_id
delivery.order.delivered
Order delivered
order_id, driver_id
delivery.order.failed
Delivery failed
order_id, driver_id, reason
delivery.order.cancelled
Order cancelled
order_id
delivery.zone.created
Delivery zone created
zone_id, name
delivery.zone.updated
Delivery zone updated
zone_id
delivery.zone.deleted
Delivery zone deleted
zone_id
delivery.hub.updated
Hub updated
hub_id, name
delivery.hub.deleted
Hub deleted
hub_id
custom.*
User-published custom events via POST /infra/webhooks/publish
Any JSON payload
test.ping
Sent when testing a webhook
message

Payload envelope

All payloads are wrapped: { event_id, topic, timestamp, data: { ...fields } }. Deliveries include an HMAC-SHA256 signature in the X-Saasignal-Signature: sha256={hex} header.

Publish Event

POST
/infra/webhooks/publish
webhooks:publish

Publish a custom event to trigger matching webhooks. Topics must start with custom. (e.g. custom.order.created). Payload data is capped at 64 KB. Base publish cost is charged up front; each downstream delivery attempt is billed separately and varies slightly with payload size.

curl
curl -X POST https://api.saasignal.saastemly.com/infra/webhooks/publish \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"topic":"...","data":{"key":"value"}}'
json — 202 Accepted
{ "status": "ok" }
typescript
await ss.infra.webhooks.publish('custom.order.created', {
  order_id: 'ord_123',
  customer: 'cust_456',
})
  1. Use the SDK or API to publish custom events
  2. Topics must start with custom. (e.g. custom.order.created)
  3. All webhooks subscribed to a matching topic pattern will receive the event
Body field
Type
Description
topic required
string
Event topic. Must start with custom. (e.g. custom.order.created). Max 256 chars.
1–256 charspattern ^[\w.:@-]+$
data required
any
Arbitrary JSON payload to include in the webhook delivery. Max 64 KB.
Error responses
400 Bad request
401 Unauthorized
402 Insufficient tokens
429 Rate limited

Create Webhook

POST
/infra/webhooks
webhooks:write

Register a webhook subscription for event topics. Topics: kv.set, kv.delete, channels.published, jobs.completed, jobs.failed, jobs.dead, logistics.* (geo/geofence/tracking events), or * for all. Supports prefix wildcards (e.g. jobs.*).

curl
curl -X POST https://api.saasignal.saastemly.com/infra/webhooks \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"name":"example","url":"https://app.acme.com/webhooks/saasignal","topics":["kv:write"]}'
json — 201 Created
{ "status": "ok" }
typescript
await ss.infra.webhooks.create({
  name: 'order-events',
  url: 'https://app.acme.com/webhooks/saasignal',
  topics: ['kv.set', 'jobs.*'],
  secret: 'whsec_...',
})
  1. Open Dashboard and select your project
  2. Go to Webhooks in the sidebar
  3. Click Add Webhook
  4. Enter a name, endpoint URL, and select the topics to subscribe to
  5. Click Create
Body field
Type
Description
name required
string
Display name for this webhook (1–128 chars)
1–128 chars
url required
string
HTTPS endpoint URL that receives webhook POST requests
topics required
array
Event topics to subscribe to (e.g. kv.set, jobs.*, *). See Available Topics.
1–50 items
secret
string
Signing secret for HMAC-SHA256 verification (16–256 chars). Auto-generated if omitted.
16–256 chars
enabled
boolean
Whether the webhook is active. Default true.
Error responses
401 Unauthorized
402 Insufficient tokens
429 Rate limited

List Webhooks

GET
/infra/webhooks
webhooks:read0.0000008 tokens

List all webhooks for the project.

curl
curl https://api.saasignal.saastemly.com/infra/webhooks \
  -H "Authorization: Bearer sk_live_..."
json — 200 OK
{ "status": "ok" }
typescript
await ss.infra.webhooks.list()
  1. Open Dashboard and select your project
  2. Go to Webhooks to see all registered webhooks with their status and topics
Query param
Type
Description
limit
integer
Max results per page. Range 1–100, default 25.
range ≥1 .. ≤100default 25
cursor
string
Pagination cursor from a previous response.
enabled
boolean
Filter by enabled status.
values: true, false
Error responses
401 Unauthorized
402 Insufficient tokens
429 Rate limited

Get Webhook

GET
/infra/webhooks/{webhook_id}
webhooks:read0.0000008 tokens

Retrieve a single webhook with delivery stats.

curl
curl https://api.saasignal.saastemly.com/infra/webhooks/{webhook_id} \
  -H "Authorization: Bearer sk_live_..."
json — 200 OK
{ "status": "ok" }
typescript
await ss.infra.webhooks.get('wh_01JNXS...')
  1. Open Dashboard and select your project
  2. Go to Webhooks and click on any webhook to view its details, secret, and delivery history
Path param
Type
Description
webhook_id required
string
Webhook ID
Error responses
401 Unauthorized
402 Insufficient tokens
404 Not found
429 Rate limited

Update Webhook

PATCH
/infra/webhooks/{webhook_id}
webhooks:write0.0000053 tokens

Update webhook fields.

curl
curl -X PATCH https://api.saasignal.saastemly.com/infra/webhooks/{webhook_id} \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{}'
json — 200 OK
{ "status": "ok" }
typescript
await ss.infra.webhooks.update('wh_01JNXS...', { enabled: false })
  1. Open Dashboard and select your project
  2. Go to Webhooks and click on the webhook
  3. Edit the URL, topics, or toggle enabled/disabled
  4. Click Save
Path param
Type
Description
webhook_id required
string
Webhook ID
Body field
Type
Description
name
string
Updated display name
1–128 chars
url
string
Updated HTTPS endpoint URL
topics
array
Updated list of event topics
1–50 items
secret
string
Updated signing secret
16–256 chars
enabled
boolean
Enable or disable the webhook
Error responses
401 Unauthorized
402 Insufficient tokens
404 Not found
429 Rate limited

Delete Webhook

DELETE
/infra/webhooks/{webhook_id}
webhooks:write0.0000053 tokens

Permanently delete a webhook and its delivery history.

curl
curl -X DELETE https://api.saasignal.saastemly.com/infra/webhooks/{webhook_id} \
  -H "Authorization: Bearer sk_live_..."
typescript
await ss.infra.webhooks.delete('wh_01JNXS...')
  1. Open Dashboard and select your project
  2. Go to Webhooks and click on the webhook
  3. Click Delete to remove it
Path param
Type
Description
webhook_id required
string
Webhook ID
Error responses
401 Unauthorized
402 Insufficient tokens
404 Not found
429 Rate limited

Test Webhook

POST
/infra/webhooks/{webhook_id}/test
webhooks:write0.0000053 tokens

Send a test ping to the webhook URL and return the response status.

curl
curl -X POST https://api.saasignal.saastemly.com/infra/webhooks/{webhook_id}/test \
  -H "Authorization: Bearer sk_live_..."
json — 200 OK
{ "status": "ok" }
typescript
await ss.infra.webhooks.test('wh_01JNXS...')
  1. Open Dashboard and select your project
  2. Go to Webhooks and click on the webhook
  3. Click Test to send a test.ping event
Path param
Type
Description
webhook_id required
string
Webhook ID to send a test.ping event to
Error responses
401 Unauthorized
402 Insufficient tokens
404 Not found
429 Rate limited

List Deliveries

GET
/infra/webhooks/{webhook_id}/deliveries
webhooks:read0.0000008 tokens

Paginated delivery log for a webhook.

curl
curl https://api.saasignal.saastemly.com/infra/webhooks/{webhook_id}/deliveries \
  -H "Authorization: Bearer sk_live_..."
json — 200 OK
{ "status": "ok" }
typescript
await ss.infra.webhooks.listDeliveries('wh_01JNXS...')
  1. Open Dashboard and select your project
  2. Go to Webhooks and click on a webhook
  3. The Deliveries tab shows all delivery attempts with status, timing, and response codes
Path param
Type
Description
webhook_id required
string
Webhook ID
Query param
Type
Description
limit
integer
Max results per page. Default 25.
range ≥1 .. ≤100default 25
cursor
string
Pagination cursor
status
string
Filter by delivery status: success or failed
Error responses
401 Unauthorized
402 Insufficient tokens
429 Rate limited

Retry Delivery

POST
/infra/webhooks/{webhook_id}/deliveries/{delivery_id}/retry
webhooks:write0.0000053 tokens

Re-dispatch a failed webhook delivery.

curl
curl -X POST https://api.saasignal.saastemly.com/infra/webhooks/{webhook_id}/deliveries/{delivery_id}/retry \
  -H "Authorization: Bearer sk_live_..."
json — 200 OK
{ "status": "ok" }
typescript
await ss.infra.webhooks.retryDelivery('wh_01JNXS...', 'dlv_01JNXS...')
  1. Open Dashboard and select your project
  2. Go to Webhooks → click on a webhook → Deliveries
  3. Click Retry next to a failed delivery
Path param
Type
Description
webhook_id required
string
Webhook ID
delivery_id required
string
Delivery ID to retry
Error responses
401 Unauthorized
402 Insufficient tokens
404 Not found
429 Rate limited