Receiving
Read keys, listing, the live stream, reconnecting, and dismissal.
Access: Keys with the events:read scope, issued by Albastar Corp.
A reader (an app such as a dashboard) reads events with a read key: a list for history, a server-sent event stream for new events.
Read keys
Albastar Corp. issues each read key with a name, such as dashboard, and a grant: every source, or a list of
sources. Every response and stream frame holds events of granted sources only. Store the key as
ALBASTAR_EVENTS_READ_KEY.
A publish key on a read route fails with 403 scope_missing. Dismissal state belongs to the read key: two readers
with the same key share dismissals; two keys do not.
Check a key
GET /v1/events/info returns the key's kind, name and granted sources. The route accepts a read key or a publish
key. Call the route once when a reader adds a connection.
curl https://api.albastov.com/v1/events/info \
-H "Authorization: Bearer $ALBASTAR_EVENTS_READ_KEY"{
"protocol": "ambient/1",
"name": "Albastar API",
"key": "reader",
"reader": "dashboard",
"sources": ["stripe", "vercel"],
"features": ["batch", "search"],
"docs": "https://api.albastov.com/v1/events/docs"
}List events
GET /v1/events lists events newest first, 100 by default and at most 500 a page. Filter by source, type,
severity_min and text (q); sticky=open lists the sticky events this key has not dismissed.
curl
curl "https://api.albastov.com/v1/events?limit=5&severity_min=warn" \
-H "Authorization: Bearer $ALBASTAR_EVENTS_READ_KEY"Python
import json, os, urllib.request
req = urllib.request.Request(
"https://api.albastov.com/v1/events?limit=5&severity_min=warn",
headers={
"User-Agent": "my-app/1.0",
"Authorization": f"Bearer {os.environ['ALBASTAR_EVENTS_READ_KEY']}",
},
)
for event in json.load(urllib.request.urlopen(req))["events"]:
print(event["received_at"], event["source"], event["text"])JavaScript
const res = await fetch(
"https://api.albastov.com/v1/events?limit=5&severity_min=warn",
{
headers: {
Authorization: `Bearer ${process.env.ALBASTAR_EVENTS_READ_KEY}`,
},
},
);
for (const event of (await res.json()).events) {
console.log(event.received_at, event.source, event.text);
}Page back with before=<next_before> until next_before is null. Catch up with after=<cursor>: events after
the cursor, oldest first. latest_cursor is the newest cursor among the granted sources; pass the value as after
or as the stream's Last-Event-ID to resume. Response fields:
List events.
Stream events
GET /v1/events/stream holds a text/event-stream open and sends each new event of the granted sources as an
event frame whose id is the event's cursor. A state frame reports a dismissal by the same key, a gap frame
reports events the catch-up left out, and a : keepalive comment arrives every 20 s.
curl -N https://api.albastov.com/v1/events/stream \
-H "Authorization: Bearer $ALBASTAR_EVENTS_READ_KEY"retry: 3000
: connected
id: 1752
event: event
data: {"id":"evt_01M6GDQMZRD7F5H9K3M6P8R1T4","cursor":"1752",…}
event: state
data: {"id":"evt_01M6GDQM…","dismissed_at":"2026-11-03T10:05:00Z"}
: keepaliveWithout a cursor, the stream starts at new events. With Last-Event-ID: <cursor> or ?after=<cursor>, the stream
first sends the events after the cursor (at most 1,000), then this key's dismissals since the cursor, then new
events. Every frame: Stream events.
Reconnect
- On start, call
GET /v1/events/info, thenGET /v1/events?sticky=openfor open sticky events, thenGET /v1/events?limit=1forlatest_cursor. - Open the stream with
Last-Event-ID: <latest_cursor>. - Store the
idof everyeventframe. - Reconnect when 60 s pass without a byte, or when the connection closes. Wait 1 s, then double the wait up to 30 s, with jitter; reset the wait after a successful connection.
- Resume with
Last-Event-ID: <last stored id>. On agapframe, fetch the skipped events withGET /v1/events?after=<after>&limit=500, repeating with the last cursor of each page asafteruntil the page reaches cursorresume. - On
429 too_many_streams, close an unused stream of the same key, then retry.
Dismiss events
POST /v1/events/{id}/dismiss sets this key's dismissal of an event; DELETE on the same path clears the dismissal.
Both are idempotent: a second POST keeps the first time. The response is the event with dismissed_at, and every
open stream of the same key receives a state frame.
curl -X POST \
https://api.albastov.com/v1/events/evt_01M6GDQMZRD7F5H9K3M6P8R1T4/dismiss \
-H "Authorization: Bearer $ALBASTAR_EVENTS_READ_KEY"Conventions
- Cursors are opaque decimal strings, strictly increasing in store order.
- Times are RFC 3339 UTC. Optional fields are
null. - Errors are JSON with
codeandmessage.
Reference
- Events reference: query parameters, frames, responses and error codes for every read route.