Skip to content

Consuming the API

Once a project has an endpoint and a key, your applications talk to it through the OpenAI-compatible chat completions API. This is the only protocol customer applications use — if your code already talks to OpenAI, it already talks to Adapta.

The three things you need

Thing Where it comes from Used as
Base URL Your server, e.g. http://your-server:8000/v1 OpenAI client base_url
Endpoint slug The Endpoint tab in the console the model field
Scoped key (adp_…) The Keys tab (shown once) the api_key

Python (OpenAI SDK)

from openai import OpenAI

client = OpenAI(
    base_url="http://your-server:8000/v1",
    api_key="adp_xxxx…",            # scoped to one project endpoint
)

resp = client.chat.completions.create(
    model="support-kb-a1b2c3d4",    # your endpoint slug (from the console)
    messages=[{"role": "user", "content": "What is our refund window?"}],
)
print(resp.choices[0].message.content)

curl

curl http://your-server:8000/v1/chat/completions \
  -H "Authorization: Bearer adp_xxxx…" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "support-kb-a1b2c3d4",
    "messages": [{"role": "user", "content": "What is our refund window?"}]
  }'

What's different from vanilla OpenAI

  • The key picks the endpoint, not the model field. Each adp_ key is bound to exactly one endpoint. The model slug you pass must match the key's endpoint — a mismatched slug is rejected (403). This means a key can never reach an endpoint it wasn't issued for.
  • Answers carry citations when the project has documents. If the project has indexed documents (a Knowledge project, or a finetune project that also uploaded documents), the response is grounded in them and includes the source chunks it used. Each citation is {index, source, score, score_kind, text, file_id, page, ocr, confidence}: text is the cited passage, page is the 1-based page (null for formats without pages), and for scanned documents ocr is true and confidence is the mean OCR confidence in 0–1 (null otherwise). The index matches the [N] markers in the answer.
  • Follow-up questions are understood. On a project with documents, once the conversation has two or more user turns the server first asks the model to rewrite your last question as a standalone one ("and how many days for that?" becomes "How many vacation days do employees get?"), then retrieves on that rewrite and your original text. It costs one short extra generation (at most 40 tokens) only on those follow-up turns; first questions are unaffected. Those tokens are counted in usage. If the rewrite fails or times out, the server quietly retrieves on your last message alone. Send "adapta": {"condense": false} to turn it off for a request.
  • Usage is metered. Every response includes prompt_tokens, completion_tokens, and total_tokens, and is rolled up per day (visible in the console's Usage tab).
  • Finetune endpoints serve base + adapter. A finetune project's endpoint applies its LoRA adapter on top of the base model transparently — the request shape is identical. (Creating one requires an imported adapter that has passed the CPU eval gate.) If that project also indexed documents, retrieval and the adapter compose in the same call (see Knowledge + behavior together).

Images (vision endpoints)

An endpoint whose project was created on a vision base model accepts the standard OpenAI image content-parts — the image inline as a base64 data: URL:

import base64

with open("invoice.png", "rb") as f:
    data_url = "data:image/png;base64," + base64.b64encode(f.read()).decode()

resp = client.chat.completions.create(
    model="invoice-reader-a1b2c3d4",
    messages=[{"role": "user", "content": [
        {"type": "image_url", "image_url": {"url": data_url}},
        {"type": "text", "text": "Extract vendor, date and total as JSON."},
    ]}],
)

Rules (v1, all violations come back as typed 4xx errors, never a crash):

  • Inline data: URLs only — the server never fetches remote image URLs.
  • png, jpeg or webp; ≤ 10 MB and ≤ 8192 px per image; ≤ 4 images per request.
  • Requests with images are non-streaming (stream must be false) and skip document retrieval — text-only requests on the same endpoint still answer from indexed documents with citations.
  • Sending an image to a text endpoint returns a clear 400 — create the project on a vision base instead.

JSON mode (response_format)

Any endpoint can be forced to emit valid JSON, OpenAI-style. The output is constrained at decode time (a llama.cpp grammar), so it parses every time — no adapter needed for the shape:

resp = client.chat.completions.create(
    model="your-endpoint-slug",
    messages=[{"role": "user", "content": "Extract name and total from: ACME invoice, $42.10"}],
    response_format={"type": "json_object"},
)

{"type": "json_schema", "json_schema": {"name": "...", "schema": {...}}} constrains the output to that schema; an invalid schema is a 422. Works with streaming. {"type": "text"} (the default) is unconstrained.

Streaming

Set "stream": true (or stream=True in the SDK) to receive tokens as server-sent events, the same as the OpenAI streaming protocol. The final chunk carries the usage totals, and — when the project has indexed documents — citations in the same shape as the non-streaming response (each entry's score_kind says whether score came from the reranker or from vector similarity alone). (Not available for requests carrying images — see above.)

Errors

Errors come back in a consistent envelope with a correlation_id:

{ "error": { "code": "rate_limited", "message": "…", "correlation_id": "…" } }

If you need to report a problem, the correlation_id lets an operator find the exact request in the server logs. No internal details (stack traces, library errors) are ever exposed in the response.


The full request/response schema for every endpoint is in the API reference, generated from the live OpenAPI spec.