Webhooks
Webhooks allow you to receive real-time notifications when events occur in Support Station. This enables you to build integrations that respond to ticket updates, new messages, and more. On this page, we will look at how to create, retrieve, update, and delete webhooks, as well as view delivery history.
The webhook model
The webhook model contains configuration for receiving event notifications.
Properties
- Name
id- Type
- string
- Description
Unique identifier for the webhook (UUID).
- Name
name- Type
- string
- Description
Human-readable name for the webhook.
- Name
url- Type
- string
- Description
The public HTTP or HTTPS URL that will receive webhook POST requests. Private network addresses and redirects are not supported.
- Name
secret- Type
- string
- Description
HMAC-SHA256 signing secret (only returned once on creation).
- Name
events- Type
- string[]
- Description
Array of event types this webhook is subscribed to.
- Name
is_active- Type
- boolean
- Description
Whether the webhook is currently active.
- Name
last_delivery_at- Type
- timestamp
- Description
Timestamp of the last delivery attempt (nullable).
- Name
last_delivery_status- Type
- string
- Description
Status of the last delivery:
successorfailed(nullable).
- Name
created_at- Type
- timestamp
- Description
Timestamp of when the webhook was created.
- Name
updated_at- Type
- timestamp
- Description
Timestamp of when the webhook was last updated. Present on list, retrieve, and update responses. Create responses omit this field and the last-delivery fields.
Event Types
The following events can trigger webhook notifications:
- Name
ticket.created- Description
A new ticket was created.
- Name
ticket.updated- Description
A ticket was updated (status, priority, assignee, tags changed).
- Name
message.created- Description
A new message was added to a ticket.
List all webhooks
Returns all webhooks in your organization without pagination metadata.
Required scope: webhooks:read
Request
curl https://api.supportstation.io/api/v1/webhooks \
-H "Authorization: Bearer {token}"
Response
{
"data": [
{
"id": "webhook-uuid",
"name": "Production Webhook",
"url": "https://example.com/webhook",
"events": ["ticket.created", "ticket.updated"],
"is_active": true,
"last_delivery_at": "2025-01-15T10:00:00.000Z",
"last_delivery_status": "success",
"created_at": "2025-01-01T00:00:00.000Z",
"updated_at": "2025-01-15T10:00:00.000Z"
}
]
}
Retrieve a webhook
Returns a single webhook by ID.
Required scope: webhooks:read
Request
curl https://api.supportstation.io/api/v1/webhooks/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer {token}"
Response
{
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Production Webhook",
"url": "https://example.com/webhook",
"events": ["ticket.created", "ticket.updated"],
"is_active": true,
"last_delivery_at": "2025-01-15T10:00:00.000Z",
"last_delivery_status": "success",
"created_at": "2025-01-01T00:00:00.000Z",
"updated_at": "2025-01-15T10:00:00.000Z"
}
}
Create a webhook
Creates a new webhook. The webhook secret is only returned once in the response - make sure to store it securely for signature verification.
Required scope: webhooks:write
Required attributes
- Name
name- Type
- string
- Description
Human-readable name for the webhook (max 100 characters).
- Name
url- Type
- string
- Description
A public HTTP or HTTPS URL that will receive POST requests.
- Name
events- Type
- string[]
- Description
Array of events to subscribe to. At least one event is required.
Request
curl -X POST https://api.supportstation.io/api/v1/webhooks \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"name": "My Integration",
"url": "https://example.com/webhook",
"events": ["ticket.created", "message.created"]
}'
Response
{
"data": {
"id": "new-webhook-uuid",
"name": "My Integration",
"url": "https://example.com/webhook",
"secret": "whsec_abc123def456...",
"events": ["ticket.created", "message.created"],
"is_active": true,
"created_at": "2025-01-15T10:00:00.000Z"
}
}
Store the secret securely - it will not be returned in subsequent API calls. You will need it to verify webhook signatures.
Update a webhook
Updates an existing webhook.
Required scope: webhooks:write
Optional attributes
- Name
name- Type
- string
- Description
New name, from 1 to 100 characters.
- Name
url- Type
- string
- Description
New URL for receiving events.
- Name
events- Type
- string[]
- Description
New list of events to subscribe to. At least one supported event is required.
- Name
is_active- Type
- boolean
- Description
Enable or disable the webhook.
Request
curl -X PATCH https://api.supportstation.io/api/v1/webhooks/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"events": ["ticket.created", "ticket.updated", "message.created"],
"is_active": true
}'
Response
{
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "My Integration",
"url": "https://example.com/webhook",
"events": ["ticket.created", "ticket.updated", "message.created"],
"is_active": true,
"last_delivery_at": "2025-01-15T10:00:00.000Z",
"last_delivery_status": "success",
"created_at": "2025-01-01T00:00:00.000Z",
"updated_at": "2025-01-15T12:00:00.000Z"
}
}
Delete a webhook
Deletes a webhook. This will stop all event deliveries to this endpoint.
Required scope: webhooks:write
Request
curl -X DELETE https://api.supportstation.io/api/v1/webhooks/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer {token}"
Response
{
"data": {
"deleted": true
}
}
Get delivery history
Returns the delivery history for a webhook, showing past delivery attempts and their status.
Required scope: webhooks:read
Query parameters
- Name
page- Type
- integer
- Description
Page number (default: 1).
- Name
limit- Type
- integer
- Description
Items per page, max 100 (default: 20).
Request
curl https://api.supportstation.io/api/v1/webhooks/550e8400-e29b-41d4-a716-446655440000/deliveries \
-H "Authorization: Bearer {token}"
Response
{
"data": [
{
"id": "delivery-uuid",
"event": "ticket.created",
"status": "success",
"status_code": 200,
"attempt_count": 1,
"created_at": "2025-01-15T10:00:00.000Z",
"completed_at": "2025-01-15T10:00:01.000Z"
},
{
"id": "delivery-uuid-2",
"event": "message.created",
"status": "failed",
"status_code": 500,
"attempt_count": 5,
"created_at": "2025-01-15T09:00:00.000Z",
"completed_at": "2025-01-15T09:15:00.000Z"
}
],
"meta": {
"pagination": {
"page": 1,
"limit": 20,
"total": 150,
"total_pages": 8
}
}
}
Webhook Payload
Each webhook request includes:
- Name
event- Type
- string
- Description
The event type that triggered the webhook.
- Name
timestamp- Type
- string
- Description
ISO 8601 timestamp of when the event occurred.
- Name
organization_id- Type
- string
- Description
Your organization UUID.
- Name
data- Type
- object
- Description
The event data (varies by event type).
ticket.created payload
{
"event": "ticket.created",
"timestamp": "2025-01-15T10:30:00.000Z",
"organization_id": "org-uuid",
"data": {
"id": "ticket-uuid",
"ticket_number": "42",
"subject": "Help with login",
"status": "OPEN",
"priority": "MEDIUM",
"channel": "API",
"customer": {
"id": "customer-uuid",
"email": "[email protected]",
"name": "John Doe"
},
"assignee": null,
"team": null,
"tags": [],
"created_at": "2025-01-15T10:30:00.000Z",
"updated_at": "2025-01-15T10:30:00.000Z",
"closed_at": null
}
}
Message Event Payload
When a new message is added to a ticket, the payload includes the message details along with ticket context.
message.created payload
{
"event": "message.created",
"timestamp": "2025-01-15T11:00:00.000Z",
"organization_id": "org-uuid",
"data": {
"id": "message-uuid",
"ticket_id": "ticket-uuid",
"ticket_number": "42",
"content": "Thanks for your help!",
"type": "INBOUND",
"author": {
"type": "customer",
"id": "customer-uuid",
"email": "[email protected]",
"name": "John Doe"
},
"created_at": "2025-01-15T11:00:00.000Z"
}
}
Verifying Webhooks
To ensure webhook requests are genuinely from Support Station, each request includes a signature header that you can verify:
X-Webhook-Signature: sha256=abc123...
X-Webhook-Timestamp: 2025-01-15T10:30:00.000Z
X-Webhook-Event: ticket.created
Verify the signature over the raw request body using your webhook secret. Do not parse and re-serialize the JSON before verification.
Signature verification
import crypto from 'node:crypto';
import express from 'express';
const app = express();
const secret = process.env.WEBHOOK_SECRET;
if (!secret) throw new Error('WEBHOOK_SECRET is required');
function verifyWebhook(payload, signature, secret) {
if (typeof signature !== 'string') return false;
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
const expected = Buffer.from(`sha256=${expectedSignature}`);
const received = Buffer.from(signature);
return expected.length === received.length &&
crypto.timingSafeEqual(expected, received);
}
// Register this route before express.json() so the raw bytes are preserved.
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-webhook-signature'];
const payload = req.body;
if (!verifyWebhook(payload, signature, secret)) {
return res.status(401).send('Invalid signature');
}
// Process the webhook
const event = JSON.parse(payload.toString('utf8'));
console.log('Received event:', event.event);
res.status(200).send('OK');
});
Best Practices
- Respond quickly: Return a 2xx response promptly. Delivery requests time out after 30 seconds. Process webhooks asynchronously if needed.
- Handle duplicates: Webhooks may be delivered more than once. Deduplicate with the signed event, timestamp, and resource ID. The delivery ID is available in delivery history but is not sent in the webhook request.
- Verify signatures: Always verify the webhook signature before processing.
- Retry handling: Failed webhooks will be retried with exponential backoff (up to 5 total attempts, with retry delays of 1, 2, 4, and 8 minutes; queue delays can add time).
- Circuit breaker: After 10 failed delivery attempts without a success, new events stop being queued. The
is_activefield does not change, and existing queued deliveries can still run. Setis_activetotrueor update the URL to reset the failure count.
Subscribe by URL
This route supports Zapier REST hooks. Required scope: tickets:write.
Provide a valid url and one supported event. It adds the event to a webhook with that URL or creates a new webhook. The response does not include a signing secret. Use POST /webhooks when you need the secret for signature checks.
Request body
{
"url": "https://example.com/webhook",
"event": "ticket.created"
}
Response (201)
{
"data": {
"id": "webhook-uuid",
"url": "https://example.com/webhook",
"event": "ticket.created"
}
}
Unsubscribe by URL
Required scope: tickets:write.
Provide a valid url. Optionally provide one supported event to remove only that subscription. If no events remain, the webhook is deleted. Omit event to delete the webhook and all its subscriptions. An unknown URL or an event that is not subscribed returns success without changes.
Request body
{
"url": "https://example.com/webhook",
"event": "ticket.created"
}
Response (201)
{
"data": { "success": true }
}