Errors, retries and usage
Handle failures, recover interrupted requests, and reconcile usage and credits.
Know the pricing and credit policy
All model aliases and routing modes use one fixed 5% markup on the configured provider cost. Exact aliases and Kendr managed routing do not have different markup percentages. Credits added by a completed paid package purchase never expire; promotional grants can have a grant-specific expiration.
- Use the public catalog's pricing_policy and per-model pricing fields for public quotes.
- Use authenticated GET /v1/models for models and rates available to the current account.
- Use kendr_usage.credits_charged_micros as the authoritative settled model charge.
- Use the wallet activity or billing summary to distinguish non-expiring purchased lots from expiring promotional lots.
See Credits and billing for package and lot behavior.
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://api.kendr.org/api/catalog
Dashboard balance
curl https://api.kendr.org/api/me/dashboard \ -H "X-Kendr-Session: $KENDR_SESSION"
The dashboard response includes user.credit_balance, packages, api_keys, purchases, ledger, and surfaces.
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": "kendr-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": {
"receipt_id": "kr_86f82eb4d7a54fa2b30d8ce43e7d5f72",
"request_id": "0887c41b-5285-409d-ad04-b900b9a258c3",
"policy_version": "balanced-v1",
"catalog_version": "catalog-2026-08-12",
"pricing_version": "cloud-v1",
"rate_card_version": "2026-08-12T00:00:00Z",
"requested_model": "kendr-intelligent",
"selected_model_alias": "kc-llama-4-scout",
"task_category": "flash",
"reason_code": "trivial_greeting_cheapest_fast",
"confidence": "0.95",
"tools": [],
"latency_ms": 842,
"attempted_candidates": [
{"attempt": 1, "selected_model_alias": "kc-llama-4-scout", "status": "succeeded", "latency_ms": 842}
],
"usage": {
"input_tokens": 36,
"cached_input_tokens": 0,
"cache_write_tokens": 0,
"output_tokens": 25,
"reasoning_tokens": 0,
"total_tokens": 61
},
"cost": {
"currency": "credits",
"credits_charged_micros": 11650,
"credits_charged": "0.01165"
}
},
"kendr_optimization": {
"enabled": true,
"requested_mode": "balanced",
"resolved_mode": "balanced",
"engine": "kendr_optimizer",
"estimated_input_tokens_avoided": 24,
"estimated_credits_saved_micros": 4000,
"estimated_credits_without_optimization_micros": 15650,
"shadow": false
}
}
| Block | What it tells you |
|---|---|
| kendr_usage | Exact 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_routing | Present on Kendr managed routing requests. It identifies the requested route product, selected public alias, decision category/reason/confidence, sanitized attempt and fallback outcomes, latency, policy/catalog/pricing versions, settled token or billing-unit usage, and settled credit cost. Store receipt_id for support or audit correlation, but treat it as opaque and never parse its current kr_... form. Provider identities, router-model internals, and raw provider errors are not public. |
| kendr_optimization | Optimizer 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. |
A failed managed-route request can return sanitized routing identifiers, versions, selected alias, attempts, and outcome metadata in its error details. It does not claim settled usage or cost when the request was not settled successfully.
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://api.kendr.org/v1 KENDR_MODEL=kendr-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.
| Configuration | Purpose |
|---|---|
| KENDR_API_KEY | Server-side API key used by your integration. |
| KENDR_BASE_URL | Client SDK base URL, usually https://api.kendr.org/v1. |
| KENDR_MODEL | Default model alias, for example kendr-intelligent. |
| KENDR_PUBLIC_BASE_URL | Public origin used in generated links and callbacks. |
| KENDR_ALLOWED_ORIGINS | Comma-separated origins allowed to call browser-facing JSON routes. |
| KENDR_DATABASE_URL or DATABASE_URL | PostgreSQL connection string. |
| KENDR_REDIS_URL | Redis connection string for shared runtime state. |
| KENDR_INTERNAL_JWT_SECRET | Secret used to authenticate Kendr service-to-service calls. |
| KENDR_CREDENTIAL_ENCRYPTION_KEY | Encryption key for stored connector and MCP credentials. |
| KENDR_USER_REQUESTS_PER_MINUTE | Per-user model request rate limit. The implementation default is 60 when Redis-backed rate-limit state is available; the same limit is enforced per replica from in-process state when that shared state is unavailable. Deployment and account limits can differ, and a 429 response is authoritative. |
| KENDR_SES_FROM_EMAIL, KENDR_SES_REGION | Email sender and region for sign-in messages. |
| AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN | AWS credentials when the deployment uses AWS-backed capabilities. |
| AWS_REGION, AWS_DEFAULT_REGION | Default AWS region for general AWS SDK calls. |
| BEDROCK_MODEL_REGION | Region used for Bedrock model invocation. This can differ from the default AWS region. |
| Managed web-search gateway | Cross-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_KEY | Provider credentials for native model routes. |
| DEEPSEEK_API_KEY, ZAI_API_KEY, QWEN_API_KEY or DASHSCOPE_API_KEY, BYTEPLUS_API_KEY or ARK_API_KEY, KIMI_API_KEY, MISTRAL_API_KEY, SARVAM_API_KEY, TOKENRA_API_KEY | Additional provider credentials for enabled native routes. QWEN_API_KEY and BYTEPLUS_API_KEY take precedence over their provider-native fallback names. |
| KENDR_FREE_ACCOUNT_CREDITS, KENDR_REFERRAL_REWARD_CREDITS, KENDR_REFERRAL_JOIN_BONUS_CREDITS, KENDR_PROMOTIONAL_CREDIT_EXPIRATION_DAYS | Promotional grant amounts and the default promotional-grant expiry window. The older KENDR_CREDIT_EXPIRATION_DAYS name remains a compatibility fallback. Neither setting applies to completed paid purchases; purchased credit lots have no expiry. |
| RAZORPAY_KEY_ID, RAZORPAY_KEY_SECRET | Payment 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.
| Status | What it means | Recommended action |
|---|---|---|
| 400 | The request is invalid or uses an unsupported field. | Do not retry unchanged. Validate the body against OpenAPI and correct it. |
| 401 / 403 | The key is missing, invalid, revoked, or lacks the required scope. | Do not loop. Check the credential and scopes. |
| 402 | The shared wallet cannot cover the request. | Ask the account owner to add credits, then retry as a new attempt. |
| 408 / 429 | The call timed out or traffic is being limited. | Retry with exponential backoff and jitter. Honor Retry-After if a gateway includes it, but do not require that header. |
| 500 / 502 / 503 | The 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. |
Model calls can take longer than normal JSON APIs, especially for intelligent routing or tool use. Before the first streamed answer delta, a disconnect may cancel the request. After output starts, Kendr finishes and settles the server-owned request. To recover without any chance of starting a second generation, send an authenticated, empty-body POST /api/v1/llm/responses/replay with the original Idempotency-Key. This actor- and wallet-scoped lookup never plans, reserves credits, or invokes a provider, and a settled replay retains its optimizer and web-search receipts. Compatibility clients may instead resend the exact original body and key with X-Kendr-Idempotency-Replay-Only: true.
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://api.kendr.org/api/me/billing/summary \ -H "X-Kendr-Session: $KENDR_SESSION"
Available models
curl https://api.kendr.org/v1/models \ -H "Authorization: Bearer $KENDR_API_KEY"
Build authenticated model selection from GET /v1/models at runtime. Use unauthenticated GET /api/public/models for public directories or pre-login discovery; its versioned response exposes public capabilities, availability, and credit rates but deliberately omits private provider routes and account-specific aliases. 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://api.kendr.org API 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.