Webhooks

The Data API is pull-only. To be told when something happens (a family enrolls, a trial is booked, a season opens), the studio adds a Send Webhook step to one of its Workflows in HotGlue. Workflows are the studio's automation layer: a trigger (new enrollment, new lead, membership started…) followed by steps such as sending an email, tagging a person, or calling your URL.

This means the studio, not the API key holder, decides what is sent and when. Agree the URL and payload with the studio and have them add the step.

What the step sends

  • Method: POST, PUT, PATCH or DELETE, chosen in the step
  • Headers: any the studio configures, plus the signature headers below when a signing secret is set
  • Body: text the studio writes in the step, usually JSON, with merge tags filled in from the triggering event (for example the student's name or the class id)

Because the body is composed per workflow, there is no fixed event schema. Ask the studio to send the fields you need, or send them a body template to paste in.

Verifying a webhook

When the studio sets a signing secret on the step, every call carries:

X-HotGlue-Timestamp: 1791820800            (Unix seconds)
X-HotGlue-Signature: sha256=3f2a…           (hex HMAC-SHA256)

The signature is HMAC-SHA256, keyed with the shared secret, over the string "<timestamp>.<raw body>". Verify it before trusting the payload, and reject timestamps older than a few minutes to stop replays.

import { createHmac, timingSafeEqual } from 'node:crypto'

export function verifyHotGlueWebhook(secret, rawBody, headers) {
  const ts = headers['x-hotglue-timestamp']
  const sig = headers['x-hotglue-signature'] ?? ''
  if (!ts || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false
  const expected = 'sha256=' + createHmac('sha256', secret).update(`${ts}.${rawBody}`).digest('hex')
  return expected.length === sig.length && timingSafeEqual(Buffer.from(expected), Buffer.from(sig))
}

Use the raw request body, byte for byte, not a re-serialised JSON object.

Delivery

  • Your endpoint must be reachable on the public internet over HTTPS. Private and internal addresses are refused.
  • Answer with a 2xx quickly. Do any heavy work after responding.
  • A failed call does not stop the rest of the studio's workflow unless the studio turns that on, and HotGlue does not queue retries. If you need every event without fail, pair the webhook with a periodic reconciliation pull from the API.