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

GET
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.
data required
any
Arbitrary JSON payload to include in the webhook delivery. Max 64 KB.
Error responses
400 Topic does not start with <code>custom.</code>, invalid format, or payload exceeds 64 KB
401 Unauthorized
402 Insufficient Tokens
429 Rate Limited

Create Webhook

GET
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)
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.
secret
string
Signing secret for HMAC-SHA256 verification (16–256 chars). Auto-generated if omitted.
enabled
boolean
Whether the webhook is active. Default true.
Error responses
400 Invalid body (validation error)
401 Unauthorized
402 Insufficient Tokens
429 Rate Limited

List Webhooks

GET
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.
cursor
string
Pagination cursor from a previous response.
enabled
boolean
Filter by enabled status.
Error responses
401 Unauthorized
402 Insufficient Tokens
429 Rate Limited

Get Webhook

GET
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

GET
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
url
string
Updated HTTPS endpoint URL
topics
array
Updated list of event topics
secret
string
Updated signing secret
enabled
boolean
Enable or disable the webhook
Error responses
400 Invalid body (validation error)
401 Unauthorized
402 Insufficient Tokens
404 Not Found
429 Rate Limited

Delete Webhook

GET
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

GET
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
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.
cursor
string
Pagination cursor
status
string
Filter by delivery status: success or failed
Error responses
401 Unauthorized
402 Insufficient Tokens
404 Not Found
429 Rate Limited

Retry Delivery

GET
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