Supabase backend API contracts
Verified from supabase/config.toml, the nine function handlers under
supabase/functions/, and their browser callers on 2026-08-03. This document
describes the repository state; it is not a substitute for checking the
deployed function configuration before a live operation.
All public function URLs have the form
<VITE_SUPABASE_URL>/functions/v1/<function-name>. Browser callers use the
shared nullable Supabase client or a user access token. Secret-bearing calls
remain inside Edge Functions.
Function surface
| Function | Methods | Authentication enforced by current code/config | Request and response contract |
|---|---|---|---|
chat-models | GET, POST, OPTIONS | Explicit verify_jwt = true; handler also requires Bearer and calls auth.getUser() | GET accepts refresh=1; POST accepts {refresh?: boolean}. Returns the verified model/combo catalog, default availability, fetch time, stale flag, optional warning, image_input_version, and explicit per-model image capability metadata. |
run-chat | GET, POST, OPTIONS | Explicit verify_jwt = true; handler validates the bearer user, rate-limits that user, and checks project ownership | GET is only the model-catalog compatibility path and requires models=1 (refresh=1 is optional). POST validates project_id, session/turn identifiers, input, bounded history, optional brain context, model, response version, and optional ephemeral image attachments. The research-v1 path returns validated JSON and persists a session, trace, and LLM span; the legacy transport remains SSE-compatible. |
chat-affect | POST, OPTIONS | Explicit verify_jwt = true; handler validates the bearer user, rate limit, project ownership, and referenced span/trace | Accepts bounded identifiers, phase, text hash, a bounded affect analysis, optional text/model, and optional router refinement. Persists a sanitized annotation; raw text is not stored in annotation metadata. |
chat-feedback | POST, OPTIONS | Explicit verify_jwt = true; handler validates the bearer user, rate limit, project ownership, and referenced span/trace | Accepts project/span/trace/session/turn identifiers, positive or negative, score 1 or 0, and an optional bounded explanation. Writes a user_feedback span annotation. |
ingest-arcade-trajectory | POST, OPTIONS | Explicit verify_jwt = true; handler validates the bearer user and owner-scoped project | Accepts the validated episode and chunk envelope produced by src/data/arcade-research.ts. Calls the service-role-only ingest_arcade_trajectory_internal function and returns acknowledged episode/chunk state. |
delete-arcade-trajectories | POST, OPTIONS | Explicit verify_jwt = true; handler validates the bearer user | Calls the service-role-only delete_arcade_trajectories_internal function for the authenticated owner and returns deleted episode/chunk counts. This is separate from clearing a local consent queue. |
run-playground | POST, OPTIONS | No explicit function entry in supabase/config.toml; the handler requires Bearer, calls auth.getUser(), and rate-limits the user | Accepts a CHAT or STR template, format, provider/model routing values, invocation parameters, variables, and optional tools/response format/input. Streams token, done, or error SSE events. |
run-eval | POST, OPTIONS | No explicit function entry in supabase/config.toml; handler requires x-exepert-ingest-key matching EXEPERT_INGEST_KEY | Accepts {spanId?, traceId?, evaluatorName?} and requires a span or trace identifier. Runs a configured heuristic/code/LLM evaluator and writes annotations with the service role. |
otlp-receiver | POST, OPTIONS | No explicit function entry in supabase/config.toml; handler requires the constant-time-checked x-exepert-ingest-key, applies a per-isolate rate limit, and caps the body | Accepts OTLP/HTTP JSON resourceSpans, maps OpenInference attributes, and writes traces/spans with the service role. The Playwright live-verification spec only probes unauthenticated rejection; it does not perform successful ingestion. |
Unsupported methods return 405. JSON endpoints use structured error bodies;
streaming endpoints report terminal errors as SSE events where the handler has
already opened a stream.
Native image-input compatibility
chat-models advertises native image support only when the catalog includes
image_input_version: "image-input-v1". Each model also carries
supports_images and image_capability_source. Capability comes only from
explicit provider metadata, the server-side ROUTER_VISION_MODELS allowlist,
or a combo whose known members all support images; route names are never used
as evidence. A client connected to an older catalog remains safely text-only.
run-chat accepts additive attachments arrays on the current turn and on
user messages. PNG, JPEG, and WebP data is validated for count, strict base64,
MIME and magic-byte agreement, dimensions, per-image size, and the total
request budget before 9Router is called. Image-only current turns are valid;
blank text without a validated current attachment is not. The browser Chat UI
remains text-only, while the native desktop may use this optional contract.
Images are session-memory context, not durable records. Persisted transcript and observability payloads contain only bounded count, MIME, dimensions, and byte-size metadata—never filenames, base64, data URLs, or raw bytes. This feature adds no Storage bucket or database migration.
Browser-to-function callers
src/chat/client.tscallschat-models,run-chat, andchat-feedback.src/affect/client.tscallschat-affect.src/data/arcade-research.tsinvokes the two Arcade functions.src/data/dal.tsinvokesrun-eval.src/ui/prompt-playground.tscallsrun-playground.- OTLP ingestion is a trusted exporter/server integration, not a normal browser DAL write path.
Security invariants
- Treat the six explicit
verify_jwtentries and handler checks as separate, cumulative controls. A directory without a config entry is not proof that a deployed function is public or private. - Validate the exact hosted project and deployed configuration before invoking live endpoints. Repository inspection alone does not prove deployment state.
- Browser bundles may contain only client-safe
VITE_*values. Service-role, ingest, router, provider, and administrative keys remain Edge secrets or server-only values. - Preserve owner checks before privileged writes. Do not add service-role use to browser code or broaden RLS to make a request pass.
- Do not assume a standalone
denoexecutable exists. Use the Supabase CLI-managed Edge Function workflow unless Deno is explicitly installed and validated for a task.
Source of truth
- Function deployment/auth configuration:
supabase/config.tomlplus the deployed project configuration. - Request validation and response behavior: each
supabase/functions/<name>/index.tshandler and its shared modules. - Browser request construction: the callers listed above.
- Database authorization and callable SQL: cumulative files in
supabase/migrations/, deployed state, and current advisor results.