Citadel Events stores short notices from apps (a deploy finished, a sale, a site down) and streams them to readers
such as Pager. The API implements the Ambient protocol, version ambient/1.
| Term | Definition |
|---|---|
| Source | A named stream of events: github, narkis-production. Slug [a-z0-9-]{2,48}, plus a name, icon and color |
| Event | One notice: type, severity, one line of text, optional detail, subject, url, display, data |
| Publish key | abk_ key bound to one source; publishes into that source only |
| Read key | abk_ key granted every source or a list of sources; lists, streams and dismisses |
| Webhook | A URL that turns another app's HTTP requests into events of one source, by a mapping |
Base URL: https://citadel.albastov.com/v1/events. The owner creates sources and mints keys in the admin panel.
Quick start
Publish one event with a publish key:
curl https://citadel.albastov.com/v1/events \
-H "Authorization: Bearer $CITADEL_EVENTS_KEY" \
-H "Content-Type: application/json" \
-d '{"type":"backup.done","text":"Backup **done** · 412 MB","idempotency_key":"backup-2026-10-31"}'{"id":"evt_01JBX7Q2W8V5K3M9N4P6R2T8YC","cursor":"1734","source":"backups","source_name":"Backups","source_color":null,"source_icon":null,"type":"backup.done","severity":"info","text":"Backup **done** · 412 MB","detail":null,"subject":null,"url":null,"occurred_at":"2026-10-31T10:02:03Z","received_at":"2026-10-31T10:02:03Z","display":null,"data":null,"dismissed_at":null}Read the newest events with a read key:
curl "https://citadel.albastov.com/v1/events?limit=5" -H "Authorization: Bearer $CITADEL_EVENTS_READ_KEY"The examples are not run by the example checker: the checker holds an API key, not an events key.
Reference
Ways in and out
| Direction | Route | Credential | Page |
|---|---|---|---|
| In: publish | POST /v1/events, POST /v1/events/batch | Publish key | Sources and publishing |
| In: webhook | POST /v1/events/hooks/in/{token} | Token in the URL, optional signature | Webhooks |
| Out: list | GET /v1/events | Read key | Receiving events |
| Out: stream | GET /v1/events/stream | Read key | Receiving events |
Every event enters one store and reaches every stream whose grant includes its source. Publishing and webhooks
apply the same field validation and the same idempotency_key dedupe; webhooks also drop a repeated body within 10
minutes (Duplicates).
Events
| Field | Value |
|---|---|
type | [a-z0-9._-]{1,48}, dotted nouns: deploy.failed, pr.merged |
severity | debug, info, notice, warn, error, critical; default info |
text | One line, at most 300 characters; inline **bold**, *italic*, `code` |
detail | Plain text, at most 1000 characters, newlines allowed |
subject | One line, at most 80 characters |
url | Absolute https:// URL |
occurred_at | RFC 3339; default the time of receipt; clamped to 7 days either side of receipt |
display | Presentation hints for readers: lifetime, layout, effect, target, period, color |
data | JSON object, at most 8 KB, stored as sent |
Citadel adds id, cursor, source, source_name, source_color, source_icon, received_at and, for a
reader, dismissed_at.
| Severity | Use for |
|---|---|
debug | Wiring tests |
info | Completed work: deploy done, PR merged, backup done, sale |
notice | Items to read: PR opened, new signup, configuration change |
warn | Degraded service: retries, slow responses, disk at 85% |
error | Failures that need a person: deploy failed, site down |
critical | Money, data or security at risk now |
Lifetime and sticky
display.lifetime sets how long a reader shows an event: whole seconds 1..86400, or "sticky". A sticky event
stays on screen until a reader dismisses it. Use "sticky" for states that last until someone acts: a site down, a
failed deploy. Without lifetime, the reader's default applies (Pager: 60 s). Dismissal is per read key: one
reader dismissing an event leaves the event open for every other reader.
Citadel's own sources
| Source | Name | Origin |
|---|---|---|
github | GitHub | Webhook (built-in) |
coolify | Coolify | Webhook (built-in) |
narkis | Stripe | Webhook (built-in): Narkis sales |
hostkey | HOSTKEY | Webhook (built-in) |
uptime | Uptime | Citadel's uptime checker, plus remote checkers through a webhook (built-in) |
citadel | Citadel | Citadel's own 500 responses, panics and failed jobs |
generations | Generations | Images, videos and songs made by members |
members | Members | Sign-ins, access requests, gallery posts |
crashes | Browser crashes | New and returning browser crash signatures from Albastar sites |
The built-in webhooks are ordinary webhooks with seeded mappings; see
Built-in webhooks. The uptime checker,
citadel, generations, members and crashes are written by Citadel itself, not received as webhooks, and
take no webhook and no publish key.
Do not publish deploy, CI or sale events that GitHub, Coolify or Stripe already report.
Pager
Pager is the macOS reader for ambient/1 hubs. Pager connects with a read key, calls GET /v1/events/info, reads
open sticky events with GET /v1/events?sticky=open, and holds GET /v1/events/stream open. Pager draws each
event as one line beside its source's name, icon and color, applies display hints, and dismisses sticky events
with POST /v1/events/{id}/dismiss. See Receiving events.
Agent guide
GET /v1/events/docs returns this section (the four Events pages) as one text/markdown document, without
authentication. A publish key's brief links to it.
Limits
| Limit | Value |
|---|---|
| Publish body | 16 KB; batch 256 KB |
| Publish rate, per publish key | 120 requests a minute, bursts of 30; 300 events a minute |
| Webhook body | 2 MiB |
| Webhook rate, per source | 120 deliveries a minute, bursts of 30 |
| Webhook noise budget, per source | 5 events a minute shown one by one; the rest as one summary event |
idempotency_key window | 7 days, per source |
| Streams, per read key | 8 open |
| History | 90 days per source by default |
Changelog
- 2026-10-31: Every webhook, GitHub, Coolify, Stripe, HOSTKEY and remote uptime included, runs through one receiver
with a mapping stored as data. Signatures can be required per source; refused requests are counted. New webhooks
start pending and publish after review. A delivery without an
idempotencykey or delivery header dedupes on its body for 10 minutes. - 2026-10-30: Webhooks for any source:
POST /v1/events/hooks/in/{token}with a mapping. - 2026-10-15:
ambient/1: publish, batch, list, stream, dismiss, sources, info.