Information
- OpenAPI version:
3.1.0
The Glitter AI API reads the guides, teams and members a key may see. It is read-only in v1.
Every request carries an API key as a bearer token:
Authorization: Bearer glt_live_…
A team admin creates a key under My Team → API; an organization admin creates one under Organization Settings → API. A key belongs to one team, or to one organization and then reaches every team in it. A key sees every guide of its teams, drafts included, as a team admin does; only a guide whose recording is still processing stays out. The full key is shown once, at creation.
A key carries the scopes it was created with: guides:read, teams:read, members:read. A request outside the key’s scopes answers 403 scope_missing.
Lists take limit (1 to 100, default 50) and return next_cursor. Pass it back as cursor for the next page; null means the last page. Cursors are opaque.
Every refusal is one JSON shape (RFC 9457) with a stable code to switch on:
{ "type": "https://www.glitter.io/developers/api-reference#errors", "title": "Not found", "status": 404,
"detail": "No guide 9c2f… is visible to this key.", "code": "not_found", "request_id": "…" }
| Code | Status | Meaning |
|---|---|---|
invalid_key |
401 | The bearer is missing, malformed, unknown or revoked. |
key_expired |
401 | The key passed its expiry. Create a new one. |
scope_missing |
403 | The key lacks the scope this endpoint needs. |
api_disabled |
403 | The organization turned API access off. |
team_keys_disabled |
403 | The organization allows organization keys only. |
rate_limited |
429 | 600 requests per minute per key. Retry-After says when. |
invalid_request |
400 | A parameter is wrong; detail names it. |
not_found |
404 | Nothing the key may see has that id. |
internal_error |
500 | Our fault. Quote request_id to support. |
Every response carries X-Request-Id, RateLimit-Limit and RateLimit-Remaining.
Glitter will POST a JSON envelope to an endpoint URL you add under My Team → API or Organization Settings → API when a guide is created, updated, deleted, finishes processing or fails, and when someone joins, leaves or changes role on a team. Pick the events when you add the endpoint. The Webhooks group below lists every event with its schema and an example; the Webhooks guide covers setup, delivery, retries, verification and troubleshooting.
Every guide in the team sends events, drafts included, which is what a team admin sees in the app; placement.team_guides tells a draft from a guide in Team Guides. The payload carries the guide’s summary and where it lives (placement), never its body: call GET /guides/{guideId} with your key for the content, in markdown_body. changed lists the columns a guide.updated event is about. Edits inside one minute arrive as one event.
Every delivery carries these headers:
Content-Type: application/json
User-Agent: Glitter-Webhooks/1
X-Glitter-Event: guide.updated
X-Glitter-Delivery: 7c1d…
X-Glitter-Signature: sha256=9f2c…
Checking the signature is optional. It is an HMAC-SHA256 of the raw body with the endpoint’s signing key, which you will find behind Reveal on the endpoint’s row. Complete verifiers in seven languages are behind that same button; the Node and Python ones:
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);
});
import hashlib
import hmac
import os
from flask import Flask, abort, request
app = Flask(__name__)
def verify_glitter_webhook(raw_body: bytes, signature_header: str | None, signing_key: str) -> bool:
expected = "sha256=" + hmac.new(signing_key.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature_header or "")
@app.post("/glitter")
def glitter():
if not verify_glitter_webhook(request.get_data(), request.headers.get("X-Glitter-Signature"), os.environ["GLITTER_SIGNING_KEY"]):
abort(401)
event = request.get_json()
return "", 200
Verify the raw bytes as received, before any JSON parsing. Rotating the key replaces it at once; deliveries that fail while you update your server are retried.
Answer any 2xx within 10 seconds. Glitter will not follow redirects. On any other answer Glitter will retry up to 8 times over about a day. After a day of nothing but failures the endpoint will pause and the admins of its home will get one email; a 410 pauses it at once. Resume from the endpoint’s menu, with the option to redeliver what was missed. Every attempt, with the request and the answer, is under Deliveries, where any one can be sent again. Glitter sends from no fixed IP addresses, so the signature is the way to know a request is Glitter’s.
A key created under My Team → API or Organization Settings → API.
Security scheme type: http
Bearer format: Glitter API key