Search docs

Search the Vexo developer documentation

Get the ring

Platform

Captures API

The write-and-fan-out loop. A capture is a moment of consented context, distributed wherever the wearer allows.

POST/v1/captures
GET/v1/captures/stream

The capture object

Every capture carries its own consent record. The signals summary, the grant that authorized it, and the fan-out result travel together, so any consumer can audit why it holds the data it holds.

The capture object
capture.json
{
  "id": "cap_7hd93kq2m4",
  "object": "capture",
  "ts": "2026-07-18T14:22:07.412Z",
  "wearer": "vx_user_9f2k8paq",
  "device": "ring_04c7e1",
  "signals": {
    "hr": 58,
    "hrv": 61,
    "temp_delta": -0.12,
    "motion": "still"
  },
  "consent": {
    "grant": "grant_p8s2vw",
    "scopes": ["captures:read", "biometrics:hrv"],
    "granted_at": "2026-07-12T09:03:44Z",
    "revocable": true
  },
  "distribution": [
    { "type": "app", "target": "app_focuslog", "delivered": true },
    { "type": "webhook", "target": "wh_x83nd0", "delivered": true },
    { "type": "mcp-server", "target": "mcp_calmcoach", "delivered": false }
  ]
}
NameTypeDescription
idrequiredstringUnique identifier, prefixed cap_.
tsrequiredstringISO 8601 timestamp of the moment the ring sealed the capture.
wearerrequiredstringThe wearer's user id, prefixed vx_user_. Stable across grants.
signalsrequiredobjectDerived metrics fused on-device. Only families covered by the grant appear.
consentrequiredobjectThe grant id, the scopes it covers, and when it was given. Immutable once written.
distributionarrayWhere the capture fanned out: apps, webhooks, and MCP servers, with per-target delivery state.Default: []
notestringOptional app-supplied annotation, 280 characters max. Shown to the wearer verbatim.Default: null

Fields on the capture object.

Create a capture

POST/v1/captures

Creating a capture asks the ring for a sealed reading now, rather than waiting for the wearer's own cadence. The call blocks until the ring responds, typically under 400ms when the wearer is in range.

Create a capture
create.ts
import { Vexo } from "@vexoring/sdk";

const vexo = new Vexo({ token: process.env.VEXO_USER_TOKEN });

const capture = await vexo.captures.create({
  wearer: "vx_user_9f2k8paq",
  signals: ["hr", "hrv", "temp"],
  note: "post-run cooldown",
});

console.log(capture.id); // cap_7hd93kq2m4

Stream captures

GET/v1/captures/stream

The stream endpoint speaks WebSocket by default and falls back to SSE when the client cannot upgrade. Both carry the same capture objects in the same order. The SDK picks the transport for you and exposes one async iterator either way, so your loop does not change.

Stream captures
stream.ts
const stream = vexo.captures.stream({
  wearer: "vx_user_9f2k8paq",
  signals: ["hrv", "motion"],
});

for await (const capture of stream) {
  // { id: "cap_7hd93kq2m4", signals: { hrv: 61, motion: "still" }, ... }
  render(capture.signals);
}

// The loop ends when the grant is revoked. Close code: 4403.

The distribution loop

After a capture is sealed, Vexo fans it out to every destination the wearer has authorized. Distribution is per-destination and per-scope: revoking one grant removes one target from the loop without touching the others. Each entry in the capture's distribution array records the outcome.

NameTypeDescription
appdestinationYour application, via the REST API or an open stream. Requires captures:read.
webhookdestinationA signed POST to an endpoint you registered. Requires webhooks:manage on the app plus captures:read on the wearer.
mcp-serverdestinationA marketplace context server acting for an agent. Requires the scopes declared in its manifest, granted individually by the wearer.

Destination types and the scope each one requires.

Errors

Capture errors are precise about consent. A 401 means the token is dead, a 403 means the token is alive but the grant does not cover the request. Never retry a 403; ask for the scope instead.

NameTypeDescription
401 token_revokederrorThe wearer revoked the grant behind this token. Re-run the authorization flow; do not retry.
403 scope_missingerrorThe grant is active but does not include the scope this call needs. Request the scope, then retry once granted.
409 wearer_offlineerrorThe ring is out of range or asleep. Retry with backoff, or subscribe to device.paired webhooks to know when it returns.
429 rate_limitederrorPer-wearer create limit is 60 per minute. Honor the Retry-After header; the stream endpoint is never rate limited.

Error codes returned by capture endpoints.