Developer & API

Build with Kendr using one scoped API key.

Sign in, fund the Kendr wallet, create one key, and call frontier models through AWS Bedrock or native provider routes without distributing those provider credentials to users or applications. API requests use the same balance as the Kendr plan, so there is no separate API subscription.

Base URL: https://kendr.org OpenAI and Anthropic compatible Works with your harness Streaming and web search No separate API fee

Connect a harness

Kendr speaks the OpenAI and Anthropic wire formats, so most agents, IDEs, and SDKs connect by pointing their base URL at https://kendr.org and pasting one scoped key. Use model: "auto" to let Intelligent Auto pick the best route per request — harness-supplied model names outside the kc-* catalog are auto-routed the same way. Paste the key you saved at creation in place of <YOUR_KEY> below — full keys are never shown again.

Claude Code — point the Anthropic base URL at Kendr

export ANTHROPIC_BASE_URL=https://kendr.org
export ANTHROPIC_AUTH_TOKEN=<YOUR_KEY>
claude
Scope note

Requests stream over SSE in each vendor's wire format, and /v1/messages/count_tokens is available for Anthropic-style token counting. Tools you declare execute on Kendr's servers during generation; harness-local tool execution (for example Claude Code editing files on your machine) is not yet supported, so coding agents work best today for chat, review, and planning workflows.

Frontier models through Kendr Cloud

The model gateway accepts Authorization: Bearer kndr_live_... and exposes the model aliases currently available to your Kendr account. Existing desktop clients can keep using /api/v1/llm/*; new integrations can use the standard /v1/* routes.

RoutePurpose
GET /v1/modelsList currently available Kendr model aliases.
POST /v1/responsesGenerate with the OpenAI Responses shape, including streaming, tools, and optional web search.
POST /v1/chat/completionsUse OpenAI-compatible chat clients, SSE chunks, and optional web search.
POST /v1/messagesUse the Anthropic Messages request and response shape, including optional streaming.
POST /v1/messages/count_tokensFree Anthropic-compatible token estimate for a message request.
POST /v1/video/analysesQueue an account-scoped asynchronous video analysis.
GET /v1/video/analyses/{id}Poll video analysis state and result.
GET /api/v1/openapi.jsonDownload the model-only OpenAPI contract.
GET|POST /api/me/ai/preferencesRead or save default and mode-specific aliases after Kendr login.
curl https://kendr.org/v1/chat/completions \
  -H "Authorization: Bearer kndr_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: my-request-001" \
  -d '{
    "model": "kc-intelligent",
    "web_search": true,
    "messages": [{"role": "user", "content": "Summarize this decision."}]
  }'

Text model aliases can stream responses and can use web search when the route and account configuration support it. Omit web_search to let Kendr decide from the current request, set it to true to request current context, or set it to false to prohibit search. Idempotency-Key is optional on /v1/* requests — one is generated per request when absent, and supplying your own stable key makes retries replay the settled result instead of charging again. Successful responses include the final Kendr usage record.

Use Optimized mode through the API

Add an optimization policy to any Normal model or kc-intelligent request. For most production integrations, use mode: "balanced", engine: "auto", and allow_lossy_context: true. This lets Kendr reduce eligible earlier context while protecting system instructions, the latest user request, code, commands, diffs, URLs, schemas, identifiers, numbers, and exact artifacts.

Important

allow_lossy_context: true is required for semantic input compression. If it is false, Kendr is limited to lossless cleanup, reversible reference encoding, and concise-output guidance, so the request may show a smaller measurable input-cost saving.

Recommended curl request

curl https://kendr.org/v1/responses \
  -H "Authorization: Bearer $KENDR_API_KEY" \
  -H "Idempotency-Key: optimizer-request-001" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "kc-intelligent",
    "messages": [
      {"role": "user", "content": "Earlier detailed context..."},
      {"role": "assistant", "content": "Earlier answer..."},
      {"role": "user", "content": "Review the implementation and propose a safe patch."}
    ],
    "optimization": {
      "profile": "code",
      "mode": "balanced",
      "engine": "auto",
      "allow_lossy_context": true
    }
  }'

JavaScript

const response = await fetch("https://kendr.org/v1/responses", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.KENDR_API_KEY}`,
    "Idempotency-Key": crypto.randomUUID(),
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    model: "kc-intelligent",
    input: "Summarize the relevant decisions in this long project context.",
    optimization: {
      profile: "general",
      mode: "balanced",
      engine: "auto",
      allow_lossy_context: true
    }
  })
});

if (!response.ok) throw new Error(`Kendr request failed: ${response.status}`);
const result = await response.json();
console.log(result.output_text);
console.log(result.kendr_usage.credits_charged_micros);
console.log(result.kendr_optimization?.estimated_credits_saved_micros ?? 0);

Python

import os
import uuid
import requests

response = requests.post(
    "https://kendr.org/v1/responses",
    headers={
        "Authorization": f"Bearer {os.environ['KENDR_API_KEY']}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "model": "kc-intelligent",
        "input": "Summarize the relevant decisions in this long project context.",
        "optimization": {
            "profile": "research",
            "mode": "balanced",
            "engine": "auto",
            "allow_lossy_context": True,
        },
    },
    timeout=180,
)
response.raise_for_status()
result = response.json()
print(result["output_text"])
print(result.get("kendr_optimization", {}))

Choose a mode

ModeBehaviorUse it when
offSends the request without optimizer transformations.You need an explicit optimizer-off baseline.
balancedLossless cleanup, reversible reference encoding, and guarded compression of eligible earlier context.Default production choice.
aggressiveTargets a smaller retained context and denser output while preserving protected spans.Very long, lower-risk context where cost reduction is the priority.

Legacy auto and safe values are still accepted and treated as balanced; without allow_lossy_context the balanced mode applies only lossless and reversible strategies, which matches the old safe behavior.

Profiles are general, code, and research. Use engine: "auto" unless you are testing a specific engine. extractive is the built-in relevance-based compressor and deterministic never performs semantic compression. Additional engine values are reserved for server-controlled experiments and always fall back safely.

Read the optimization receipt

{
  "kendr_usage": {
    "input_tokens": 1200,
    "output_tokens": 180,
    "credits_charged_micros": 180000
  },
  "kendr_optimization": {
    "enabled": true,
    "requested_mode": "balanced",
    "resolved_mode": "balanced",
    "engine": "extractive",
    "original_input_tokens_estimated": 1800,
    "optimized_input_tokens_estimated": 1200,
    "estimated_input_tokens_avoided": 600,
    "estimated_credits_saved_micros": 40000,
    "estimated_credits_without_optimization_micros": 220000,
    "quality_guard": {"passed": true, "checks": ["latest_user_request_preserved"], "violations": []},
    "shadow": false
  }
}
  • kendr_usage.credits_charged_micros is the authoritative settled charge. Divide microcredits by 1,000,000 to display Kendr credits.
  • estimated_credits_saved_micros is the rate-card-priced counterfactual saving from estimated avoided input tokens. Do not subtract it again; the settled charge already uses the optimized provider request.
  • estimated_credits_without_optimization_micros is the comparable estimated charge without that input reduction.
  • shadow: true means the deployment is running a preview-only evaluation. The provider received the original request, so no saving was applied.
  • A zero saving is valid for short requests, protected content, structured-output/tool contracts, rejected quality checks, or unavailable semantic engines. Check fallback_reason, skipped_strategies, and quality_guard.violations.

Streaming

Send the same optimization object with stream: true. The final done event contains settled usage and the complete kendr_optimization receipt; token deltas do not contain billing information. Use /api/v1/llm/responses when you need Kendr-native named SSE events.

Model request fields

Use the same fields across /v1/responses, /v1/chat/completions, /v1/messages, and /api/v1/llm/responses where the compatibility format allows them.

FieldRequiredUse
modelYesKendr alias such as kc-intelligent or a Normal text model alias from GET /v1/models.
input or messagesYesPrompt text or role-based chat messages, depending on the route format.
instructionsNoSystem-level guidance for Responses-compatible requests.
max_output_tokens or max_tokensNoCaps generated tokens for the selected route.
streamNoSet true to receive server-sent events.
web_searchNoTri-state control: omit for automatic selection from the current request, use true to request search, or false to prohibit it.
toolsNoTool declarations executed on the server side. Use {"type": "kendr_mcp", "server_id": "mcp_..."} to activate a registered, trusted MCP server (see Call remote MCP tools). Provider-native tool declarations pass through to compatible routes, but pending tool calls are never returned to the client; only final text comes back.
conversation_idNoStable identifier for a conversation. Intelligent Auto stores the selected model per conversation and reuses it on later turns (kendr_routing.source: "sticky"), skipping router latency and cost. Omit it and every request routes from scratch. Not accepted on /v1/messages.
request_idNoBody-level alternative to the Idempotency-Key header; idempotency_key is also accepted. All three are optional — a request ID is generated when none is supplied.
response_formatNoStructured-output preference for routes that support it.
optimizationNoKendr context optimization policy. Use mode: "off" to disable explicitly.
metadataNoApplication metadata. metadata.require_web_search makes search mandatory; metadata.intelligent_reroute selects a model again instead of reusing a compatible conversation route.

Text aliases expose web_search in the model catalog when the capability is available. Kendr supports provider-native web search on compatible routes and a managed cross-provider web-search gateway when configured.

curl -N https://kendr.org/v1/responses \
  -H "Authorization: Bearer $KENDR_API_KEY" \
  -H "Idempotency-Key: web-search-001" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "kc-intelligent",
    "input": "What changed in the web search provider landscape today?",
    "web_search": true,
    "stream": true
  }'
  • Use web_search: true to request current web grounding.
  • Use web_search: false for private/offline prompts that must not use web search.
  • Use metadata.require_web_search: true only when the request should fail rather than answer without web search.
  • When an answer uses web results, show source URLs returned in the model output or cited context.

Call remote MCP tools

Kendr tool calling is server-executed. You attach a registered MCP server to a request; Kendr injects the server URL and its encrypted authorization on the backend, the model calls the MCP tools during generation, and you receive only the final assistant text. There is no client-side function-calling loop: the API never returns a pending tool call for your code to execute, and it has no endpoint for submitting tool results back.

1. Register the server once

Create the server under your account with POST /api/me/mcp-servers (or the MCP servers page in the web app). Registration is a signed-in account action: authenticate with the X-Kendr-Session token from app login, the browser session cookie, or an OAuth token carrying the app scope. API keys cannot manage MCP servers — they only invoke models. The URL must be public HTTPS without embedded credentials, and the server must be marked trusted before it can be enabled. The optional authorization token is stored encrypted (AES-256-GCM) and is never returned by any API.

curl https://kendr.org/api/me/mcp-servers \
  -H "X-Kendr-Session: $KENDR_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Team docs",
    "server_label": "team-docs",
    "server_url": "https://mcp.example.com/mcp",
    "description": "Internal documentation search",
    "authorization": "Bearer mcp-token-...",
    "allowed_tools": ["search_docs", "read_page"],
    "enabled": true,
    "trusted": true
  }'

The response includes the generated id (mcp_...) you will reference in model requests. allowed_tools optionally restricts which MCP tools the model may call.

2. Reference it in a model request

curl https://kendr.org/v1/responses \
  -H "Authorization: Bearer $KENDR_API_KEY" \
  -H "Idempotency-Key: mcp-demo-001" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "kc-intelligent",
    "input": "Search the team docs for the deployment runbook and summarize it.",
    "conversation_id": "support-thread-8841",
    "tools": [
      { "type": "kendr_mcp", "server_id": "mcp_1f2e3d4c5b6a" }
    ]
  }'

Rules and limits

  • The server must be enabled and trusted and belong to the authenticated account, otherwise the request fails with 400 "MCP server ... is unavailable, disabled, or not trusted." The rejection happens before any credits are reserved.
  • MCP tools require an OpenAI Responses route. With kc-intelligent, routing automatically restricts candidates to compatible routes; with a Normal alias on an incompatible route the request fails with 400 "The selected model route does not support remote MCP tools. Choose an OpenAI Responses model."
  • At most 8 MCP servers can be activated per request.
  • Requests that declare tools do not stream token deltas; the response is delivered when generation completes.
  • Provider-native tool declarations (for example OpenAI function tools) are forwarded to compatible routes, but any unexecuted tool call the model emits is not returned; if a model answers only with a tool call, Intelligent Auto treats the output as incomplete and no credits are charged.

Know which credential belongs where

Kendr has three credential layers. They solve different problems and are intentionally not interchangeable.

CredentialWho configures itWhere it is sentPurpose
Kendr API key
kndr_live_...
Customer or customer applicationAuthorization or X-API-KeyCalls model, query, usage, and other API-key-enabled Kendr routes.
Kendr session or OAuth tokenKendr login flowCookie, X-Kendr-Session, or bearer headerActs for a signed-in user and can create or revoke Kendr API keys.
Connected-service authorizationThe signed-in customerKendr connection flow onlyAllows an explicitly connected app or MCP server to provide data and tools. It is never returned through model API responses.
Do not proxy vendor secrets from your client

Your integration should send a Kendr API key and a Kendr model alias such as kc-intelligent. It should not send a vendor API key, Secret Manager ARN, or provider model credential.

Use OpenAI, Anthropic, or native HTTPS clients

OpenAI JavaScript client

import OpenAI from "openai";
import crypto from "node:crypto";

const client = new OpenAI({
  apiKey: process.env.KENDR_API_KEY,
  baseURL: "https://kendr.org/v1"
});

const response = await client.responses.create({
  model: "kc-intelligent",
  input: "Plan a safe database migration."
}, {
  headers: { "Idempotency-Key": crypto.randomUUID() }
});

console.log(response.output_text);
console.log(response.kendr_usage);

OpenAI Python client

import os
import uuid
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["KENDR_API_KEY"],
    base_url="https://kendr.org/v1",
)

response = client.responses.create(
    model="kc-intelligent",
    input="Plan a safe database migration.",
    extra_headers={"Idempotency-Key": str(uuid.uuid4())},
)

print(response.output_text)

Anthropic-compatible request

curl https://kendr.org/v1/messages \
  -H "Authorization: Bearer $KENDR_API_KEY" \
  -H "Idempotency-Key: messages-001" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "kc-intelligent",
    "max_tokens": 1200,
    "messages": [{"role":"user","content":"Review this plan."}]
  }'

Live streaming

curl -N https://kendr.org/v1/chat/completions \
  -H "Authorization: Bearer $KENDR_API_KEY" \
  -H "Idempotency-Key: stream-001" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "kc-intelligent",
    "stream": true,
    "messages": [{"role":"user","content":"Explain the rollout."}]
  }'
Streaming behavior

Normal text routes stream compatible deltas as they arrive. kc-intelligent streams route-selection status first, then streams the selected model's answer and final usage record. Intelligent Auto does not make a second-model verification call.

Integration rules

Item Value
Base domain https://kendr.org
Public discovery GET /api/catalog and GET /api/openapi.json
Query execution POST /api/v1/query; Kendr validates auth and credits, executes the selected surface, then returns a stable Kendr result payload.
Primary server auth Authorization: Bearer kndr_live_... or X-API-Key: kndr_live_...
Customer state GET /api/me/dashboard returns user, packages, api_keys, purchases, ledger, and surfaces

Quickstart flow

1
Discover surfaces and packages
Call GET /api/catalog to see the current surfaces, packages, and docs metadata.
2
Authenticate
Use an API key for backend code, or use X-Kendr-Session or OAuth when the request is directly tied to a customer session.
3
Fetch credits and wallet state
Call GET /api/me/dashboard to read the credit balance, packages, purchases, ledger, and enabled surfaces.
4
Run a query
Send the surface key, query string, and optional params to POST /api/v1/query.

Auth headers accepted by the query route

Mode Header When to use it
API key Authorization: Bearer kndr_live_... Best for server-to-server integrations and background jobs.
API key X-API-Key: kndr_live_... Alternative header when bearer auth is not convenient.
App session X-Kendr-Session: SESSION_TOKEN Best when your app just authenticated the user through Kendr.
OAuth bearer Authorization: Bearer kndr_oat_... Best for Kendr Desktop or CLI flows using PKCE or device code with the app scope.
Browser cookie kendr_session cookie Works for browser-origin requests after /api/auth/otp/verify.

Query request body

Field Required Meaning
surface Yes The Kendr surface key, such as web_search, ai_search, web_answer, google_search, google_maps, google_flights, or google_hotels.
query Yes The primary query string.
params No An object for optional values such as gl, hl, page, location, or travel fields.
Top-level extras No Optional params can also be sent beside surface and query. Kendr merges them with params.
{
  "surface": "google_search",
  "query": "best llm observability tools",
  "params": {
    "gl": "us",
    "hl": "en",
    "page": 1
  }
}

Query examples

Kendr is a plain HTTPS and JSON API. The same request contract works from JavaScript, Python, .NET, Java, and Go, so you can use whichever runtime already exists in your stack.

API key auth

curl https://kendr.org/api/v1/query \
  -X POST \
  -H "Authorization: Bearer $KENDR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "surface": "google_search",
    "query": "best llm observability tools",
    "params": {
      "gl": "us",
      "hl": "en",
      "page": 1
    }
  }'

App session auth

curl https://kendr.org/api/v1/query \
  -X POST \
  -H "X-Kendr-Session: $KENDR_SESSION" \
  -H "Content-Type: application/json" \
  -d '{
    "surface": "google_maps",
    "query": "coworking spaces in austin",
    "params": {
      "gl": "us",
      "hl": "en"
    }
  }'
Switch languages

Kendr currently ships helper clients for JavaScript and Python. Use the tabs to switch between Curl, JavaScript, Python, .NET, Java, and Go for the same query contract.

Server-side fetch request to POST /api/v1/query

const response = await fetch('https://kendr.org/api/v1/query', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.KENDR_API_KEY}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    surface: 'google_search',
    query: 'best llm observability tools',
    params: { gl: 'us', hl: 'en', page: 1 }
  })
});

const payload = await response.json();
console.log(payload.data, payload.remaining_credits);

Official helper clients

Kendr currently publishes helper clients for JavaScript and Python. Use these when you want a thinner call surface around the raw HTTP routes, or call the HTTPS endpoints directly from any other runtime.

JavaScript SDK

import { KendrClient } from './sdk/javascript/index.js';

const client = new KendrClient({
  apiKey: process.env.KENDR_API_KEY,
  baseUrl: 'https://kendr.org'
});

const response = await client.query({
  surface: 'google_search',
  query: 'best llm observability tools',
  params: { gl: 'us', hl: 'en', page: 1 }
});

console.log(response.data);

Python SDK

from kendr import KendrClient

client = KendrClient(
    api_key='YOUR_KENDR_API_KEY',
    base_url='https://kendr.org',
)

response = client.query(
    surface='google_search',
    query='best llm observability tools',
    params={'gl': 'us', 'hl': 'en', 'page': 1},
)

print(response['data'])

Fetch surfaces, packages, and current credits

Use the public catalog to discover what is available, then use the customer dashboard to read the live wallet balance and package state for the authenticated user.

Public catalog

curl https://kendr.org/api/catalog

Dashboard balance

curl https://kendr.org/api/me/dashboard \
  -H "X-Kendr-Session: $KENDR_SESSION"
Returned by the dashboard

The dashboard response includes user.credit_balance, packages, api_keys, purchases, ledger, and surfaces.

Create and use API keys

API keys are customer-scoped credentials. They are created after a customer logs in through a browser session, app session, or OAuth bearer token, and the raw key is returned only once.

Create a key

curl https://kendr.org/api/me/api-keys \
  -X POST \
  -H "X-Kendr-Session: $KENDR_SESSION" \
  -H "Content-Type: application/json" \
  -d '{
    "label": "Production"
  }'

List keys

curl https://kendr.org/api/me/api-keys \
  -H "X-Kendr-Session: $KENDR_SESSION"
Important

The create response includes raw_token once. Persist it immediately and use the returned api_key.id later for revocation.

Successful response and errors

Successful query response

{
  "ok": true,
  "surface": "google_search",
  "credits_charged": 1,
  "remaining_credits": 249,
  "data": {
    "results": [],
    "answer": "",
    "citations": []
  },
  "normalized": {
    "results": [],
    "answer": "",
    "citations": []
  }
}

Errors to handle

  • 400: invalid payload, missing surface, unsupported surface, or invalid JSON.
  • 401: no valid API key, session, or OAuth bearer token was supplied.
  • 402: the wallet does not have enough credits for the requested surface.
  • 502: the requested surface could not be completed.
Charging rule

Kendr charges credits only after the request succeeds. Failed attempts do not directly consume credits.

Read the model response envelope

Successful model responses add three Kendr blocks next to the compatibility-format fields. A trimmed /v1/responses result:

{
  "id": "0887c41b-5285-409d-ad04-b900b9a258c3",
  "model": "kc-intelligent",
  "status": "completed",
  "output_text": "Hello! How can I help?",
  "usage": { "input_tokens": 36, "output_tokens": 25, "total_tokens": 61 },
  "kendr_usage": {
    "credits_charged": "0.01165",
    "credits_charged_micros": 11650,
    "remaining_credits": "49886.925505",
    "request_id": "0887c41b-5285-409d-ad04-b900b9a258c3",
    "pricing_version": "cloud-v1"
  },
  "kendr_routing": {
    "selected_model_alias": "kc-llama-4-scout",
    "source": "router_model",
    "task_category": "flash",
    "reason_code": "trivial_greeting_cheapest_fast",
    "confidence": "0.95",
    "tools": []
  },
  "kendr_optimization": {
    "enabled": true,
    "requested_mode": "balanced",
    "resolved_mode": "balanced",
    "engine": "extractive",
    "estimated_input_tokens_avoided": 24,
    "estimated_credits_saved_micros": 4000,
    "estimated_credits_without_optimization_micros": 15650,
    "shadow": false
  }
}
BlockWhat it tells you
kendr_usageExact settlement for this request: credits charged (also as integer microcredits), remaining wallet balance, token counts, and the idempotent request_id. Retrying with the same idempotency key replays this settled response instead of charging again.
kendr_routingPresent on kc-intelligent requests. selected_model_alias is the model that answered; source is router_model, sticky (reused via conversation_id), or deterministic_fallback; tools lists server-side tools that were active, such as web_search.
kendr_optimizationOptimizer receipt: requested and resolved mode, engine, compression ratio, estimated input tokens avoided, estimated credits saved, comparable unoptimized cost, quality checks, and fallback state. shadow: true is preview-only; resolved_mode: "off" means the request was sent unmodified.

Keep API keys out of client code

Store Kendr API keys in a server-side secret manager or protected environment variable. Never place a live key in browser JavaScript, a mobile bundle, a public repository, logs, analytics events, or support screenshots.

# Local development only
KENDR_API_KEY=kndr_live_replace_with_your_key

# Application configuration
KENDR_BASE_URL=https://kendr.org/v1
KENDR_MODEL=kc-intelligent
  • Create separate keys for development and production so one environment can be revoked without interrupting the other.
  • Use the narrowest scopes your integration needs. Model listing needs models:read; generation needs models:invoke; usage reads need usage:read.
  • Rotate a key by creating its replacement, updating callers, confirming traffic on the new key, and then revoking the old key.
  • Proxy browser and mobile requests through your backend when your product cannot safely hold a server credential.

Kendr configuration reference

Use environment variables or protected deployment secrets for service configuration. Client applications normally need only KENDR_API_KEY, KENDR_BASE_URL, and the selected model alias.

ConfigurationPurpose
KENDR_API_KEYServer-side API key used by your integration.
KENDR_BASE_URLClient SDK base URL, usually https://kendr.org/v1.
KENDR_MODELDefault model alias, for example kc-intelligent.
KENDR_PUBLIC_BASE_URLPublic origin used in generated links and callbacks.
KENDR_ALLOWED_ORIGINSComma-separated origins allowed to call browser-facing JSON routes.
KENDR_DATABASE_URL or DATABASE_URLPostgreSQL connection string.
KENDR_REDIS_URLRedis connection string for shared runtime state.
KENDR_INTERNAL_JWT_SECRETSecret used to authenticate Kendr service-to-service calls.
KENDR_CREDENTIAL_ENCRYPTION_KEYEncryption key for stored connector and MCP credentials.
KENDR_USER_REQUESTS_PER_MINUTEPer-user model request rate limit. Defaults to 60.
KENDR_SES_FROM_EMAIL, KENDR_SES_REGIONEmail sender and region for sign-in messages.
AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKENAWS credentials when the deployment uses AWS-backed capabilities.
AWS_REGION, AWS_DEFAULT_REGIONDefault AWS region for general AWS SDK calls.
BEDROCK_MODEL_REGIONRegion used for Bedrock model invocation. This can differ from the default AWS region.
Managed web-search gatewayCross-provider web search is configured on the Kendr side through the managed gateway; deployments enable it during setup and applications need no additional configuration.
OPENAI_API_KEY, ANTHROPIC_API_KEY, GEMINI_API_KEY, XAI_API_KEYProvider credentials for native model routes.
DEEPSEEK_API_KEY, ZAI_API_KEY, QWEN_API_KEY, KIMI_API_KEY, MISTRAL_API_KEYAdditional provider credentials for enabled native routes.
KENDR_FREE_ACCOUNT_CREDITS, KENDR_REFERRAL_REWARD_CREDITS, KENDR_REFERRAL_JOIN_BONUS_CREDITS, KENDR_CREDIT_EXPIRATION_DAYSStarter, referral, and expiry policy. Defaults are 100 starter credits, 250 credits for each side of a successful referral, and 30-day credit expiry.
RAZORPAY_KEY_ID, RAZORPAY_KEY_SECRETPayment provider credentials for credit purchases.

Retries, timeouts, and idempotency

Idempotency-Key is optional — Kendr generates one per request when absent. Supplying your own key is recommended for production retry safety: reuse the same key only when retrying the same logical operation, and create a new key for a new user request.

StatusWhat it meansRecommended action
400The request is invalid or uses an unsupported field.Do not retry unchanged. Validate the body against OpenAPI and correct it.
401 / 403The key is missing, invalid, revoked, or lacks the required scope.Do not loop. Check the credential and scopes.
402The shared wallet cannot cover the request.Ask the account owner to add credits, then retry as a new attempt.
408 / 429The call timed out or traffic is being limited.Retry with exponential backoff and jitter. Honor Retry-After when present.
500 / 502 / 503The request could not be completed by the service or selected route.Retry a small number of times with the same idempotency key, then surface a recoverable error.
Timeout budget

Model calls can take longer than normal JSON APIs, especially for intelligent routing or tool use. Set an application timeout that matches your user experience, and cancel work when the caller disconnects.

Handle streaming correctly

  • Send stream: true and keep the HTTP connection open for server-sent events.
  • Parse complete SSE events rather than treating arbitrary network chunks as complete JSON messages.
  • Handle start, status, delta, done, and error events where present.
  • Accumulate text deltas for display, but treat the terminal event as the authoritative completion and usage record.
  • If the connection fails before completion, retry the same logical request with the same idempotency key.
  • Do not assume every model emits identical timing or intermediate events; depend only on the compatible event fields you use.

Track usage without recording sensitive prompts

Record your own request identifier, idempotency key, selected Kendr alias, status, latency, token totals, and returned credit charge. Avoid logging authorization headers or full prompts unless your own privacy policy explicitly requires and protects that data.

Account usage

curl https://kendr.org/api/me/billing/summary \
  -H "X-Kendr-Session: $KENDR_SESSION"

Available models

curl https://kendr.org/v1/models \
  -H "Authorization: Bearer $KENDR_API_KEY"

Build model selection from GET /v1/models at runtime. An alias can become temporarily unavailable, so avoid hardcoding assumptions about every account or environment.

Production checklist

  • Create a production key with only the scopes the service requires and store it outside source control.
  • Use TLS and the exact https://kendr.org base domain.
  • Generate an idempotency key for every chargeable operation and preserve it across retries.
  • Handle 400, 401, 402, 429, and 5xx responses deliberately; cap retry attempts.
  • Set connect, read, and overall deadlines appropriate to model latency.
  • Read the model catalog at runtime and provide a graceful fallback when the preferred alias is unavailable.
  • Monitor latency, failures, token usage, and credits charged without logging secrets.
  • Test key rotation, low-credit behavior, duplicate retries, streaming disconnects, and revoked credentials before launch.