TicketStacker

Help › API and integrations

Webhooks

Have TicketStacker call your system the moment a ticket reaches a step, with a signed JSON payload you can trust.

A webhook lets TicketStacker tell another system that something happened: a repair was approved, an order is ready, a ticket closed. You decide exactly when, because webhooks are attached to steps.

Add one

  1. Open the stack's editor and go to Automations.
  2. Pick the step, like Ready for pickup or Done.
  3. Under Changes the ticket, add Call a web address (webhook) and enter your address. It must start with https://.
  4. Click Publish changes.

From then on, every ticket that arrives in that step sends one call. The ticket's History shows each call. See Automatic actions.

What we send

A POST with a JSON body:

{
  "event": "ticket.step_entered",
  "sent_at": "2026-10-14T15:20:03Z",
  "ticket": {
    "id": "8c1e…",
    "key": "FR-42",
    "title": "Leaking faucet",
    "status": { "key": "done", "name": "Done" },
    "step": { "key": "fixed", "name": "Fixed" },
    "is_closed": true,
    "fields": { "location": "South Lamar", "cost": 41250 },
    "url": "https://app.ticketstacker.com/stacks/…/t/42"
  }
}

ticket is the same object the API returns; see the ticket object. It shows the ticket after the step's other automatic actions ran, so a new owner or a filled-in field is already there.

Headers:

Header
X-TicketStacker-Event ticket.step_entered
X-TicketStacker-Delivery A unique ID for this call. Use it to ignore repeats
X-TicketStacker-Signature sha256= and an HMAC of the body (below)

Check it came from us

Anyone can find your address, so check the signature before you act on a call. Your company's Webhook signing secret is in Settings → API & webhooks. Compute an HMAC-SHA256 of the raw request body with that secret and compare it to the header.

Node.js:

import { createHmac, timingSafeEqual } from "node:crypto";

function fromTicketStacker(rawBody, signatureHeader, secret) {
  const expected = "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex");
  return signatureHeader?.length === expected.length &&
    timingSafeEqual(Buffer.from(signatureHeader), Buffer.from(expected));
}

Python:

import hmac, hashlib

def from_ticketstacker(raw_body: bytes, signature_header: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(signature_header or "", expected)

Use the body exactly as it arrived. Parsing the JSON and turning it back into text changes it, and the signature won't match.

Answer quickly, and retries

Reply with any 2xx status within 10 seconds. If you need to do slow work, accept the call first and do the work afterwards.

If your address is down or answers with an error, we try again: after 1 minute, then 4, 9 and 16 minutes, up to 5 tries in all. Thanks to retries the same delivery can arrive twice, so use X-TicketStacker-Delivery to skip one you've already handled.

Good to know

  • Webhooks start working once your company is activated.
  • Addresses must be public https:// addresses; we don't call localhost or private networks.
  • Need to send data into TicketStacker instead? Use the API.

We run our own platform this way, too: when a new company signs up, our API files it as a ticket in our own TicketStacker, and approving that ticket calls a webhook that switches the company on.

Related

NextConnect TicketStacker to other apps

Still stuck? Email hello@ticketstacker.com. A real person reads every message.