API reference

Build on VERIS.

One endpoint per AI capability. Multi-provider routing, cost controls, and full request history are included. Everything below is live today.

Base URL

production
https://veris.digital/api/v1

Health, the OpenAPI spec, and CORS preflight need no authentication. Every other endpoint requires a bearer token.

Authentication

header
Authorization: Bearer $VERIS_API_KEY

Keys look like veris_ab12cd_…. Legacy aios_ keys still work. There is no x-api-key header. Create a key inside VERIS at Applications → your application → Developer → API keys. A key is scoped to its workspace and can address any application and environment in it.

Health check

GET /api/v1/health · no auth
curl https://veris.digital/api/v1/health

{"status":"ok","version":"1.0.0","capabilities":["chat","embed"],"timestamp":"2026-08-02T12:00:00.000Z"}

Chat

POST /api/v1/chat
curl -X POST https://veris.digital/api/v1/chat \
  -H "Authorization: Bearer $VERIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "application": "resume-parser",
    "environment": "production",
    "workload": "extract",
    "input": "Extract name, email, and years of experience from this resume."
  }'
FieldRequiredDescription
applicationYesApplication slug. project is accepted as a legacy alias, and OpenAI SDK clients may send it as model.
environmentNoDefaults to production.
workloadNoWorkload tag used for routing, evaluation, and cost attribution.
inputEitherShorthand for a single user message.
messagesEitherArray of { role, content }. One of input or messages is required.
maxCostUsdNoReject the request if the cheapest candidate exceeds this cost.
200 response
{
  "requestId": "8f1c0b2e-…",
  "status": "ok",
  "provider": "openai",
  "model": "gpt-4o-mini",
  "text": "{\"name\":\"Ada Lovelace\",…}",
  "inputTokens": 42,
  "outputTokens": 128,
  "latencyMs": 812,
  "costUsd": 0.00034,
  "fallbackUsed": false,
  "workload": "extract"
}

Every response carries an x-veris-request-id header matching requestId. Use it to find the routing decision in Request History. For streaming, use the OpenAI-compatible endpoint below with stream: true.

OpenAI-compatible chat

POST /api/v1/chat/completions speaks the OpenAI chat completions contract. Standard fields keep standard meanings: model is a model, never an application. Send veris-auto to let VERIS choose the model for the workload. The application comes from your API key, or from the x-veris-application header.

Sending your application name as the model, for example "model": "amatch", is rejected rather than silently ignored. Send "model": "veris-auto" and pass the application in the x-veris-application header. To constrain which models VERIS may choose, set model preferences on the workload.

typescript
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://veris.digital/api/v1",
  apiKey: process.env.VERIS_API_KEY,
});

const res = await client.chat.completions.create({
  model: "veris-auto", // VERIS selects the model
  messages: [
    { role: "system", content: "You are a resume parser. Reply with JSON." },
    { role: "user", content: "Extract name, email, and years of experience." },
  ],
});

console.log(res.choices[0].message.content);

Routing context travels in headers, so the request body stays a standard OpenAI body: x-veris-application, x-veris-environment, x-veris-workload, x-veris-strategy, x-veris-domain.

typescript (streaming)
const stream = await client.chat.completions.create(
  {
    model: "veris-auto",
    messages: [{ role: "user", content: "Summarise this contract." }],
    stream: true,
  },
  { headers: { "x-veris-application": "contract-analyzer", "x-veris-workload": "summarise" } },
);

for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}

Tool turns round-trip. Send the assistant message with its tool_calls, then the tool message carrying the matching tool_call_id. VERIS transports tool history and preserves call ids end to end. It never executes a tool, and it only routes the turn to a model that supports tools.

VERIS never silently ignores a parameter. A field it cannot honour returns a 400 naming the field, so you always know what ran. A tool result whose tool_call_id matches no preceding call is rejected by name rather than dropped. Responses carry x-veris-request-id, x-veris-provider and x-veris-model.

Embeddings

POST /api/v1/embeddings
curl -X POST https://veris.digital/api/v1/embeddings \
  -H "Authorization: Bearer $VERIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "application": "resume-parser",
    "environment": "production",
    "input": "Senior software engineer with 8 years of backend experience."
  }'

input is a string or an array of strings. The response follows the OpenAI shape and adds requestId, provider, dimensions, latencyMs, costUsd, and fallbackUsed, all safe to ignore.

Document OCR

POST /api/v1/document/ocr
curl -X POST https://veris.digital/api/v1/document/ocr \
  -H "Authorization: Bearer $VERIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "application": "resume-parser",
    "environment": "production",
    "document": {
      "source": "url",
      "mime_type": "application/pdf",
      "url": "https://example.com/invoice.pdf"
    }
  }'

Send the file as a permitted HTTPS url, as inline base64 ("source": "inline"), or as a multipart upload. The response returns page text plus page_count, pages_succeeded, pages_failed, and the provider's own charge in its own currency. When a provider prices in a currency other than USD, cost_usd is null rather than a misleading zero.

Errors

every non-2xx response
{
  "error": {
    "code": "invalid_api_key",
    "message": "The API key is invalid.",
    "requestId": "8f1c0b2e-…"
  }
}
CodeHTTPWhen
missing_api_key401No Authorization: Bearer header.
invalid_api_key401Malformed, revoked, expired, or unknown key.
invalid_request400Request body failed validation.
project_not_found404Application slug does not exist in this workspace.
environment_not_found404Environment does not exist for this application.
budget_blocked402Monthly budget cap reached.
no_candidates422No provider satisfies the routing policy.
rate_limited429Too many requests. Back off with jitter.
internal500Every upstream provider failed after fallbacks.

Rate limits

PlanRequestsConcurrent
Free60 / minute5
Team600 / minute50
Business3,000 / minute250
EnterpriseCustomCustom

Limits apply per workspace. Exceeding one returns 429 with code rate_limited.

Versioning

/api/v1 is stable. Breaking changes ship in /v2. New optional fields and new capabilities may appear in v1 at any time.

Machine-readable

On the roadmap

Coming soon
SDKs
TypeScript, Python, Go clients.
Coming soon
CLI
veris: routing, keys, and deploys.
Coming soon
Realtime
Stream responses and events.
Need help?
Reach out and we will unblock you.
Contact us