Skip to content
VEXKIO/ docs
QuickstartAPI ReferenceGuidesExamples
Dashboard
  • Overview
  • Quickstart
  • Authentication
  • Errors and rate limits
All endpoints (33)
Core APIs
  • Emotions API
  • Voice API
  • Fusion API
  • Signs API
Verticals
  • VEXKIO Copilot
  • VEXKIO Coach
  • VEXKIO Learn
  • VEXKIO Call
  • VEXKIO Sales
  • VEXKIO Research
  • VEXKIO Wellbeing
  • VEXKIO Telemedicine
  • VEXKIO Real Estate
  • VEXKIO Finance
  • VEXKIO Interview
  • NEXUS CRM
Disruptive
  • VEXKIO Studio
  • VEXKIO Negotiations
  • VEXKIO Live
  • VEXKIO Podcast
  • VEXKIO White-label
Platform
  • VEXKIO Analytics
  • VEXKIO Data Insights
  • VEXKIO Simulator
  • VEXKIO OEM SDK
B2C
  • VEXKIO Mirror
  • VEXKIO Kids
Social Impact
  • VEXKIO Signs
  • VEXKIO Signs Edu
  • VEXKIO Signs Enterprise
  • VEXKIO Edu
  • VEXKIO Rehab
  • VEXKIO Therapy Voice
  • All endpoints
  • Status page
  • Marketing site
  • Dashboard
API Reference / Core APIs

Fusion API

Face plus voice in one call. Both modalities required for the model path.

Endpoint
POST /fusion
Auth
Bearer api_key
Price
$0.08 per session

Request

POST a JSON object. The fields below are the ones the handler reads; anything else in the body is ignored. Every rule in the table was transcribed from supabase/functions/fusion/index.ts.

FieldTypePresenceBehaviour in the handler
session_idstringoptionalOpaque string. Omit and a UUID is generated for you.
image_b64stringone of theseBase64 frame. Capped at 15,000,000 characters (413 above it).
audio_b64stringone of theseBase64 clip. Capped at 40,000,000 characters (413 above it).
landmarksnumber[][]one of theseCap here is 1000 points, NOT the 478 of /emotions — the two endpoints do not share a limit. Elements must be arrays.
mfcc_featuresnumber[][]one of theseCap here is 2000 frames, NOT the 200 of /voice. Elements must be arrays.

Smallest body that clears validation. The shape is real; the values in angle brackets are placeholders you replace.

curl https://krrrxshxncvcsumugxbi.functions.supabase.co/fusion \
  -X POST \
  -H "Authorization: Bearer $VEXKIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
      "session_id": "demo-0001",
      "image_b64": "<base64 of one frame>",
      "audio_b64": "<base64 of the matching clip>"
    }'

Headers

HeaderRequiredDescription
AuthorizationyesBearer <api_key>. The key must carry the fusion scope; a key without it is rejected as 401, not 403.
Content-Typeyesapplication/json

No other request header changes behaviour. This page used to document an X-Idempotency-Key; no endpoint reads it, so it has been removed rather than left as a no-op promise.

Response

200 returns the object below. There is no envelope and no wrapper: the fields are top-level.

FieldTypePresenceBehaviour in the handler
statestringrequiredOne of the five VEXKIO states.
confidencenumberrequiredProbability the classifier assigned to `state` IN THIS REQUEST, rounded to 3 decimals. It is not model accuracy, not a benchmark, and not a quality guarantee. Read `inference` before you trust it.
probabilitiesobjectrequiredOne entry per VEXKIO state.
inferencestringrequired"real" or "heuristic".
session_idstringrequiredEchoed back, or the generated UUID.
latency_msnumberrequiredWall time measured inside the function.
model_versionstringrequiredDefaults to "ia-09-v0.1.0"; replaced by the origin's tag when a model ran.
timestampstringrequiredISO-8601, generated at response time.
llm_enrichmentobject | nullrequirednull unless VEXKIO_LLM_ENRICH=true on the deployment.

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.

example-shape.json
{
  "state": "friccion",
  "confidence": 0.28,
  "probabilities": {
    "neutral":     0.20,
    "apertura":    0.20,
    "friccion":    0.28,
    "tension":     0.16,
    "desconexion": 0.16
  },
  "inference": "heuristic",
  "session_id": "demo-0001",
  "latency_ms": 41,
  "model_version": "ia-09-v0.1.0",
  "timestamp": "2026-01-01T00:00:00.000Z",
  "llm_enrichment": null
}

Before you trust a number

  • The multimodal model only runs when image_b64 AND audio_b64 are BOTH present and the deployment has INFERENCE_URL set. One modality alone always lands on the heuristic.
  • With sparse or empty inputs the heuristic returns a near-uniform map, so confidence lands around 0.2. That is the classifier declining to choose, not a weak reading.

Errors

Almost every error body is a JSON object with a single human-readable error string. There is no stable machine-readable code field on these endpoints - branch on the HTTP status, not on the string.

error-shape.json
{ "error": "Provide image_b64 (string) or landmarks (array)" }

The second tab is the one body on these endpoints that does NOT carry an error key. A client that reads only body.error will log undefined for a cost-cap 429, so read the status first. Note the field is retry_after_seconds - seconds, not milliseconds - it appears on this body only, and its value is however many seconds remain until 00:00 UTC, so the number above is just an illustration.

HTTPWhen
400Body is not a JSON object, or a field failed validation. The error string names the field.
401Missing Bearer header, unknown key, revoked key, or a key that lacks this endpoint's scope. All four collapse into the same 401 - a missing scope is NOT a 403 here.
403Organization is suspended. Some non-core endpoints also use 403 for missing consent or attestation flags.
413Body over 12,000,000 bytes, or a base64 field over its own cap (15,000,000 chars for image_b64, 40,000,000 for audio_b64).
429Per-IP throttle, per-org daily limit, monthly session quota exhausted, or a cost cap. See Rate limits below.

Codes this page used to list - invalid_payload, scope_missing, session_not_found, model_unavailable - are not emitted by any handler and have been removed.

Rate limits

Three independent limits can produce a 429. All of them are read from supabase/functions/_shared/rate_limit.ts and the handler itself.

LimitValueScope
Pre-auth throttle60 / minutePer client IP, in-memory, checked before the key is even validated.
Daily request limit10,000 / day (default)Per organization. Resolved from your daily_quota, else your sessions_limit, else the deployment default.
Session quotaper planCumulative sessions used against your plan ceiling. Exhausting it returns 429 until the plan is upgraded.

Two corrections to what this page used to claim. There is no Starter / Pro / Business / Enterprise rps table in force - the tiered limiter exists in the repo but is not imported by any deployed function. And no endpoint emits X-RateLimit-Limit, X-RateLimit-Remaining or X-RateLimit-Reset headers, so do not build a budget on reading them. Retry with your own bounded exponential backoff.

Ready to call it?

Issue a key scoped to fusion in the dashboard and run the curl above. $0.08 per session.

Get an API keyRead the quickstart

Product

  • Marketing site
  • Product catalog
  • Dashboard

Docs

  • Quickstart
  • API Reference
  • Status

Company

  • Privacy
  • Terms
  • hello@vexkio.com

Copyright 2026 VEXKIO. All rights reserved.