# Cencori - Integration Contract for Code Agents This file is a strict integration guide for code agents and automation tools. Use it as the source of truth for: - package names - import paths - environment variables - base URLs - request/response shapes - stable public APIs Do not treat this file as a product roadmap. Only use the APIs and patterns documented here. ## What Cencori Is Cencori is the runtime control layer for production AI. Your application sends AI traffic to Cencori, and Cencori handles routing, security enforcement, observability, and cost tracking. ## Stable Public Surfaces Use one of these integration paths: 1. Official TypeScript SDK - Package: `cencori` - Best for: server routes, backend services, direct SDK usage 2. Vercel AI SDK provider - Import: `cencori/vercel` - Best for: `streamText()`, `generateText()`, `useChat()` 3. TanStack AI adapter - Import: `cencori/tanstack` - Best for: `@tanstack/ai` 4. OpenAI-compatible endpoint - Base URL: `https://api.cencori.com/v1` - Best for: OpenAI-compatible SDKs, agent frameworks, desktop tools 5. Native Cencori HTTP endpoints - Base URL: `https://cencori.com` - Best for: direct calls to `/api/ai/*` or `/api/v1/telemetry/web` ## Setup Decision - Existing product: preserve the app and use the Existing Product Setup Contract. - New starter app: use the Scaffold Contract with `create-cencori-app`. - Do not scaffold a new app when the user asks to integrate Cencori into an existing product. - Do not hand-wire a starter from scratch when `create-cencori-app` is available and the user wants a new app. ## Existing Product Setup Contract When the user already has an application, do not start by scaffolding a new app. Use the existing product path: 1. Create or open the Cencori project for the app/environment. 2. Pick one known-good model ID in Models or Playground. 3. Confirm the project can route to that model: - use an enabled catalog, managed, or free model first when available - otherwise add the upstream provider key in Project > Providers 4. Create a secret project key in Project > API Keys. 5. Store the key server-side as `CENCORI_API_KEY`. 6. Change the existing SDK client, provider adapter, or base URL. 7. Send one test request and verify it appears in dashboard Logs. Use `create-cencori-app` only when the user wants a new Cencori starter app. For existing products, preserve the app's current auth, database, routing, UI, and response contract. Fast first-test model IDs: ```text gpt-4o claude-sonnet-4.5 gemini-2.5-flash ``` If a request returns `Provider 'openai' is not configured`, Cencori authentication worked. The project still needs provider access for the selected model. ## Scaffold Contract Use `create-cencori-app` when the user wants a new Cencori starter app or asks to scaffold a fresh project. ### Commands ```bash npx create-cencori-app my-ai-app npx create-cencori-app my-ai-app --template nextjs npx create-cencori-app my-ai-app --template tanstack npx create-cencori-app my-ai-app --template nextjs --no-chat npx create-cencori-app my-ai-app --template nextjs --api-key csk_... npx create-cencori-app my-ai-app --template nextjs --dev ``` ### Supported Options - `--template nextjs`: Next.js App Router, Vercel AI SDK streaming, server route at `app/api/chat/route.ts`, env file `.env.local` - `--template tanstack`: Vite + React Query app with server-side Cencori calls, env file `.env` - `--no-chat`: skip the demo chat UI - `--no-install`: write files without installing dependencies - `--api-key `: pre-fill the env file and verify the key against `https://api.cencori.com/v1/models` - `--dev`: start the dev server after scaffolding and installing dependencies ### Scaffolded App Rules 1. The CLI creates `.env.local` for Next.js and `.env` for TanStack. 2. The generated env file should contain `CENCORI_API_KEY=csk_...`. 3. The generated `.env.example` should use `CENCORI_API_KEY=csk_...`. 4. The Next.js template uses `cencori/vercel` with `streamText()` and `toUIMessageStreamResponse()`. 5. The TanStack template keeps Cencori calls on the server. 6. The default first-test model is `gpt-4o`; users can switch to `claude-sonnet-4.5` or `gemini-2.5-flash` after provider access is confirmed. 7. `--api-key` verifies Cencori authentication only. Users may still need provider access for the selected model. 8. If key verification is temporarily unavailable, scaffolding may continue and the generated app reads `CENCORI_API_KEY` from the env file. 9. After scaffolding, run `npm run dev` and confirm a request appears in dashboard Logs. ## Do Not Use These As Public Contract Yet Avoid generating code against these surfaces unless the target project already implements and verifies them: - unsupported TypeScript SDK config fields like `timeout`, `retries`, `maxRetries`, `failover`, `fallbackModels`, `circuitBreaker` - undocumented SDK telemetry flags like `CENCORI_TELEMETRY=0` ## Security Rules - Use `CENCORI_API_KEY` for server-side secrets. - Project secret keys use the `csk_...` prefix. - Never expose `csk_...` keys in client-side code. - Do not use `NEXT_PUBLIC_*` env vars for secret Cencori keys. - Use `https://api.cencori.com/v1` only for OpenAI-compatible clients. - Use the SDK default base URL unless you intentionally need to override it. ## Recommended Next.js Setup Assumption: Next.js App Router with Vercel AI SDK. ### Install ```bash npm install cencori ai ``` ### Environment ```bash # .env.local CENCORI_API_KEY=csk_... ``` ### Shared Cencori Setup ```typescript // lib/cencori.ts import { Cencori } from 'cencori'; import { cencori } from 'cencori/vercel'; export const cencoriClient = new Cencori({ apiKey: process.env.CENCORI_API_KEY!, }); export { cencori }; ``` ### Streaming Chat Route ```typescript // app/api/chat/route.ts import { streamText, convertToModelMessages, type UIMessage } from 'ai'; import { cencori } from '@/lib/cencori'; export async function POST(req: Request) { const { messages, model = 'gpt-4o' }: { messages: UIMessage[]; model?: string } = await req.json(); const result = streamText({ model: cencori(model), messages: await convertToModelMessages(messages), }); return result.toUIMessageStreamResponse(); } ``` ### Client Chat UI ```tsx // app/page.tsx 'use client'; import { useChat } from '@ai-sdk/react'; import { DefaultChatTransport } from 'ai'; import { useState, type FormEvent } from 'react'; function getMessageText(message: { parts?: Array<{ type: string; text?: string }> }) { return message.parts ?.map((part) => (part.type === 'text' ? part.text || '' : '')) .join('') || ''; } export default function Chat() { const [input, setInput] = useState(''); const { messages, sendMessage, status } = useChat({ transport: new DefaultChatTransport({ api: '/api/chat' }), }); function handleSubmit(event: FormEvent) { event.preventDefault(); if (!input.trim() || status !== 'ready') return; const text = input.trim(); setInput(''); void sendMessage({ text }); } return (
{messages.map((message) => (
{getMessageText(message)}
))}
setInput(event.target.value)} />
); } ``` ### Optional Web Telemetry ```typescript // proxy.ts import type { NextRequest } from 'next/server'; import { NextResponse } from 'next/server'; import { cencoriClient } from '@/lib/cencori'; export async function middleware(request: NextRequest) { const startedAt = Date.now(); const response = NextResponse.next(); void cencoriClient.telemetry.reportWebRequest({ host: request.headers.get('host') || 'unknown', method: request.method, path: request.nextUrl.pathname, queryString: request.nextUrl.search ? request.nextUrl.search.slice(1) : undefined, statusCode: response.status, userAgent: request.headers.get('user-agent') || undefined, referer: request.headers.get('referer') || undefined, latencyMs: Date.now() - startedAt, }); return response; } ``` ## Official TypeScript SDK ### Install ```bash npm install cencori ``` ### Initialize ```typescript import { Cencori } from 'cencori'; const cencori = new Cencori({ apiKey: process.env.CENCORI_API_KEY, }); ``` ### Supported Client Configuration The TypeScript SDK currently supports only: - `apiKey` - `baseUrl` - `headers` Example: ```typescript const cencori = new Cencori({ apiKey: process.env.CENCORI_API_KEY, baseUrl: 'https://cencori.com', headers: { 'X-Trace-ID': 'req_123', }, }); ``` Do not generate SDK code using extra config fields that are not listed above. ## Core SDK Methods ### Chat ```typescript const response = await cencori.ai.chat({ model: 'gpt-4o', messages: [ { role: 'system', content: 'You are a helpful assistant.' }, { role: 'user', content: 'Hello!' }, ], temperature: 0.2, maxTokens: 300, }); console.log(response.content); console.log(response.toolCalls); console.log(response.usage.totalTokens); ``` ### Chat Streaming ```typescript const stream = cencori.ai.chatStream({ model: 'gpt-4o', messages: [{ role: 'user', content: 'Tell me a story.' }], }); for await (const chunk of stream) { process.stdout.write(chunk.delta); } ``` ### Structured Output ```typescript const response = await cencori.ai.generateObject({ model: 'gpt-4o', prompt: 'Generate a fictional user profile.', schema: { type: 'object', properties: { name: { type: 'string' }, age: { type: 'number' }, }, required: ['name', 'age'], }, }); console.log(response.object); ``` ### Embeddings ```typescript const response = await cencori.ai.embeddings({ model: 'text-embedding-3-small', input: 'Hello world', }); console.log(response.embeddings[0]); ``` ### Image Generation ```typescript const response = await cencori.ai.generateImage({ prompt: 'A futuristic city at sunset', model: 'gpt-image-1.5', size: '1024x1024', }); console.log(response.images[0].url); ``` ### Web Telemetry ```typescript await cencori.telemetry.reportWebRequest({ host: 'app.example.com', method: 'GET', path: '/api/chat', statusCode: 200, latencyMs: 42, }); ``` ## SDK Chat Response Shape `cencori.ai.chat()` returns a TypeScript SDK response with camelCase usage fields: ```json { "id": "chatcmpl_123", "model": "gpt-4o", "content": "Hello! How can I help?", "toolCalls": null, "finishReason": "stop", "usage": { "promptTokens": 13, "completionTokens": 7, "totalTokens": 20 } } ``` ## Vercel AI SDK ### Install ```bash npm install cencori ai ``` ### Default Provider ```typescript import { cencori } from 'cencori/vercel'; import { generateText } from 'ai'; const result = await generateText({ model: cencori('gpt-4o'), prompt: 'Write a haiku about AI infrastructure.', }); console.log(result.text); ``` ### Custom Provider ```typescript import { createCencori } from 'cencori/vercel'; export const cencori = createCencori({ apiKey: process.env.CENCORI_API_KEY!, }); ``` Preferred import path for Vercel AI SDK is `cencori/vercel`. Do not prefer the root-package re-export in generated examples. ## TanStack AI ### Install ```bash npm install cencori @tanstack/ai ``` ### Default Adapter ```typescript import { chat } from '@tanstack/ai'; import { cencori } from 'cencori/tanstack'; for await (const chunk of chat({ adapter: cencori('gpt-4o'), messages: [{ role: 'user', content: 'Hello world' }], })) { if (chunk.type === 'content') { console.log(chunk.delta); } } ``` ### Custom Adapter Factory ```typescript import { createCencori } from 'cencori/tanstack'; const provider = createCencori({ apiKey: process.env.CENCORI_API_KEY!, }); const adapter = provider('gpt-4o'); ``` ## OpenAI-Compatible Clients Use this mode when a tool already expects an OpenAI-compatible client. ### Required Settings - `api_key`: your Cencori project key (`csk_...`) - `base_url`: `https://api.cencori.com/v1` ### Python ```python from openai import OpenAI client = OpenAI( api_key="your_cencori_api_key", base_url="https://api.cencori.com/v1" ) response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) ``` ### Node.js ```typescript import OpenAI from 'openai'; const client = new OpenAI({ apiKey: process.env.CENCORI_API_KEY, baseURL: 'https://api.cencori.com/v1', }); const response = await client.chat.completions.create({ model: 'gpt-4o', messages: [{ role: 'user', content: 'Hello!' }], }); ``` ### Agent Frameworks And Desktop Tools For OpenAI-compatible frameworks and tools, set: ```bash OPENAI_BASE_URL=https://api.cencori.com/v1 OPENAI_API_BASE=https://api.cencori.com/v1 OPENAI_API_KEY=$CENCORI_API_KEY ``` This applies to tools such as: - Continue - CrewAI - LangChain `ChatOpenAI` - AutoGen - other OpenAI-compatible agent runtimes ## Native Cencori HTTP Endpoints Base origin: - `https://cencori.com` Common endpoints: - `POST /api/ai/chat` - `POST /api/ai/embeddings` - `POST /api/ai/images/generate` - `POST /api/v1/telemetry/web` ### Native Chat Example ```bash curl https://cencori.com/api/ai/chat \ -H "CENCORI_API_KEY: csk_..." \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "messages": [{"role": "user", "content": "Hello!"}], "stream": false }' ``` ### Native Chat Response The native chat endpoint includes an OpenAI-compatible `choices[0].message` shape and Cencori convenience fields such as `content`, `toolCalls`, `cost_usd`, and `finish_reason`. ## OpenAI-Compatible HTTP Endpoint Base origin: - `https://api.cencori.com/v1` ### Chat Completions Example ```bash curl https://api.cencori.com/v1/chat/completions \ -H "Authorization: Bearer csk_..." \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "messages": [{"role": "user", "content": "Hello!"}] }' ``` ### OpenAI-Compatible Response Shape ```json { "id": "chatcmpl-abc123", "object": "chat.completion", "created": 1677652288, "model": "gpt-4o", "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 } } ``` ### Responses API (OpenAI-Compatible) The Responses API supports built-in tools (web search, file search, code interpreter) in addition to standard function calling. ```bash curl https://api.cencori.com/v1/responses \ -H "Authorization: Bearer csk_..." \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "input": "What is the latest news about AI?", "tools": [ { "type": "web_search_preview", "search_context_size": "medium" } ], "temperature": 0.5 }' ``` ### Responses API Request Fields - `model` (required): Model ID string - `input` (required): String or array of input items (message, function_call, function_call_output, file) - `instructions`: System instructions for the model - `tools`: Array of tools — supports `web_search_preview`, `file_search`, `code_interpreter`, and standard `function` definitions - `tool_choice`: `"auto"` | `"none"` | `"required"` | `{ type: "function", name: "..." }` - `temperature`: Sampling temperature (0–2) - `max_output_tokens`: Maximum tokens in the response - `top_p`: Nucleus sampling - `stream`: Enable SSE streaming - `user`: End-user ID for billing - `previous_response_id`: Chain responses for multi-turn conversations - `response_format`: Control output — `{ type: "text" }`, `{ type: "json_object" }`, or `{ type: "json_schema", json_schema: { name, schema } }` ### Responses API Response Shape ```json { "id": "resp_abc123", "object": "response", "created": 1728678400, "model": "gpt-4o", "output": [ { "id": "msg_xyz", "type": "message", "role": "assistant", "status": "completed", "content": [ { "type": "output_text", "text": "According to [1] the latest AI news...", "annotations": [ { "type": "url_citation", "start_index": 13, "end_index": 16, "url": "https://example.com/article", "title": "Article Title" } ] } ] }, { "id": "ws_abc", "type": "web_search_call", "status": "completed", "output": { "query": "latest news about AI", "results": [...] } } ], "usage": { "input_tokens": 50, "output_tokens": 150, "total_tokens": 200 }, "status": "completed" } ``` Annotations (`url_citation`) are attached to `output_text` items when the model cites web search results. Each annotation has `start_index`, `end_index`, `url`, and `title`. ### File Input Items Upload files inline for file_search indexing: ```json { "type": "file", "filename": "doc.txt", "content": "file text here", "mime_type": "text/plain" } ``` Content is chunked and stored in `scan_chat_memory` with source prefix `file:`. ### Structured Output Use `response_format` to enforce JSON output: ```json { "response_format": { "type": "json_schema", "json_schema": { "name": "event", "schema": { "type": "object", "properties": { "date": { "type": "string" }, "location": { "type": "string" } }, "required": ["date", "location"], "additionalProperties": false } } } } ``` When `type` is `json_schema`, the model is forced to call a hidden function tool matching the schema, and the arguments become the response text. ### Built-in Tools - **`web_search_preview`**: Automatically searches the web and injects results as context. Supports `search_context_size` (`low`/`medium`/`high`). - **`file_search`**: Searches your Cencori project's memory/vector store. Supports `max_num_results` and `filters`. - **`code_interpreter`**: Executes code blocks generated by the model (Python/JavaScript). Runs in a sandboxed environment. ### SDK Usage ```typescript // Non-streaming const response = await cencori.ai.responses({ model: 'gpt-4o', input: 'Search the web for AI news and summarize.', tools: [{ type: 'web_search_preview', search_context_size: 'high' }], }); // Streaming (SSE) const stream = cencori.ai.responsesStream({ model: 'gpt-4o', input: 'Tell me about AI.', }); for await (const event of stream) { if (event.type === 'response.output_text.delta') { process.stdout.write(event.data.delta as string); } } ``` ### Python SDK Usage ```python from openai import OpenAI client = OpenAI( api_key="your_cencori_api_key", base_url="https://api.cencori.com/v1" ) response = client.responses.create( model="gpt-4o", input="What is the weather?", tools=[{"type": "web_search_preview"}] ) print(response.output[0].content[0].text) ``` Note: The `/v1/responses` endpoint requires `Authorization: Bearer ` header. ## Authentication Rules ### Use `CENCORI_API_KEY` Header For - `https://cencori.com/api/ai/*` - `https://cencori.com/api/v1/telemetry/web` ### Use `Authorization: Bearer ...` For - `https://api.cencori.com/v1/*` ## Model Selection One Cencori project key can be used across many models. Choose the model per request: ```typescript await cencori.ai.chat({ model: 'gpt-4o', messages }); await cencori.ai.chat({ model: 'claude-sonnet-4.5', messages }); await cencori.ai.chat({ model: 'gemini-2.5-flash', messages }); ``` ## Decision Rules For Code Agents When integrating Cencori into a project: 1. Prefer the smallest working integration. 2. Reuse existing routes, auth, and env patterns. 3. Keep `CENCORI_API_KEY` on the server. 4. Prefer `cencori/vercel` when the project already uses Vercel AI SDK. 5. Prefer the OpenAI-compatible base URL only when a framework expects it. 6. Preserve the app's existing response contract when replacing another provider. 7. Do not assume undocumented routing, failover, or evaluation settings are user-configurable. 8. For existing products, complete the dashboard-to-code checklist before changing application code. 9. For new apps, prefer `create-cencori-app` over manually assembling a starter. ## Documentation Links - Docs: https://cencori.com/docs - Add Cencori to an Existing Product: https://cencori.com/docs/getting-started/existing-product - Quick Start: https://cencori.com/docs/quick-start - create-cencori-app: https://www.npmjs.com/package/create-cencori-app - Vercel AI SDK: https://cencori.com/docs/integrations/vercel-ai-sdk - TanStack AI: https://cencori.com/docs/integrations/tanstack - Authentication: https://cencori.com/docs/api/authentication - Chat API: https://cencori.com/docs/api/chat - Continue: https://cencori.com/docs/agentic-engineering/desktop/continue