Quickstart
Your first VEXKIO call.
The shortest path to a 200 from the Emotions API. No SDK. Every snippet on this page is transcribed from supabase/functions/emotions/index.ts, so the body you copy is the body the handler validates.
1. Grab an API key
Sign in to app.vexkio.com and open Settings / API keys. Create a key and scope it to emotions for this tutorial. Scope matters: a key without the endpoint's scope is rejected with the same 401 as a missing key, so a scope mistake looks exactly like a bad key.
export VEXKIO_API_KEY="<your key>"2. Install nothing, and check the wiring
The API is HTTP plus JSON. You do not need a package. There is no health or usage endpoint to ping - this page previously told you to call /healthcheck, which does not exist and returns 404. Probe the real endpoint instead:
# Cheapest valid probe: an unauthenticated POST.
# A 401 proves the endpoint is reachable and the auth layer is alive.
curl -i https://krrrxshxncvcsumugxbi.functions.supabase.co/emotions -X POST -H "Content-Type: application/json" -d '{}'
# Now with your key. An empty object is a well-formed request that fails
# validation, so a valid key returns 400 with the field it wants:
# {"error":"Provide image_b64 (string) or landmarks (array)"}
curl -i https://krrrxshxncvcsumugxbi.functions.supabase.co/emotions \
-X POST \
-H "Authorization: Bearer $VEXKIO_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'A 400 naming a missing field means your key is valid and scoped. A 401 means the key is missing, wrong, revoked, or not scoped to emotions.
3. Make your first scoring call
The endpoint accepts exactly two inputs and needs at least one of them: image_b64 (base64 of a single frame) or landmarks (MediaPipe FaceMesh tuples). There is no frames array and no batching - one call scores one moment.
curl https://krrrxshxncvcsumugxbi.functions.supabase.co/emotions \
-X POST \
-H "Authorization: Bearer $VEXKIO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"session_id": "demo-0001",
"image_b64": "<base64 of one JPEG or PNG frame, no data: prefix>"
}'The Bearer header is the same across every endpoint. The body is not: each product validates its own fields. Check the endpoint's page in the API reference before you reuse this body anywhere else.
4. Read the response
A 200 returns the dominant state, the confidence for that state, the full distribution, and - the field that matters most - inference, which tells you what actually produced the answer.
Example values, not a measurement
The field names, types and nesting below are transcribed from the deployed handler. The numbers and strings are placeholders chosen to illustrate the shape. They are not a recorded call, not a benchmark, and not a claim about how any VEXKIO model performs.
In particular confidence is the probability the classifier assigned within a single request. It is not accuracy. VEXKIO publishes no accuracy figure on this site.
{
"state": "apertura",
"confidence": 0.41,
"probabilities": {
"neutral": 0.19,
"apertura": 0.41,
"friccion": 0.15,
"tension": 0.14,
"desconexion": 0.11
},
"inference": "heuristic",
"session_id": "demo-0001",
"latency_ms": 38,
"model_version": "heuristic-v1",
"timestamp": "2026-01-01T00:00:00.000Z",
"llm_enrichment": null
}inference
Read this before anything else. real means the IA-01 model produced the score. heuristic means a geometric rule over your landmarks did - no model ran. unavailable and insufficient_signal mean nothing was measured.
state
The argmax of probabilities: one of neutral, apertura, friccion, tension, desconexion - or the literal insufficient_signal.
The second tab above is a real 200. When there is no usable signal the endpoint still answers 200, with confidence forced to exactly 0, an all-zero probability map and a reason string. Treat that as no reading, never as a neutral reading. Code that only checks res.ok will silently record a zero as a result.
5. Handle errors and rate limits
Errors are JSON with a single human-readable error string. This page used to promise a stable { code, message, request_id, retry_after_ms } envelope across all products. None of those four fields exists. Branch on the HTTP status.
{ "error": "Provide image_b64 (string) or landmarks (array)" }| Status | Meaning | Action |
|---|---|---|
| 400 | Body or field rejected | Read error; it names the field. Terminal. |
| 401 | Key missing, invalid, revoked, or missing this scope | Check the scope before re-issuing. Terminal. |
| 403 | Organization suspended | Billing or account state. Terminal. |
| 413 | Payload over the cap | Downscale the frame or shorten the clip. Terminal. |
| 429 | IP throttle, daily limit, session quota or cost cap | Back off on your own schedule - no retry hint is returned. |
| 5xx | Upstream issue | Bounded exponential backoff, then give up. |
// Bounded backoff. Note what is NOT here: no retry_after_ms field
// exists on these responses, and no X-RateLimit-* headers are emitted,
// so the client owns the schedule.
import { setTimeout as sleep } from "node:timers/promises";
const DELAYS_MS = [250, 500, 1000, 2000];
export async function scoreWithRetry(body: unknown): Promise<unknown> {
for (const delay of [0, ...DELAYS_MS]) {
if (delay > 0) await sleep(delay);
const res = await fetch("https://krrrxshxncvcsumugxbi.functions.supabase.co/emotions", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.VEXKIO_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
if (res.ok) return await res.json();
if (res.status === 429 || res.status >= 500) continue; // transient
const err = await res.json().catch(() => ({}));
throw new Error(`${res.status}: ${err.error ?? "unknown error"}`); // terminal
}
throw new Error("emotions: exhausted retries");
}Next steps
Browse the API reference
Four endpoints with a fully transcribed contract; the rest marked as not documented yet rather than guessed.
Try the Voice API
Base64 audio or MFCC frames over REST. Read its caveats before trusting a confidence of 1.
Combine face and voice
Fusion needs both modalities in one call to reach the model path.
Open the dashboard
Usage, billing, audit log and key management.