Bosphorus

← Knowledge base

Evren model cookbook: chat, embeddings, rerank, decision, OCR

What each kind of Evren model does, which apps show which, how to get your personal API key, and tested curl and Python samples for every kind.

APIEvrenModelsDevelopersChecked against the product on

Three ways to reach your Evren key

The same request for an EV/ model, from aimo, from the VS Code extension and from an app of your own. Only the way it proves who you are changes.

aimo, the chat workspace

aimo signs each turn for you, so its shared connection still carries who you are.

  1. YouYou sign in to aimo with your company account and pick an EV/ model.Sends on your message
  2. aimoClientaimo adds its own service key and a short-lived token, signed for you.Sends on service key + your signed token
  3. GatewayThe gateway checks the signature and knows it is you. Your personal limits apply.Sends on who you are
  4. Your vaultYour Evren key is read for this one call.Sends on your Evren key
  5. EvrenEvren answers in the chat. Usage is recorded under your name.

Without an Evren key: aimo shows the reason as a chat message, with a link to My variables, and the conversation stays open.

aimo for VS Code

The extension holds a personal key of its own, minted when you sign in.

  1. YouYou sign in once with a device code.Sends on your request
  2. VS CodeClientThe extension sends your personal key, kept in VS Code's secret storage.Sends on your personal key
  3. GatewayThe gateway resolves the key to you. Your limits apply.Sends on who you are
  4. Your vaultYour Evren key is read for this one call.Sends on your Evren key
  5. EvrenEvren answers in the editor. Usage is recorded under your name.

Without an Evren key: The chat panel shows the gateway's reason and offers Select model.

Your app, with each person's own key

Each person gets a personal key once, and your app sends it with their requests.

  1. YouThe person gets a personal key once, from a sign-in.Sends on their request
  2. Your appClientYour app sends that person's key as the Bearer.Sends on their personal key
  3. GatewayThe gateway resolves the key to that person. Their limits apply.Sends on who they are
  4. Your vaultTheir Evren key is read for this one call.Sends on their Evren key
  5. EvrenEvren answers your app. Usage is recorded under their name.

Without an Evren key: HTTP 503 with the reason. Show it, and point the person to My variables.

Your app, with one team key

A team key names a team, not a person, so there is no Evren key to read.

  1. YouPeople use your app.Sends on a request
  2. Your appClientYour app sends the same team key for everyone.Sends on team key
  3. Gateway503The gateway finds no person behind the key and answers 503.
  4. Your vaultNo vault is read.Never reached
  5. EvrenEvren is never called.Never reached

What happens instead: Every EV/ call answers 503: this request carries no user identity. Models that do not need your own key still work with a team key.

What differs
aimoVS CodeAPI, personal keyAPI, team key
How you sign inCompany account, in the browserDevice code, onceDevice code, once per personNot at all
What the request carriesaimo's key + your signed tokenYour personal keyThat person's keyOne team key
Who the gateway seesYouYouThat personNo one
Evren key usedYoursYoursTheirsNone: 503

In every case you save the Evren key once, on My variables, and no client ever holds it. aimo can sign for whoever is signed in because the platform trusts it to; an outside app is not given that power, so it sends each person's own key.

The platform serves more than chat models. Some models turn text into vectors, some score documents against a question, some answer typed questions with probabilities, and some read text out of images or audio. This article explains the kinds, which apps show which, and how to call each kind from your own code. It uses the Evren models (EV/…) throughout, because they cover every kind. Every sample was run against a live installation on 10 October 2026, and the answers quoted are the ones it gave.

The model kinds

Every entry in GET /v1/models has a type. The type tells you which endpoint answers the model. A model works only on its own endpoint: a rerank model sent to the chat endpoint gets a 400 that names the right one.

Type What it does Typical use Endpoint Evren models
llm Writes text, follows instructions, calls tools Assistants, agents, summaries, code POST /v1/chat/completions EV/deepseek-v4-flash, EV/glm-5.3, EV/gemma-4-31b, EV/qwen3-vl-30b (reads images), EV/auto and others
guard Classifies a message as safe or unsafe, with categories Moderation before or after an LLM call POST /v1/chat/completions EV/qwen3-guard-4b
embedding Turns text into a vector of 4096 numbers Semantic search, retrieval, clustering, duplicates POST /v1/embeddings EV/qwen3-embedding-8b
rerank Scores how relevant each document is to a query Ordering search hits before they reach an LLM POST /v1/rerank EV/qwen3-reranker-8b
decision Answers typed questions about a text with probabilities, no prose Ticket routing, triage, yes/no checks POST /api/alpha/decisions EV/clef (text and images), EV/laya
ocr Returns the text in an image, verbatim Receipts, scanned forms, screenshots POST /v1/ocr EV/dots-ocr, EV/deepseek-ocr-2
transcription Turns speech into text Meeting notes, voice input POST /v1/audio/transcriptions EV/qwen3-asr-1.7b

Other providers use the same types; a vlm is a chat model on the on-premises pool that also reads images. Chat models also carry capabilities when the provider reports them: vision, tools and reasoning. The other kinds carry none.

How people choose a model

Where What it offers
aimo, the chat window Chat models only. The other kinds are hidden there, because a chat with them fails.
aimo for VS Code 1.1.4 and later Chat models only, in Select Model and in the default pick.
The agent workspace Chat models only in the agent editor. Its model router can use decision models to pick a model per request.
Your own code Read GET /v1/models with your key and choose by type. Your model profile may narrow the list; you only see what you may call.

Model ids carry a provider prefix. EV/ means Evren: hosted in Türkiye, run on your own Evren key, and paid from your own Evren credits. The id without the prefix also works.

curl -s $GW/v1/models -H "Authorization: Bearer $BOSPHORUS_KEY" \
  | jq -r '.data[] | select(.id | startswith("EV/")) | "\(.type)\t\(.id)"' | sort

On the day this was written it listed 16 Evren models: two decision, one embedding, one guard, eight llm, two ocr, one rerank and one transcription.

Part A: your keys, once

Evren models run on each person’s own Evren key. You never send that key in a request: you keep it on My variables, and the gateway reads it for the person the request identifies. So your code needs a personal platform key, gpk_…, that identifies you. The samples call it $BOSPHORUS_KEY. A team or service key names no person, and Evren models refuse it with a 503.

A1. Save your Evren key

  1. Create an Evren LLM key in the Evren portal. It starts with evren_llm_. A key that starts with evren_mdl_ is the wrong kind and is refused.
  2. Open My variables in the console, find Evren keys, paste the LLM key and press Save. Saving accepts Evren’s terms of use on your behalf. The Platform key (evren_plt_…) is optional and only shows your credit balance.

A2. Get your personal API key

On the same My variables page, press Create my API key on the My API key card at the top. The key appears with a Copy button; load it into your shell as BOSPHORUS_KEY. A copy is kept in your own vault, so Show key gives you the same key again later, and Replace issues a new one when you need it.

From a terminal instead

If your account cannot open the console, mint the key from a sign-in with your company account. You need curl and jq; on Windows use WSL or Git Bash. On an installation that signs in with Microsoft Entra ID:

GW=https://ai.corp.example
CFG=$(curl -s $GW/api/identity/config)
TENANT=$(echo "$CFG" | jq -r .entra.tenantId)
CLIENT=$(echo "$CFG" | jq -r .entra.clientId)
SCOPE=$(echo "$CFG" | jq -r .entra.scope)
DA=https://login.microsoftonline.com/$TENANT/oauth2/v2.0/devicecode
TE=https://login.microsoftonline.com/$TENANT/oauth2/v2.0/token
DC=$(curl -s $DA -d client_id=$CLIENT --data-urlencode "scope=$SCOPE")
echo "$DC" | jq -r .message

Open the address it prints, enter the code and sign in. Then, in the same terminal:

TOKEN=$(curl -s $TE -d grant_type=urn:ietf:params:oauth:grant-type:device_code \
  -d client_id=$CLIENT -d device_code=$(echo "$DC" | jq -r .device_code) | jq -r .access_token)
curl -s -X POST "$GW/api/identity/token?client=api" -H "Authorization: Bearer $TOKEN" | jq -r .key.key

The last command prints your key once and keeps no copy. Keep it in a password manager and load it into your shell as BOSPHORUS_KEY. An installation that signs in with its own OpenID Connect provider uses the same steps with different addresses; the API with your own keys article shows both.

Whichever way you got it, check that the key is you and that your Evren key is saved:

export GW=https://ai.corp.example
export BOSPHORUS_KEY=gpk_...
curl -s $GW/api/identity/me -H "Authorization: Bearer $BOSPHORUS_KEY" | jq -r .user.email
curl -s $GW/api/identity/evren-key -H "Authorization: Bearer $BOSPHORUS_KEY" | jq -r .llm.status

The first line prints your address and the second prints active. A null on the first line means you are using a team key.

A3. Lost the key, or need help?

Your situation What to do
You have never had one Press Create my API key on My variables. It takes a few seconds.
You had one and lost it If you created it on My variables, press Show key there. A key from the terminal is shown only when minted: run the terminal steps again and add &rotate=true to the last command, and the old key stops working while a new one is printed. Your other keys, such as the one aimo for VS Code holds, are not touched.
It may have leaked Replace it now: Replace on My variables, or &rotate=true for a terminal key. Anyone holding it can call models as you, on your Evren credits.
You only want to try one call The sign-in token, $TOKEN, works as the Bearer for about an hour, once you have minted a personal key at least once.
Sign-in is refused, or the check prints null Ask your platform administrator. They can check that your account may sign in and that you are not using a team key. They cannot create a personal key for you: it is minted only from your own sign-in, which is what makes it identify you.
You need a key for a shared service Ask the administrator for a team or service key on Teams & Keys. Such a key cannot use EV/ models, because no person stands behind it whose Evren key could be read.

Part B: the samples

The Python samples use the OpenAI SDK where it has a matching call, and requests for the three endpoints it does not know: rerank, decision and OCR. Set GW and BOSPHORUS_KEY first, as in A2.

B1. Chat

Any OpenAI SDK works with only the address and the key changed. Several Evren chat models reason before they answer and share max_tokens between reasoning and the answer, so a small max_tokens can return an empty answer. Leave it out or keep it generous.

curl -s $GW/v1/chat/completions -H "Authorization: Bearer $BOSPHORUS_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"model":"EV/deepseek-v4-flash","messages":[{"role":"user","content":"Merhaba"}]}' \
  | jq -r '.choices[0].message.content'

On one run it answered Merhaba! Size nasıl yardımcı olabilirim? The same call from Python, followed by a streamed one:

import os
from openai import OpenAI

client = OpenAI(base_url=os.environ["GW"] + "/v1", api_key=os.environ["BOSPHORUS_KEY"])

reply = client.chat.completions.create(
    model="EV/deepseek-v4-flash",
    messages=[{"role": "user", "content": "Merhaba"}],
)
print(reply.choices[0].message.content)

for chunk in client.chat.completions.create(
    model="EV/glm-5.3", stream=True,
    messages=[{"role": "user", "content": "Count to five."}],
):
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)

B2. Guard

EV/qwen3-guard-4b does not hold a conversation. Send it the message to check and it answers with a verdict and categories. Use it on user input before an agent acts on it, or on an answer before you show it.

curl -s $GW/v1/chat/completions -H "Authorization: Bearer $BOSPHORUS_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"model":"EV/qwen3-guard-4b","messages":[{"role":"user","content":"What is the weather in Izmir?"}]}' \
  | jq -r '.choices[0].message.content'

It answered Safety: Safe and Categories: None on two lines. A question about making a weapon answered Safety: Unsafe and Categories: Violent. A small parser:

def guard(client, text):
    out = client.chat.completions.create(
        model="EV/qwen3-guard-4b",
        messages=[{"role": "user", "content": text}],
    ).choices[0].message.content
    fields = dict(line.split(": ", 1) for line in out.splitlines() if ": " in line)
    safe = fields.get("Safety") == "Safe"
    cats = [] if fields.get("Categories") in (None, "None") else fields["Categories"].split(", ")
    return safe, cats

B3. Embeddings

An embedding places a text by meaning: texts about the same thing land close together. Embed your documents once, store the vectors, then embed each query and compare. Pass a list as input to embed several texts in one call.

curl -s $GW/v1/embeddings -H "Authorization: Bearer $BOSPHORUS_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"model":"EV/qwen3-embedding-8b","input":"Merhaba dünya"}' \
  | jq '.data[0].embedding | length'

It printed 4096. Comparing three texts:

import math

def embed(client, texts):
    r = client.embeddings.create(model="EV/qwen3-embedding-8b", input=texts)
    return [d.embedding for d in r.data]

def cosine(a, b):
    dot = sum(x * y for x, y in zip(a, b))
    return dot / (math.sqrt(sum(x * x for x in a)) * math.sqrt(sum(y * y for y in b)))

q, related, unrelated = embed(client, [
    "How do I reset my VPN password?",
    "Steps to change the password of your VPN account",
    "The canteen serves lentil soup on Fridays",
])
print(round(cosine(q, related), 3), round(cosine(q, unrelated), 3))

It printed 0.856 0.262: the related pair scores far higher. Keep one embedding model per index, because vectors from different models cannot be compared.

B4. Rerank

A reranker reads the query and each document together and scores relevance. It is more accurate than comparing embeddings, and slower, so use it on a short list: fetch 20 to 50 candidates with embeddings, rerank them, and give the best few to the LLM. Results come back sorted by score, and index points into the list you sent.

curl -s $GW/v1/rerank -H "Authorization: Bearer $BOSPHORUS_KEY" \
  -H 'Content-Type: application/json' -d '{
    "model": "EV/qwen3-reranker-8b",
    "query": "What is the capital of France?",
    "documents": ["Bananas are yellow.", "Paris is the capital of France.", "Berlin is in Germany."]
  }' | jq -c '.results[] | {index, relevance_score}'

It ranked index 1 first at 0.551, then index 2 at 0.002 and index 0 at 0.0004. The answer also echoes each document and reports usage.prompt_tokens. The model reads up to 4K tokens per query and document, so split long documents into passages. From Python:

import os, requests

GW, KEY = os.environ["GW"], os.environ["BOSPHORUS_KEY"]

def rerank(query, documents, top=3):
    r = requests.post(f"{GW}/v1/rerank", timeout=60,
                      headers={"Authorization": f"Bearer {KEY}"},
                      json={"model": "EV/qwen3-reranker-8b", "query": query, "documents": documents})
    r.raise_for_status()
    return [(documents[x["index"]], x["relevance_score"]) for x in r.json()["results"][:top]]

B5. Decision

A decision model reads a state, such as an e-mail, a ticket or a JSON record, and answers named, typed questions with probabilities instead of prose. The answer is fast and has a fixed shape, which suits routing and triage. A choice question takes criteria: a list of labels, or an object of label and description. The other types are score and noul (a yes/no probability); this article has tested choice. EV/clef also accepts up to four images as data URLs in an images field.

curl -s $GW/api/alpha/decisions -H "Authorization: Bearer $BOSPHORUS_KEY" \
  -H 'Content-Type: application/json' -d '{
    "model": "EV/clef",
    "state": "Hi, I want my money back for the broken kettle.",
    "questions": {
      "intent": {
        "type": "choice",
        "instructions": "What does the customer want?",
        "criteria": {"refund": "money back", "shipping": "where is my package", "other": "anything else"}
      }
    }
  }' | jq .answers

It answered "choice": "refund" with confidence 0.99, and a probability for each label. Sending options instead of criteria is refused with a choice question takes ‘criteria’ as a dict of label -> description, or a list of labels. From Python:

import os, requests

GW, KEY = os.environ["GW"], os.environ["BOSPHORUS_KEY"]

def route_ticket(text):
    r = requests.post(f"{GW}/api/alpha/decisions", timeout=60,
                      headers={"Authorization": f"Bearer {KEY}"},
                      json={
                          "model": "EV/clef",
                          "state": text,
                          "questions": {"intent": {
                              "type": "choice",
                              "instructions": "What does the customer want?",
                              "criteria": {"refund": "money back",
                                           "shipping": "where is my package",
                                           "other": "anything else"},
                          }},
                      })
    r.raise_for_status()
    a = r.json()["answers"]["intent"]
    return a["choice"], a["confidence"]

B6. OCR

Send the image as a data URL in image. The answer is the text in it, verbatim, in text. The gateway takes JSON only, so encode the file yourself. The audit trail keeps the image’s type and size, never the image.

IMG=$(base64 < invoice.png | tr -d '\n')
jq -n --arg img "data:image/png;base64,$IMG" '{model: "EV/dots-ocr", image: $img}' \
  | curl -s $GW/v1/ocr -H "Authorization: Bearer $BOSPHORUS_KEY" \
      -H 'Content-Type: application/json' -d @- | jq -r .text

For a 640 by 160 pixel image of an invoice line it returned Invoice 4711 and Total: 128.50 EUR. From Python:

import base64, mimetypes, os, requests

GW, KEY = os.environ["GW"], os.environ["BOSPHORUS_KEY"]

def ocr(path, model="EV/dots-ocr"):
    mime = mimetypes.guess_type(path)[0] or "image/png"
    data = base64.b64encode(open(path, "rb").read()).decode()
    r = requests.post(f"{GW}/v1/ocr", timeout=120,
                      headers={"Authorization": f"Bearer {KEY}"},
                      json={"model": model, "image": f"data:{mime};base64,{data}"})
    r.raise_for_status()
    return r.json()["text"]

Check the output on small images. The same text shrunk to 420 by 90 pixels in two colours came back as unrelated text, so send images at their original resolution. In our test EV/dots-ocr read the sample and EV/deepseek-ocr-2 returned an empty string for it, so start with EV/dots-ocr.

B7. Speech to text

This is the OpenAI audio API: a multipart upload with file and model. The answer is JSON by default; response_format text, srt and vtt also work. Evren returns no timestamps, so srt and vtt carry the whole clip as one cue, and verbose_json is refused.

curl -s $GW/v1/audio/transcriptions -H "Authorization: Bearer $BOSPHORUS_KEY" \
  -F model=EV/qwen3-asr-1.7b -F file=@note.wav | jq -r .text

A spoken test sentence came back as The meeting starts at 9:30 tomorrow morning. On a Mac, say -o note.aiff "…" followed by afconvert -f WAVE -d LEI16@16000 -c 1 note.aiff note.wav makes such a clip.

Part C: test every kind in one run

This script checks your setup and calls each kind once. It prints one line per check and exits with a non-zero code if anything fails, so it also works as a smoke test in a pipeline. Set OCR_IMAGE to an image with some text in it to include OCR, and AUDIO_FILE to a WAV file to include speech to text.

#!/usr/bin/env python3
import math, os, sys, base64, mimetypes, requests

GW = os.environ.get("GW", "https://ai.corp.example").rstrip("/")
KEY = os.environ.get("BOSPHORUS_KEY") or sys.exit("set BOSPHORUS_KEY to your personal key")
H = {"Authorization": f"Bearer {KEY}"}
failed = 0


def post(path, body):
    r = requests.post(GW + path, headers=H, json=body, timeout=120)
    if r.status_code != 200:
        raise RuntimeError(f"HTTP {r.status_code}: {r.text[:300]}")
    return r.json()


def check(name, fn):
    global failed
    try:
        print(f"  ok    {name}: {fn()}")
    except Exception as e:
        failed += 1
        print(f"  FAIL  {name}: {e}")


def whoami():
    me = requests.get(GW + "/api/identity/me", headers=H, timeout=30).json()
    email = (me.get("user") or {}).get("email")
    assert email, "this key names no person: mint a personal key with ?client=api"
    return email


def evren_key():
    st = requests.get(GW + "/api/identity/evren-key", headers=H, timeout=30).json()
    status = (st.get("llm") or {}).get("status")
    assert status == "active", f"Evren LLM key status is {status!r}: save it on My variables"
    return status


def listing():
    data = requests.get(GW + "/v1/models", headers=H, timeout=30).json()["data"]
    ev = {m["id"]: m.get("type") for m in data if m["id"].startswith("EV/")}
    want = {"EV/qwen3-embedding-8b": "embedding", "EV/qwen3-reranker-8b": "rerank",
            "EV/clef": "decision", "EV/dots-ocr": "ocr", "EV/qwen3-guard-4b": "guard"}
    for mid, t in want.items():
        assert ev.get(mid) == t, f"{mid} is listed as {ev.get(mid)!r}, expected {t!r}"
    return f"{len(ev)} EV/ models, types as expected"


def chat():
    j = post("/v1/chat/completions", {"model": "EV/deepseek-v4-flash",
             "messages": [{"role": "user", "content": "Reply with the single word: pong"}]})
    text = j["choices"][0]["message"]["content"] or ""
    assert "pong" in text.lower(), repr(text)
    return text.strip()


def guard():
    j = post("/v1/chat/completions", {"model": "EV/qwen3-guard-4b",
             "messages": [{"role": "user", "content": "What is the weather in Izmir?"}]})
    text = j["choices"][0]["message"]["content"]
    assert text.startswith("Safety: Safe"), repr(text)
    return text.replace("\n", " / ")


def embeddings():
    j = post("/v1/embeddings", {"model": "EV/qwen3-embedding-8b", "input": [
        "How do I reset my VPN password?",
        "Steps to change the password of your VPN account",
        "The canteen serves lentil soup on Fridays"]})
    v = [d["embedding"] for d in j["data"]]
    cos = lambda a, b: sum(x * y for x, y in zip(a, b)) / (
        math.sqrt(sum(x * x for x in a)) * math.sqrt(sum(y * y for y in b)))
    related, unrelated = cos(v[0], v[1]), cos(v[0], v[2])
    assert len(v[0]) == 4096 and related > unrelated, (len(v[0]), related, unrelated)
    return f"{len(v[0])} dims, related {related:.3f} vs unrelated {unrelated:.3f}"


def rerank():
    docs = ["Bananas are yellow.", "Paris is the capital of France.", "Berlin is in Germany."]
    j = post("/v1/rerank", {"model": "EV/qwen3-reranker-8b",
             "query": "What is the capital of France?", "documents": docs})
    top = j["results"][0]
    assert top["index"] == 1, j["results"]
    return f"top: {docs[top['index']]!r} ({top['relevance_score']:.3f})"


def decision():
    j = post("/api/alpha/decisions", {"model": "EV/clef",
             "state": "Hi, I want my money back for the broken kettle.",
             "questions": {"intent": {"type": "choice", "instructions": "What does the customer want?",
                                      "criteria": {"refund": "money back",
                                                   "shipping": "where is my package",
                                                   "other": "anything else"}}}})
    a = j["answers"]["intent"]
    assert a["choice"] == "refund", a
    return f"choice {a['choice']} ({a['confidence']:.2f})"


def ocr():
    path = os.environ["OCR_IMAGE"]
    mime = mimetypes.guess_type(path)[0] or "image/png"
    data = base64.b64encode(open(path, "rb").read()).decode()
    text = post("/v1/ocr", {"model": "EV/dots-ocr", "image": f"data:{mime};base64,{data}"})["text"]
    assert text.strip(), "no text came back"
    return text.replace("\n", " / ")[:120]


def speech():
    with open(os.environ["AUDIO_FILE"], "rb") as f:
        r = requests.post(GW + "/v1/audio/transcriptions", headers=H, timeout=120,
                          data={"model": "EV/qwen3-asr-1.7b"}, files={"file": f})
    assert r.status_code == 200, f"HTTP {r.status_code}: {r.text[:300]}"
    return r.json()["text"]


def wrong_endpoint():
    r = requests.post(GW + "/v1/chat/completions", headers=H, timeout=60,
                      json={"model": "EV/qwen3-reranker-8b",
                            "messages": [{"role": "user", "content": "hi"}]})
    assert r.status_code == 400 and "/v1/rerank" in r.text, f"HTTP {r.status_code}: {r.text[:200]}"
    return "400, names /v1/rerank"


print(f"gateway {GW}")
for name, fn in [("personal key", whoami), ("Evren key", evren_key), ("model listing", listing),
                 ("chat", chat), ("guard", guard), ("embeddings", embeddings),
                 ("rerank", rerank), ("decision", decision),
                 ("wrong endpoint is refused", wrong_endpoint)]:
    check(name, fn)
if os.environ.get("OCR_IMAGE"):
    check("ocr", ocr)
if os.environ.get("AUDIO_FILE"):
    check("speech to text", speech)
print("all passed" if not failed else f"{failed} failed")
sys.exit(1 if failed else 0)

Save it as evren_smoke.py, install requests, and run python3 evren_smoke.py. A passing run ends with all passed.

When something is not right

What you see What to do
503 “EV/…” is served with YOUR OWN Evren key and you have not registered one Save your Evren LLM key on My variables (A1).
503 … this request carries no user identity to look one up for You sent a team or service key. Use your personal key (A2).
400 “qwen3-reranker-8b” is a rerank model — use POST /v1/rerank, not /v1/chat/completions Call the endpoint the message names.
404 … does not serve /v1/rerank — it is served by Evren rerank models only Use an EV/ model of that kind.
400 field “image” is required … OCR needs "image": "data:image/png;base64,…".
400 a choice question takes ‘criteria’ … Use criteria, as a list or an object.
400 Currently do not support verbose_json … Use json, text, srt or vtt for speech to text.
An empty chat answer with finish_reason: "length" A reasoning model spent max_tokens on thinking. Raise it or leave it out.
401 invalid api key The key was revoked or mistyped, or you sent your Evren key itself. Send your gpk_… key, or mint a new one with &rotate=true (A3).
429 A rate limit. Wait for the time in the retry-after header.

Every call appears under your name on the activity page, with its endpoint.