Console coming soon
Browse documentation

Developer docs

Start from a live reference page.

Phone layout stays search-first: pick a page here, then read it in a full single-column view.

Getting Started

Models

API Reference

Operations

Enterprise

Console

Policies

Responses API

The endpoint everything runs through. Send a prompt, choose a model, and get an answer back with what it cost and the evidence behind it.

Endpoint

POST https://evaesi.dev/api/v1/responses

The older POST /api/v1/chat route remains mounted as a compatibility alias, but /api/v1/responses is the forward contract.

The request

FieldTypeRequiredDescription
promptstringYesPrompt text to dispatch through the inference gateway.
modelstring?NoModel identifier. Canonical values are "instant", "reasoning", and "elements"; common aliases normalise case-insensitively (see Models). If omitted, selection is delegated to the inference gateway, the response's model field reports what was actually used.
max_output_rungsu64?NoOutput ceiling forwarded to the gateway, validated against the selected model's output limit, a value of 0 or one above the limit returns 400 INVALID_REQUEST. The accepted value is used verbatim for budget reservation and daily-throughput charging. Defaults and limits per model: instant 32,000; reasoning and elements 64,000.
include_regionsbool?NoExpose region metadata in usage where available.
studio_idstring?NoAccepted but not yet wired, the field is parsed and currently ignored, and the request dispatches with no studio context. Do not depend on it.
custom_instructionsstring?NoOptional instruction override.
web_searchboolNoFavor fresh retrieval where supported.
code_modeboolNoCode and documentation mode.
image_modeboolNoImage or visual mode.
streambool?NoEnable SSE streaming on the same route.

Live validation rejects empty prompts and prompts over 100000 bytes. If include_regions is enabled today, the field is reserved in the response but currently reports 0 because the live gateway does not emit region activations yet.

Response

{
  "request_id": "uuid",
  "content": "Generated text...",
  "sources": [],
  "sources_url": "/api/v1/sources/uuid",
  "usage": {
    "input_rungs": 29,
    "output_rungs": 118,
    "total_rungs": 147,
    "regions_activated": null
  },
  "model": "instant"
}

The live response payload emits sources_url as /api/v1/sources/{request_id}. The nested /api/v1/responses/{request_id}/sources path remains mounted as a compatible alias.

Two further optional fields are omitted from the JSON entirely (not null) when absent: evidence_verdict (string, the EsiGo evidence verdict when the pipeline produced one) and route (object, route metadata from the inference edge: route_id, selected_entry_point, primary_model, evidence_model, route_class, studio_required, approval_required, decided_at_ns).

Streaming

When stream: true, the same endpoint returns server-sent events. Current live event names are:

EventMeaning
response.output_text.deltaPartial output chunk
response.completedFinal completion payload
response.errorStream-level error payload

At stream start, the response headers include input-rung and rate-limit metadata (x-evaesi-input-rungs, x-ratelimit-limit, x-ratelimit-remaining). Output and total rung headers are not sent on the streaming path, instead the final response.completed event carries a usage object (input_rungs, output_rungs, total_rungs). Those values are committed to billing before the final event is sent, so they always match what GET /api/v1/usage shows afterwards. Streamed responses carry no sources_url: construct it yourself as /api/v1/sources/{request_id} from any chunk's request_id.

Each data: line is a JSON-serialized InferenceChunk. Only index, content, and done are guaranteed on every chunk; request_id, model, sources, usage, and the other optional fields are omitted when absent or empty, "sources":[] never appears on the wire, and usage appears only on the final event.

event: response.output_text.delta
data: {"index":0,"content":"The ","done":false,"request_id":"req_abc","model":"instant"}

event: response.output_text.delta
data: {"index":1,"content":"answer ","done":false,"request_id":"req_abc","model":"instant"}

event: response.completed
data: {"index":42,"content":"","done":true,"request_id":"req_abc","model":"instant","usage":{"input_rungs":29,"output_rungs":118,"total_rungs":147}}

Streamed requests always return HTTP 200. The response head is sent before inference is attempted. Errors arrive in-band as a response.error event whose data is {"error":{"code","message"}}. A mid-stream failure emits response.error followed by a final response.completed; a failure to start the stream emits response.error with no completion event. Handle response.error explicitly, never rely on the HTTP status to detect streaming failures. The stream also sends SSE keep-alive comment lines (prefixed :) during idle periods; ignore any line not prefixed data: .

Examples

cURL

curl -X POST https://evaesi.dev/api/v1/responses \
      -H "Authorization: Bearer esi_dev_YOUR_KEY" \
      -H "Content-Type: application/json" \
  -d '{
    "prompt": "What is the capital of France?",
    "model": "instant",
    "web_search": true
  }'

Python

import requests

response = requests.post(
    "https://evaesi.dev/api/v1/responses",
    headers={"Authorization": "Bearer esi_dev_YOUR_KEY"},
    json={
        "prompt": "What is the capital of France?",
        "model": "instant",
        "web_search": True,
    },
)

data = response.json()
print(data["content"])
print(f"Used {data['usage']['input_rungs']} input rungs")

JavaScript

const response = await fetch("https://evaesi.dev/api/v1/responses", {
  method: "POST",
  headers: {
    "Authorization": "Bearer esi_dev_YOUR_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    prompt: "What is the capital of France?",
    model: "instant",
    web_search: true,
  }),
});

const data = await response.json();
console.log(data.content);

Streaming Example

const response = await fetch("https://evaesi.dev/api/v1/responses", {
  method: "POST",
  headers: {
    "Authorization": "Bearer esi_dev_YOUR_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    prompt: "Explain gravity.",
    model: "instant",
    stream: true,
  }),
});

const reader = response.body.getReader();
const decoder = new TextDecoder();

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  const text = decoder.decode(value, { stream: true });
  for (const line of text.split("\n")) {
    if (line.startsWith("data: ")) {
      const chunk = JSON.parse(line.slice(6));
      if (chunk.error) {
        throw new Error(`${chunk.error.code}: ${chunk.error.message}`);
      }
      if (chunk.done) {
        console.log("\nrequest_id:", chunk.request_id);
        break;
      }
      process.stdout.write(chunk.content ?? "");
    }
  }
}