Skip to content

Webhooks

A webhook is an endpoint URL on your side that Glitter will POST to when something happens in a team: a guide is created, changes, is deleted, finishes processing or fails, and a person joins, leaves or changes role. You add the URL once, pick the events, and your code reacts within a minute instead of polling GET /guides on a schedule.

Use a webhook when you keep something in sync with Glitter: a help center, a wiki, a learning platform, a search index, a Slack channel, a warehouse. Use the API when you need the content itself: a webhook tells you what changed and carries the guide’s summary, and the API gives you the guide.

  1. Go to My Team → API (a team admin) or Organization Settings → API (an organization admin) and choose Add endpoint.

  2. Paste your Endpoint URL (https only), give it a label if you like, and tick the events you want. Save.

  3. Open the endpoint’s menu and choose Send test event. Glitter will POST a ping to your URL through the same path as every real event.

  4. Choose Deliveries on the endpoint to see the attempt: the headers and body Glitter sent, and what your server answered.

That is the whole setup. The signing key, the retries and the pause rules below are there when you need them, and most integrations only need the test event to land.

An endpoint belongs to one team or to one organization.

  • A team endpoint hears about that team’s guides and members. Team admins manage it under My Team → API.
  • An organization endpoint hears about every team in the organization, including teams that join later. Organization admins manage it under Organization Settings → API, where the list also shows every team endpoint.

Two organization switches apply to webhooks exactly as they apply to API keys. Turning API access off pauses every endpoint in the organization until it is on again. Turning team API keys off pauses team endpoints and leaves organization endpoints running.

Tick the events an endpoint should receive. Every event is also a page under the Webhooks group of the API reference, with its full schema and an example payload.

EventIn the appWhen Glitter sends it
guide.createdA guide is createdA guide was created in the team, or moved into it from another team. Drafts count; `placement.team_guides` says whether it is in Team Guides.
guide.updatedA guide changes: title, content, folder, accessA guide changed, draft or not. `changed` lists the columns that moved: title, description, body, permalink, language, folder_id, publish_status, team_role, is_public. Edits inside one minute arrive as one event.
guide.deletedA guide is deletedA guide was deleted, or moved out of this team. Only the id and team id are sent.
guide.readyA guide finishes processingProcessing of a recording or an import finished and the guide has its steps.
guide.failedProcessing failsProcessing failed. The guide is still there; its owner sees the error in Glitter.
member.joinedSomeone joins the teamA person joined the team.
member.leftSomeone leavesA person left the team or was removed.
member.role_changedA role changesA person's role on the team changed. `role` is the new role.

Every guide in the team, drafts included: an endpoint hears what a team admin sees in the app. Moving a draft to Team Guides arrives as guide.updated with publish_status in changed. If you only want guides the whole team can see, check placement.team_guides.

Every guide payload carries placement, so you can tell from the event alone whether a guide is in Team Guides and whether it has a public link:

"placement": { "team_guides": true, "public_link": false }
  • team_guides: the guide is in Team Guides, visible to the whole team.
  • public_link: anyone with the share link can read it.

A guide moved to another team is guide.deleted for the old team’s endpoints and guide.created for the new team’s.

changed names the fields that changed: title, description, body, permalink, language, folder_id, publish_status, team_role or is_public. Use it to skip what you do not care about, such as a folder move. Edits made within the same minute arrive as one event whose changed is the union of them, so a person editing for ten minutes produces about ten events, not one per keystroke.

Every event is one JSON object with the same five fields around a data object:

{
"id": "0d6c1a2b-3e4f-4a5b-8c6d-7e8f9a0b1c2d",
"type": "guide.updated",
"created_at": "2026-10-11T13:20:45.123Z",
"team_id": "4b1e7c2a-9d3f-4e8b-a1c6-0f5d2e7b9a34",
"organization_id": "9c0a4b2d-1e3f-4a5b-9c7d-2e4f6a8b0c1d",
"data": {
"guide": {
"id": "7d2e5f1a-3c4b-4d6e-8f9a-0b1c2d3e4f5a",
"title": "Set up SSO in Okta",
"description": null,
"permalink": "set-up-sso-in-okta-7d2e5f1a",
"url": "https://www.glitter.io/share/7d2e5f1a-3c4b-4d6e-8f9a-0b1c2d3e4f5a",
"updated_at": "2026-10-11T13:20:12+00:00",
"team_id": "4b1e7c2a-9d3f-4e8b-a1c6-0f5d2e7b9a34",
"folder_id": null,
"language": "english"
},
"placement": { "team_guides": true, "public_link": false },
"changed": ["title", "body"]
}
}
Field Meaning
id The event’s id. A retry of the same event carries the same id; use it to ignore duplicates.
type The event name from the table above, or ping for a test event.
created_at When the change happened, UTC.
team_id The team the event is about.
organization_id The team’s organization, or null.
data.guide The guide’s summary for the five guide.* events. guide.deleted carries only id and team_id.
data.placement Where the guide lives, as above.
data.changed The columns that changed on guide.updated; empty on the other guide events.
data.member For the three member.* events: user_id, email, name, role and team_id.
data.message For ping: a greeting.

The envelope never carries a guide’s content. When you need it, call GET /guides/{id} on the API with an API key; the guide’s markdown is in markdown_body. That keeps every webhook small and keeps your content behind a key rather than at whatever URL you pasted.

POST {your endpoint URL}
Content-Type: application/json
User-Agent: Glitter-Webhooks/1
X-Glitter-Event: guide.updated
X-Glitter-Delivery: 7c1d2e3f-4a5b-4c6d-8e7f-9a0b1c2d3e4f
X-Glitter-Signature: sha256=9f2c1a7e…
Header Meaning
X-Glitter-Event The event name, the same as type in the body. Route on it without parsing.
X-Glitter-Delivery One id per attempt. The same event retried carries a new one, so this is the id to log.
X-Glitter-Signature sha256= followed by the hex HMAC-SHA256 of the raw body, keyed with the endpoint’s signing key.

Answer any 2xx within 10 seconds. Glitter does not follow redirects, so a 3xx counts as a failure. Do the slow work after you have answered: queue it, or hand it to a background job.

A change reaches your endpoint within a minute. Glitter collects changes and sends them once a minute, which is why edits made within the same minute arrive as one guide.updated. A test event goes out at once.

On anything but a 2xx, Glitter will retry up to eight times with growing delays over about a day. After 24 hours of nothing but failures it will pause the endpoint and email the admins of its team or organization once. A 410 Gone pauses the endpoint at once, so answer 410 when an endpoint is retired for good.

A paused endpoint shows Paused and why. Resume from its menu starts deliveries again and offers to redeliver the events missed while it was paused. Every attempt stays under Deliveries with the request and the answer, and any single one can be sent again.

Events are delivered as they are drained, in parallel across endpoints, and retries reorder them. Do not assume guide.created arrives before the first guide.updated. Use data.guide.updated_at to decide whether an event is newer than what you hold, and refetch from the API when in doubt.

Checking the signature is optional and recommended for anything that writes or deletes on your side. Each delivery carries X-Glitter-Signature: sha256=…, the hex HMAC-SHA256 of the raw request body, keyed with the endpoint’s signing key. The key is created with the endpoint and is behind Reveal on its row, any time.

Two rules make it work: compute the HMAC over the raw bytes as received, before any JSON parsing or re-serialization, and compare with a constant-time function.

import { createHmac, timingSafeEqual } from 'node:crypto';
// rawBody: the request body exactly as received, before any JSON parsing
export function verifyGlitterWebhook(rawBody, signatureHeader, signingKey) {
const expected = 'sha256=' + createHmac('sha256', signingKey).update(rawBody).digest('hex');
const given = signatureHeader ?? '';
return expected.length === given.length && timingSafeEqual(Buffer.from(expected), Buffer.from(given));
}
// Express
app.post('/glitter', express.raw({ type: 'application/json' }), (req, res) => {
if (!verifyGlitterWebhook(req.body, req.get('X-Glitter-Signature'), process.env.GLITTER_SIGNING_KEY)) return res.sendStatus(401);
const event = JSON.parse(req.body);
res.sendStatus(200);
});

Rotate key on the endpoint replaces the key at once. Deliveries that fail while you paste the new key into your server are retried for a day, so a rotation costs nothing but a few failed attempts in the log.

Glitter sends from no fixed IP addresses, so there is no allowlist to maintain; the signature is how you know a request is Glitter’s. If your platform cannot run code, keep the endpoint URL private and treat it as the secret.

Zapier

  1. In Zapier, create a Zap whose trigger is "Webhooks by Zapier", event "Catch Hook".
  2. Copy the webhook URL Zapier shows you.
  3. In Glitter, choose Edit on the endpoint and paste that URL as the Endpoint URL, then save.
  4. Choose Send test event. Zapier will receive it and offer it as the sample record for the rest of the Zap.

Zapier cannot check the signature. The URL it gives you is unguessable, so treat it as the secret.

Make

  1. In Make, add a "Webhooks" module, "Custom webhook", and create a new webhook.
  2. Copy the address Make shows you.
  3. In Glitter, choose Edit on the endpoint and paste that address as the Endpoint URL, then save.
  4. In Make choose "Redetermine data structure", then in Glitter choose Send test event so Make learns the fields.

Make cannot check the signature either; the address it gives you is the secret.

n8n

  1. In n8n, add a "Webhook" node as the trigger and set its HTTP Method to POST.
  2. Copy the Test URL the node shows. Switch to its Production URL once the workflow is published.
  3. In Glitter, choose Edit on the endpoint and paste that URL as the Endpoint URL, then save.
  4. In n8n choose "Listen for test event", then in Glitter choose Send test event so n8n learns the fields.

The Webhook node cannot check the signature, but the path in its URL is random, so treat the URL as the secret.

Any other service

  1. Glitter will POST JSON to the Endpoint URL with the headers X-Glitter-Event, X-Glitter-Delivery and X-Glitter-Signature.
  2. Answer any 2xx within 10 seconds. Redirects count as failures.
  3. If the service lets you run code, verify the signature with one of the code examples. If not, keep the URL private.
  4. Choose Send test event and look for it on the other side.

Writing a receiver with an AI assistant (LLM)

Section titled “Writing a receiver with an AI assistant (LLM)”

Click Reveal in the endpoint’s Signing key column first. The dialog it opens has Copy instructions for LLM, which copies a prompt with the endpoint URL, its events, and implementation instructions.

Paste it into your assistant and you will get a receiver that reads the raw body and verifies the Webhook signature.

  • HTTPS only. Glitter refuses http:// endpoints, local addresses and private network ranges, and resolves the host on every attempt so a DNS change cannot point a delivery inward.
  • The key is yours to read. It is stored, never hashed, because HMAC needs it, and it is shown only to admins of the endpoint’s home.
  • Deliveries are logged, request and answer, for every attempt.
What you see Why, and what to do
The test event lands but real events never come The event is not ticked on the endpoint: choose Edit and check. Real events can take up to a minute.
Events arrive up to a minute late Expected. The outbox drains every minute.
Failing on the row Your endpoint answered something other than 2xx, or took more than 10 seconds, or redirected. Open Deliveries to see the exact answer. Glitter keeps retrying for a day.
Paused · a day of failures Retries ran out. Fix the endpoint, then Resume and choose to redeliver what was missed.
Paused · said it’s gone Your endpoint answered 410. Resume it if that was a mistake.
Paused · by the organization An organization admin turned API access or team API keys off. The endpoint resumes by itself when the switch is on again.
Signature does not verify You hashed a parsed and re-serialized body, or a framework changed the bytes. Verify the raw body. Confirm the key with Reveal; a rotation changes it at once.
The same event twice A retry after a late or non-2xx answer. Ignore an id you have already handled.
  • Up to 500 events are drained per minute per Glitter environment; a larger backlog continues on the next minute.
  • An endpoint has one URL, a label of up to 60 characters, and any subset of the events above.
  • Deliveries are kept; the app shows the latest 50 per endpoint.