Dashboard|

Chat API

Reference for Cencori chat completions across the official SDK and OpenAI-compatible HTTP endpoints.

Overview

Cencori exposes chat completions through two public HTTP surfaces:

EndpointPathBest For
Cencori Nativehttps://cencori.com/api/ai/chatOfficial SDKs and direct Cencori calls
OpenAI-Compatiblehttps://api.cencori.com/v1/chat/completionsExisting OpenAI-compatible clients and agent frameworks

Both surfaces authenticate with your Cencori project key and log requests to the same project dashboard.

When to Use Which Endpoint

Choose based on your use case:

Use CaseRecommended Endpoint
Using the Cencori SDK (cencori)Native endpoint. The SDK uses it automatically.
Building a new Next.js or Node routeNative endpoint or SDK.
Migrating from OpenAI directlyOpenAI-compatible endpoint.
Using agent tools that ask for an OpenAI base URLOpenAI-compatible endpoint with base URL https://api.cencori.com/v1.
Debugging whether Cencori auth and provider routing workNative endpoint with curl.

The OpenAI-compatible base URL should usually stop at /v1; most clients append /chat/completions themselves.

Official TypeScript SDK

import { Cencori } from 'cencori';
 
const cencori = new Cencori({
  apiKey: process.env.CENCORI_API_KEY,
});
 
const response = await cencori.ai.chat({
  model: 'gpt-6-sol',
  messages: [
    { role: 'system', content: 'You are a helpful assistant.' },
    { role: 'user', content: 'What is the capital of France?' },
  ],
  temperature: 0.2,
  maxTokens: 300,
});
 
console.log(response.content);
console.log(response.toolCalls);
console.log(response.usage.totalTokens);

SDK Response Shape

{
  "id": "chatcmpl_123",
  "model": "gpt-6-sol",
  "content": "The capital of France is Paris.",
  "toolCalls": null,
  "finishReason": "stop",
  "usage": {
    "promptTokens": 13,
    "completionTokens": 7,
    "totalTokens": 20
  }
}

Native Cencori HTTP Endpoint

Use the native endpoint when you want direct access to /api/ai/chat:

curl https://cencori.com/api/ai/chat \
  -H "CENCORI_API_KEY: csk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-6-sol",
    "messages": [{"role": "user", "content": "Hello!"}],
    "stream": false
  }'

This endpoint returns the OpenAI-compatible choices[0].message shape and also includes Cencori convenience fields such as content, toolCalls, and cost_usd.

OpenAI-Compatible Endpoint

Use this when a client or framework already expects the OpenAI Chat Completions API:

curl https://api.cencori.com/v1/chat/completions \
  -H "Authorization: Bearer csk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-6-sol",
    "messages": [{"role": "user", "content": "Hello!"}]
  }'

When configuring an SDK, set the base URL to https://api.cencori.com/v1, not the full /chat/completions URL.

Request Parameters

FieldTypeRequiredNotes
modelstringYesAny model routed through Cencori
messagesarrayYesConversation history
temperaturenumberNoSampling temperature
maxTokensnumberNoMax output tokens
streambooleanNoStream the response
toolsarrayNoFunction/tool definitions
toolChoicestring or objectNoTool selection mode
userId or userstringNoEnd-user identifier for attribution and Monetization
timeout_msnumberNoPer-request provider timeout in ms (1–300000, capped). Bounds one provider attempt
max_cost_usdnumberNoPer-request cost budget in USD. Unary calls fail before returning; streams surface it at final tally as HTTP 402 budget_exceeded
reasoning_effortstringNonone, minimal, low, medium, high, xhigh or max, on reasoning models. /v1/chat/completions only
knowledge_bases (knowledgeBases in the SDK)string[]NoUp to 3 knowledge base ids to ground the answer in, with citations. See Knowledge bases

OpenAI-Compatible Response Shape

{
  "id": "chatcmpl-abc123",
  "object": "chat.completion",
  "created": 1677652288,
  "model": "gpt-6-sol",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "The capital of France is Paris."
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 13,
    "completion_tokens": 7,
    "total_tokens": 20
  }
}

Knowledge bases (RAG)

Name up to three knowledge bases and Cencori searches them with the user's latest message before calling the model. The best-matching passages are added to the prompt, the model is told to cite them with [1]-style markers, and the response says which passage each marker points to.

With the SDK (cencori 1.9.0 or later), create a base, upload a file, and ask:

const kb = await cencori.knowledge.createBase({ name: 'Support docs' });
 
const pdf = new Blob([await fs.readFile('refund-policy.pdf')], { type: 'application/pdf' });
await cencori.knowledge.addFileSource(kb.id, pdf, { filename: 'refund-policy.pdf' });
 
const response = await cencori.ai.chat({
  model: 'gpt-6-sol',
  messages: [{ role: 'user', content: 'How long do refunds take?' }],
  knowledgeBases: [kb.id],
});
 
console.log(response.content);             // "Refunds are processed within 5 business days [1]."
console.log(response.knowledge?.citations); // [{ n: 1, sourceId: 'src_...', page: 2, score: 0.91, ... }]

Uploads take PDF, DOCX, Markdown, HTML, CSV, JSON or plain text, up to 8MB a file, or raw text with addInlineSource(kb.id, text). Indexing runs after the upload returns: a source is usable once getSource() reports status: 'ready'. Uploads that look like prompt injection are held as stale for review instead of indexed.

Over HTTP:

curl https://api.cencori.com/v1/chat/completions \
  -H "Authorization: Bearer $CENCORI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-6-sol",
    "messages": [{ "role": "user", "content": "How long do refunds take?" }],
    "knowledge_bases": ["kb_8f2c..."]
  }'

The response carries a knowledge object next to choices:

{
  "choices": [{ "message": { "role": "assistant", "content": "Refunds are processed within 5 business days [1]." } }],
  "knowledge": {
    "knowledge_bases": ["kb_8f2c..."],
    "citations": [
      { "n": 1, "chunk_id": "4b1e...", "source_id": "src_91ad...", "ord": 3, "page": 2, "score": 0.91 }
    ]
  }
}

When streaming, the same citations arrive as JSON in the X-Cencori-Knowledge-Citations response header, before the first token. With cencori.ai.chatStream(), the first chunk carries them as chunk.citations.

  • Up to 5 passages are used per request, the best matches across all named bases.
  • Requires a secret project key. Publishable (browser) keys get 403 secret_key_required.
  • Only project-wide knowledge bases can be used here. Tenant-scoped bases return 403 tenant_scope_mismatch; query those through a session on their agent installation.
  • An unknown id returns 404 knowledge_base_not_found.
  • Retrieval fails open: if search is unavailable, the request still runs, just without passages, and citations is empty.
  • The query embedding is billed like any embedding call. Grounded requests skip the prompt cache, so answers reflect the current documents.

Streaming

SDK Streaming

const stream = cencori.ai.chatStream({
  model: 'gpt-6-sol',
  messages: [{ role: 'user', content: 'Tell me a story.' }],
});
 
for await (const chunk of stream) {
  process.stdout.write(chunk.delta);
}

HTTP Streaming

curl -N https://api.cencori.com/v1/chat/completions \
  -H "Authorization: Bearer csk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-6-sol",
    "messages": [{"role": "user", "content": "Tell me a story."}],
    "stream": true
  }'

Tool Calling

const response = await cencori.ai.chat({
  model: 'gpt-6-sol',
  messages: [{ role: 'user', content: 'What is the weather in Tokyo?' }],
  tools: [
    {
      type: 'function',
      function: {
        name: 'get_weather',
        description: 'Get weather for a location',
        parameters: {
          type: 'object',
          properties: {
            location: { type: 'string' },
          },
          required: ['location'],
        },
      },
    },
  ],
});
 
console.log(response.toolCalls);

Error Handling

The SDK throws typed errors — never a bare message. Every failure carries the HTTP status, the machine-readable code, the requestId for support correlation, and any retry hint the server sent. Transient failures (rate limits, 5xx, network blips) retry automatically with backoff, honoring the server's Retry-After; 4xx fail fast — except 429s, which retry then surface as RateLimitError with the hint intact.

import { CencoriError, RateLimitError } from 'cencori';
 
try {
  await cencori.ai.chat({
    model: 'gpt-6-sol',
    messages: [{ role: 'user', content: 'Hello!' }],
  });
} catch (error) {
  if (error instanceof RateLimitError) {
    console.log('back off for', error.retryAfterMs, 'ms');
  } else if (error instanceof CencoriError) {
    // error.statusCode, error.code, error.requestId, error.isRetryable
    console.log(error.code, error.requestId);
  }
}

Safety classification

Every chat response carries what the input guards verdicts — so a 200 whose model resisted an attack is distinguishable from a request that was never scanned:

{
  "choices": [ ... ],
  "safety": {
    "scanned": true,
    "input": { "safe": false, "layer": "jailbreak", "riskScore": 0.86, "reasons": ["..."] }
  }
}

Streams carry the same verdict as headers (X-Cencori-Safety-Scanned, X-Cencori-Safety-Input, X-Cencori-Safety-Score). scanned: false means the request ran the passthrough lane (your own safety layers apply). Only category-level reasons are exposed — never matched excerpts.

Auto-router (auto / cencori-auto)

Pass model: 'auto' to route by task across the project's active BYOK keys only — image content → vision model, code/tool calls → code-capable model, analytical prompts → reasoning model, otherwise a cheap fast model. No usable BYOK key returns 402 byok_required; it never falls back to managed keys and works at a zero credit balance. cencori-auto forces the BYOK router when a Tensor plan mapping would otherwise own bare auto. Details: BYOK auto-router.