Webhooks
Push, don't poll. When a ticket flips status, a customer signs up, or a boss falls, cStar fires a signed HTTP POST at the URL you registered. Stripe-style signature scheme, exponential retries, and a CLI that forwards live events to localhost without an ngrok tunnel.
How it works
- Register a webhook URL via the API or team settings.
- Pick the events you care about (or subscribe to a wildcard).
- cStar fires a signed HTTP POST when those events happen.
- Your server verifies the signature, parses the payload, returns 2xx. Done.
Event Types
Events follow the pattern resource.action. Subscribe to specific events or use
wildcards when creating webhooks.
Tickets
Customers
Articles
Team
Gamification
Community
Surveys
Payload format
Same envelope every time. Field names are snake_case on the wire.
{
"id": "evt_ticket_created_1774583010044_abc12345",
"type": "ticket.created",
"created_at": "2026-03-06T14:30:00Z",
"team_id": "550e8400-e29b-41d4-a716-446655440000",
"data": {
"ticket": {
"id": "25990cb8-cbbf-4671-a973-672cde1cf42d",
"title": "Cannot log in to my account",
"status": "new",
"priority": "high",
"customer_id": "cd17b730-387d-424e-a50b-eb4a44e543af",
"customer_name": "Jane Doe",
"created_at": "2026-03-06T14:30:00Z",
"dashboard_url": "https://www.cstar.help/team/my-team/tickets?selected=25990cb8-..."
},
"customer": {
"id": "cd17b730-387d-424e-a50b-eb4a44e543af",
"name": "Jane Doe",
"email": "jane@example.com"
}
}
}Ticket events include a dashboard_url field — a direct link to the ticket in the cStar
dashboard. Use this in Slack messages, emails, or other integrations for one-click access.
Signature verification
Every delivery carries an X-Signature header in Stripe-style format: t=<unix_timestamp>,v1=<hex_hmac>. The HMAC is computed over {ts}.{rawBody} with your webhook secret. Verify before you parse.
Easiest path: import the verifier from @cstar.help/js. /webhook/node uses synchronous node:crypto; /webhook uses Web
Crypto and works in Edge runtimes, Cloudflare Workers, Bun, and Deno.
import express from 'express';
import { constructEvent } from '@cstar.help/js/webhook/node';
const app = express();
// Capture the RAW body — required for HMAC verification
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
try {
const event = constructEvent(
req.body.toString('utf8'), // raw body, not JSON.parse'd
req.headers['x-signature'], // Stripe-style: t=...,v1=...
process.env.CSTAR_WEBHOOK_SECRET
);
console.log('Verified event:', event.type, event.data);
res.status(200).json({ received: true });
} catch (err) {
// Throws WebhookSignatureVerificationError on bad/expired signatures
res.status(401).json({ error: err.message });
}
});import { constructEventAsync } from '@cstar.help/js/webhook';
export async function POST(request) {
const body = await request.text(); // raw body
const signature = request.headers.get('x-signature');
try {
const event = await constructEventAsync(
body,
signature,
process.env.CSTAR_WEBHOOK_SECRET
);
return new Response(JSON.stringify({ received: true }), { status: 200 });
} catch (err) {
return new Response(err.message, { status: 401 });
}
}{ tolerance: <seconds> } as a fourth argument to override. Legacy sha256=<hex> signatures (no timestamp) are still accepted by the verifier for backward
compatibility, but are no longer emitted by the cStar dispatcher.Request Headers
X-Signature Stripe-style HMAC-SHA256 signature: t=<unix>,v1=<hex>. Legacy sha256=<hex> is still accepted by verifySignature for
backward compatibility. The cstar listen CLI also forwards a cstar-signature alias header for local-development receivers; production
deliveries only set X-Signature.X-Event-Type Event type (e.g., ticket.created)X-Event-ID Unique event ID for idempotencyX-Webhook-ID ID of the webhook endpoint that received this deliveryX-Timestamp ISO 8601 timestamp of when the delivery was sent. (Distinct from the unix t= value embedded inside X-Signature — the unix one is what's HMAC'd.)X-Delivery-Attempt Attempt number (starts at 1)User-Agent cStar-Webhooks/1.0Content-Type application/jsonTimeouts & failures
Respond within 10 seconds or the delivery times out. Anything outside the 2xx range counts as a failure. Every attempt — successful or otherwise — lands in the webhook logs under team settings. Consecutive failures pile up per endpoint; if the count climbs, your receiver probably needs attention.
Local Testing with the CLI
Use the cStar CLI to forward webhook events to your local development server.
# Forward events to your local server
cstar listen --forward-to http://localhost:3000/webhook
# In another terminal, trigger a test event
cstar trigger ticket.created