Publishing
Store a publish key, name the source, publish events from an app, and set each event's fields and lifetime.
Access: Keys with the events:publish scope, issued by Albastar Corp.
An app publishes events into one source with a publish key.
Get a publish key
- Albastar Corp. creates the source (slug
[a-z0-9-]{2,48}, name, icon, colour, retention) and issues anabk_publish key bound to the source. - Store the key on the app's server as
ALBASTAR_EVENTS_KEY. Keep the key out of browser bundles, mobile apps, repositories and log lines.
A source takes events from publish keys, from a webhook URL, or both.
Name the source
Readers show every event beside its source's name, icon and colour. Set them once from the app with
PATCH /v1/events/sources/self; the change applies to the source's whole history.
| Field | Content |
|---|---|
name | The service as readers know it: Vercel, Stripe, GitHub |
icon | A Simple Icons slug (vercel) or an SF Symbol name (shippingbox.fill); an SF Symbol draws only on readers built for Apple platforms |
color | The brand colour: #rgb, #rrggbb or #rrggbbaa |
Use one source per service a reader recognises. Put the part of the service in each event's subject (web,
api), not in the source name.
curl -X PATCH https://api.albastov.com/v1/events/sources/self \
-H "Authorization: Bearer $ALBASTAR_EVENTS_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Vercel", "icon": "vercel", "color": "#000000"}'{
"slug": "vercel",
"name": "Vercel",
"icon": "vercel",
"color": "#000000"
}Publish an event
POST /v1/events stores one event in the key's source and returns the stored event. The source comes from the key:
leave source out.
curl
curl https://api.albastov.com/v1/events \
-H "Authorization: Bearer $ALBASTAR_EVENTS_KEY" \
-H "Content-Type: application/json" \
--max-time 5 \
-d '{
"type": "deploy.failed",
"severity": "error",
"text": "Deploy **failed** `9f3c2e1`",
"detail": "npm run build exited 1",
"subject": "web",
"display": {"lifetime": "sticky"},
"idempotency_key": "deploy-9f3c2e1"
}'Python
import json, os, urllib.request
req = urllib.request.Request(
"https://api.albastov.com/v1/events",
data=json.dumps({
"type": "deploy.failed",
"severity": "error",
"text": "Deploy **failed** `9f3c2e1`",
"detail": "npm run build exited 1",
"subject": "web",
"display": {"lifetime": "sticky"},
"idempotency_key": "deploy-9f3c2e1",
}).encode(),
headers={
"User-Agent": "my-app/1.0",
"Authorization": f"Bearer {os.environ['ALBASTAR_EVENTS_KEY']}",
"Content-Type": "application/json",
},
method="POST",
)
with urllib.request.urlopen(req, timeout=5) as res:
print(res.status, json.load(res)["id"])JavaScript
const res = await fetch("https://api.albastov.com/v1/events", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ALBASTAR_EVENTS_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
type: "deploy.failed",
severity: "error",
text: "Deploy **failed** `9f3c2e1`",
detail: "npm run build exited 1",
subject: "web",
display: { lifetime: "sticky" },
idempotency_key: "deploy-9f3c2e1",
}),
signal: AbortSignal.timeout(5000),
});
console.log(res.status, (await res.json()).id);{
"id": "evt_01M6GDQMZRD7F5H9K3M6P8R1T4",
"cursor": "1752",
"source": "vercel",
"source_name": "Vercel",
"source_color": "#000000",
"source_icon": "vercel",
"type": "deploy.failed",
"severity": "error",
"text": "Deploy **failed** `9f3c2e1`",
"detail": "npm run build exited 1",
"subject": "web",
"url": null,
"occurred_at": "2026-11-03T10:02:03Z",
"received_at": "2026-11-03T10:02:03Z",
"display": {"lifetime": "sticky"},
"data": null,
"dismissed_at": null
}Name the type as a dotted noun (deploy.succeeded, pr.merged, site.down) and keep text to one line a person
reads at a glance.
Event fields
| Field | Content |
|---|---|
type | What happened, as a dotted noun: deploy.failed, pr.merged, backup.done |
severity | debug, info, notice, warn, error or critical; default info |
text | The notice as one line, with inline **bold**, *italic* and `code` |
detail | Plain text under the line, newlines allowed |
subject | The part of the service the event concerns: web, api |
url | An https:// link to the deploy, order or incident |
occurred_at | When the event happened; default the time of receipt |
display | Presentation hints for readers: lifetime, layout, effect, target, period, color |
data | A JSON object stored as sent |
The API adds id, cursor, source, source_name, source_color, source_icon, received_at and, per read key,
dismissed_at. Lengths, formats and every display value:
Publish an event.
| 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 |
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 the event. Use "sticky" for states that last until a person acts: a site
down, a failed deploy. Without lifetime, the reader's own default applies. Dismissal belongs to the read key:
Read keys.
Publish a batch
POST /v1/events/batch publishes 1 to 100 events in one request, as {"events": [...]}. One invalid event refuses
the whole batch, and the error's meta.index names the event. The response lists the stored events in request order,
with status 201 when at least one event is new, else 200.
curl https://api.albastov.com/v1/events/batch \
-H "Authorization: Bearer $ALBASTAR_EVENTS_KEY" \
-H "Content-Type: application/json" \
-d '{
"events": [
{"type": "deploy.succeeded", "text": "Deploy **done**", "subject": "web"},
{"type": "deploy.succeeded", "text": "Deploy **done**", "subject": "docs"}
]
}'Dedupe
Set idempotency_key on every event the app may send twice: deploy-<sha>, backup-2026-11-03. A key used again
on the same source within 7 days returns the original event with 200 and stores nothing.
Retries
Publish outside the request path, with a 5 s timeout. A failed publish must not block, slow down or fail the app.
| Response | Action |
|---|---|
Network error, 5xx | Retry at most 3 times with growing waits plus jitter, for example 1 s, 4 s and 15 s; then drop the event and log one line |
429 | Wait the Retry-After seconds, then retry within the same 3 attempts |
400, 401, 403, 413 | Do not retry; log message |
Rates and body sizes: Events limits.
History
A source keeps events for 90 days unless Albastar Corp. sets another retention, from 1 to 3,650 days. An archived source takes no new events from publish keys or webhooks; its history stays readable until retention removes it.
Reference
- Events reference: fields,
displayvalues, responses and error codes forPOST /v1/events,POST /v1/events/batchandPATCH /v1/events/sources/self.