1. Endpoint and authentication
The router is one POST endpoint that speaks the OpenAI Chat Completions protocol.Your base URL is region-specific. The router is deployed in several regions, and your base URL names the region your traffic is served from. It looks likeYou authenticate with only yourhttps://<region>.context-router.slng.ai/v1(India)https://india.context-router.slng.ai/v1(US)https://us.context-router.slng.ai/v1(Indonesia)https://indonesia.context-router.slng.ai/v1(EU)https://eu.context-router.slng.ai/v1
SLNG_API_KEY, passed as a Bearer token. The OpenAI SDK’s api_key parameter sets this header for you.
2. Making a request
A request is a standard OpenAI Chat Completions call.Streaming
Streaming works exactly as in OpenAI, for every model the router serves (reasoning and non-reasoning alike). Always setstream_options.include_usage=True so you receive the final usage trailer chunk.
- It is standard OpenAI SSE:
data: {...}chunks ending withdata: [DONE]. - You only ever receive the final answer text. Whatever model answers, the stream carries just the reply itself — nothing else that could reach your TTS.
- All answer content arrives before the
finish_reason: "stop"chunk, so the usual client pattern of stopping atfinish_reason: "stop"is safe; you will never lose the tail of an answer that way. - The
usageblock rides the final chunk (that is whystream_options.include_usage=Truematters). A few underlying providers do not report usage on streams; in that case the fields are simply absent. - The stream always ends cleanly. If the underlying model fails mid-stream, the router still sends a final
finish_reason: "stop"chunk and[DONE]instead of leaving your client hanging on a truncated stream.
3. What you control (the request body)
These are the fields you set. The router reads only these from your request body (plus the optionaltemplate_variables and slng_config extensions):
model: "slng/auto" vs a named model
slng/auto(recommended): SLNG routes the request using the model configuration set up for your org. This is what lets SLNG tune which model answers without you changing any code.- A named model: if you have created a BYOK LLM in SLNG, you can pass the name of it to target that model directly.
4. Identity, agents, and sessions
Three things travel alongside your request but are not fields in the JSON body. Your org identity is fully managed by SLNG; the other two are HTTP headers you send on every request.- Internal auth + your org identity (managed for you). SLNG authenticates your API key and resolves your organization context. You never set this yourself, and there is no body field for it.
- Agent scope — the
X-Slng-Agent-Idheader. Send this on every request. It names the logical agent a call belongs to, which lets SLNG apply the right per-agent handling and optimizations. Use a stable value per agent, the same one on every call that agent makes. Best practice: put a version or date suffix in the value (for exampleclinic-scheduler-v1orclinic-scheduler-2026-07). Whenever you change that agent’s configuration in a meaningful way, most importantly its system prompt, bump the suffix (...-v2, a new date, etc.) so the new version gets a fresh identity. Reusing the old id after a prompt change means SLNG keeps treating it as the same agent, which can leave optimizations tuned to the previous version in effect. Keeping the id in step with your agent version avoids that. It is a request header, not a body field. - Session scope — the
X-Slng-Session-Idheader. Send this on every request. It identifies a single call (conversation) so SLNG can track per-call state for session-aware features. Use a value unique to each call, like a UUID, and keep it the same across all the turns of that call. It is a request header, not a body field.
template_variables for your per-call values (see section 5, and please do use it). Org identity is handled for you; X-Slng-Agent-Id and X-Slng-Session-Id are headers you send on every request.
5. Per-call values (template_variables)
Voice agents need per-call values, the caller’s name, their city, an appointment time, inside the system prompt. Instead of building those strings yourself before every request, you write a placeholder once and let SLNG fill it in.
This is one of the most important integration steps in this guide. If your system prompt contains anything that changes from call to call, send it throughThe pattern in one glance:template_variables. Do not render it into the prompt text yourself. When per-call values are baked into the prompt as plain text, every call looks slightly different to SLNG and it cannot apply its optimizations to your traffic properly. With placeholders +template_variables, SLNG sees your stable prompt and the per-call values separately, which is exactly what its optimizations need. The model sees the same final prompt either way, but the difference in the speed and cost SLNG can deliver for you is real.
{{caller_name}}, {{city}}. Names are letters, digits, and underscore. Single braces ({name}), Jinja, and ${var} are NOT placeholders, only {{name}} is. (Limits: up to 64 variables per request, names up to 64 characters, values up to 4000 characters; exceeding these returns a 422.)
Where it substitutes. When you send a template_variables object, the router replaces every {{name}} with the value you supplied in your system prompt (the system message), before it goes to the model.
Only the system message is touched. Your user and assistant message content is never modified, so a literal {{...}} a caller happens to type is left exactly as-is.
{{name}} you did not supply in template_variables, the router returns a 422:
Watch out for literal double braces. Any---Completions request, SLNG decides which underlying model answers, and you get the answer back in the standard OpenAI shape. Existing OpenAI SDKs and most voice-agent orchestrators work unchanged.{{...}}in your system prompt is treated as a variable reference. If your prompt contains a literal{{for some other reason (a code sample, a templating example), it will be read as a placeholder and trigger amissing_template_variables422 unless you supply a matching value. Avoid stray double braces in prompts you do not intend as variables. If you have a live integration that cannot avoid them, SLNG can put your org in a lenient mode where un-supplied placeholders are left as-is instead of 422ing (ask SLNG; see section 10).
Limits and errors
- The
slng_configobject must stay under 256 KB when serialized; larger returns a 400. - A config that fails validation returns a 400 starting with
invalid slng_config:and a description of what is wrong. Your credentials are never included in these messages. - Sending
slng_configas anything other than a JSON object (a string, a list) returns a 422 with codeinvalid_slng_config. - Sending it before SLNG has enabled it for your integration returns a 400
inline slng_config is not enabled.
Related
Context Router
Where the router fits in the execution layer.
Connect ElevenLabs
Use the Context router as a custom model in Eleven labs.
Connect Vapi
Use the Context router as a custom model in Vapi.
Connect Retell
Use the Context router as a custom model in Retell.