VendoVendo Docs
ReferenceHTTP API

Events (SSE)

Server-sent stream of connection and billing events for the calling deployment.

GET /api/deployments/me/events

Server-sent events channel scoped to the calling deployment's app. The SDK's events.subscribe() wraps this endpoint and reconnects on transient failures.

Auth

App Key only (vendo_sk_*). Connection-scoped keys and identity JWTs are rejected.

Request

GET /api/deployments/me/events HTTP/1.1
Host: vendo.run
Authorization: Bearer vendo_sk_…
Accept: text/event-stream
Last-Event-ID: <optional id of last received event>

Last-Event-ID is honored for replay-on-reconnect.

Response

Content-Type: text/event-stream. The server emits a : ping heartbeat every 25 seconds to keep proxies from closing the connection, plus zero or more event blocks of the form:

id: <event-id>
event: <kind>
data: <json-payload>

Event kinds

kindPayload fieldsMeaning
connection.connectedslug, connection_idTenant connected an integration
connection.disconnectedslug, connection_idTenant disconnected an integration
connection.changedslug, connection_idBinding metadata changed
connection.status_changedslug, connection_id, statusConnection status moved. status is one of connected, needs_reauth, revoked, error — the event bus drops any other DB status.
billing.usage_recordedprovider, microsA call was billed (USD × 10⁶)
billing.balance_changedremaining_microsWallet balance updated
billing.cap_warnedwindow (daily | monthly), pctApp-level spend cap crossed a warning threshold
billing.cap_trippedwindow (daily | monthly)Spend cap exhausted; subsequent calls in the window will 402

Every payload also includes id (event id) and at (ISO 8601 timestamp).

Legacy event aliases

For SDK versions <=1.0.1, the server also emits the same payload under legacy underscore-style event names:

CanonicalLegacy alias
connection.connectedconnection_created
connection.disconnectedconnection_deleted
connection.status_changed, connection.changedconnection_updated

Each alias is a separate SSE block — your EventSource fires both listeners with the same data. The aliases will be removed in a future SDK rev; see vendo-sdk-js#13. New clients should listen only on the canonical kind.

Concurrency cap

Each app may hold up to 5 concurrent SSE streams (MAX_STREAMS_PER_APP in web/src/app/api/deployments/me/events/active-count.ts). Exceeding the cap returns 429 with the canonical error envelope:

{
  "error": {
    "code": "rate_limit_sse_streams",
    "message": "Too many concurrent SSE streams."
  }
}

The response also carries a Vendo-Error-Code: rate_limit_sse_streams header. The cap is advisory and racey under burst — closing an idle stream frees a slot. Reuse a single subscription per process where possible.

Replay buffer

Last-Event-ID replay reaches back up to 100 events per app (web/src/lib/sse/event-bus.ts ring buffer). Beyond that, treat events as cache-invalidation signals and re-read GET /api/deployments/me/connections on reconnect.

Example

curl -N \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Accept: text/event-stream" \
  https://vendo.run/api/deployments/me/events
: ping

id: evt_01H…
event: connection.connected
data: {"id":"evt_01H…","at":"2026-05-20T12:00:00Z","kind":"connection.connected","slug":"openai","connection_id":"cn_…"}

id: evt_01H…
event: billing.usage_recorded
data: {"id":"evt_01H…","at":"2026-05-20T12:00:01Z","kind":"billing.usage_recorded","provider":"openrouter","micros":1234}

The ring buffer holds the last 100 events per app. Once Last-Event-ID falls outside that window, replay is incomplete — treat events as cache-invalidation signals and re-read from GET /api/deployments/me/connections on reconnect.

On this page