Skip to content

Cheap change signal for the caller's notifications

GET
/v1/notifications/head
curl --request GET \
--url http://localhost:3000/v1/notifications/head \
--header 'Authorization: Bearer <token>'

Redis-only head check for the caller’s notification stream. Requires a caller IDENTITY, not a particular token type: a user token works, and so does a server token impersonating a user via X-Dropin-User-Id. Only a BARE server token — one with no impersonation header — is rejected with FORBIDDEN, because there is no single stream to answer for. That is the same identity-not-token-type rule the rest of the API follows (spec §5, §6). A backend poller can therefore use this endpoint by impersonating the user it is polling for. No unread count — counts live in the notifications list response.

Current head token.

Media typeapplication/json
object
latest
required

Newest id fanned out to this stream, or null meaning “nothing new”.

string | null
changed
required

Tenant mutation counter. Incremented on every write the head token cannot report — an activity edited, a reaction moved, an object written. Unchanged since your last revalidation means nothing in this tenant has been mutated, so a client can skip re-reading the page and its objects entirely.

0 means nothing has ever been mutated in this tenant. That is a definite answer, not an absent one, so a brand-new or idle tenant gets the same skip as any other.

null means unknown — a counter key that exists but does not parse. Treat unknown as “revalidate”: a wasted read is cheap, a permanently stale feed is not. A Redis outage is not this case; the route fails and the client retries.

Tenant-wide on purpose. An object does not know which feeds reference it, so a per-feed counter would need fan-out on write. The cost is a false positive: one tenant’s write makes every open feed in that tenant revalidate once.

integer | null
Examplegenerated
{
"latest": "example",
"changed": 1
}

Invalid, expired, revoked, or absent token.

Media typeapplication/json
object
error
required
object
code
required
string
Allowed values: VALIDATION_FAILED UNAUTHENTICATED FORBIDDEN NOT_FOUND CONFLICT RATE_LIMITED INTERNAL
message
required
string
requestId
required
string
fields

Present only for VALIDATION_FAILED.

Array<object>
object
path
string
message
string
Example
{
"error": {
"code": "VALIDATION_FAILED"
}
}

Authenticated but not permitted.

Media typeapplication/json
object
error
required
object
code
required
string
Allowed values: VALIDATION_FAILED UNAUTHENTICATED FORBIDDEN NOT_FOUND CONFLICT RATE_LIMITED INTERNAL
message
required
string
requestId
required
string
fields

Present only for VALIDATION_FAILED.

Array<object>
object
path
string
message
string
Example
{
"error": {
"code": "VALIDATION_FAILED"
}
}

Rate limit exceeded.

Media typeapplication/json
object
error
required
object
code
required
string
Allowed values: VALIDATION_FAILED UNAUTHENTICATED FORBIDDEN NOT_FOUND CONFLICT RATE_LIMITED INTERNAL
message
required
string
requestId
required
string
fields

Present only for VALIDATION_FAILED.

Array<object>
object
path
string
message
string
Example
{
"error": {
"code": "VALIDATION_FAILED"
}
}
Retry-After
integer