Skip to main content

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

  1. Register a webhook URL via the API or team settings.
  2. Pick the events you care about (or subscribe to a wildcard).
  3. cStar fires a signed HTTP POST when those events happen.
  4. 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.

Webhook payload
{
  "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.

Express (Node)
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 });
  }
});
Edge / Workers / Bun (Web Crypto)
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 });
  }
}
Replay protection: Stripe-style signatures enforce a 300-second tolerance by default. Pass { 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 idempotency
X-Webhook-ID ID of the webhook endpoint that received this delivery
X-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.0
Content-Type application/json

Timeouts & 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