> ## Documentation Index
> Fetch the complete documentation index at: https://docs.slng.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Set up a project for your own code

> Choose what runs on SLNG, pick the region, set up the LLM, and create the API key your LiveKit, Pipecat, or custom agent uses.

Keep your orchestrator, whether LiveKit, Pipecat, or your own code, and run
speech models and the Context Router on SLNG underneath it. You set this up once,
in a project. The project decides three things your code depends on:

* **Which layers run on SLNG.** Speech to text (STT), the large language model
  (LLM), text to speech (TTS), or none of them, with the execution layer in front
  of every call.
* **The region.** The project's API key works in that region only.
* **The API key.** Only a key created inside this project can call the Context
  Router.

This page walks through the dashboard. The
[LiveKit](/guides/integrate/livekit) and [Pipecat](/guides/integrate/pipecat)
guides then show the code, and the
[SLNG templates](https://github.com/slng-ai/slng-templates) run it end to end.

## Pick the right API key

SLNG has three kinds of API key. They are not interchangeable, so check which one
you have before you write code.

| Key | Where you create it | What it calls |
| - | - | - |
| Models only | **New project** > **Call TTS/STT models API** > **Generate key**. See [Create your API key](/guides/get-started/quickstart#create-your-api-key). | Speech to text and text to speech models. Not the LLMs or the Context Router. |
| Agents managed by SLNG | A project from **Build an agent**. See [Create an agent](/guides/get-started/create-a-project/create-agent). | Your agent deployed on SLNG. |
| Your own agents | A project from **Improve my agent**, under **In your own code**. This page. | Speech models and the Context Router, for the layers your project includes. |

A models-only key works for SLNG speech in your own code, but the Context Router
rejects it with `403 project_mode_forbidden`. To use the router, create the key in
an **Improve my agent** project, as below.

## Create the project

**1. Choose your framework**

On the [Projects page](https://app.slng.ai/projects), click **New project**.
Under **Improve my agent** > **In your own code**, choose **LiveKit**,
**Pipecat**, or **Custom**.

![New project start screen with LiveKit highlighted under In your own code](https://mintcdn.com/slng-new-docs/_MURdOw87SJsfVag/heroshots/livekit-select.png?fit=max\&auto=format\&n=_MURdOw87SJsfVag\&q=85\&s=0d5e7a5a9204a823c18b11d942e8c1d3)

**2. Choose what runs on SLNG**

Pick one of the four configurations. Your orchestrator stays yours in all of
them. Each card shows its price per agent minute.

| Configuration | Runs on SLNG | Stays on your stack |
| - | - | - |
| **Execution layer only** | The execution layer: caching, routing, governance, and marking | STT, LLM, and TTS, on your own provider keys |
| **Add transcription** | The execution layer, and STT with automatic failover | LLM and TTS |
| **Voice in, voice out** | The execution layer, STT, and TTS | LLM |
| **Use your orchestrator, we handle the rest** | The execution layer, STT, LLM, and TTS | Nothing but the orchestrator |

The LLM can still go through the Context Router when it stays on your stack:
the router then answers on your own provider key. See
[Set up the LLM](#set-up-the-llm).

![Step 2 of the new project flow with four configuration cards, from Execution layer only to Use your orchestrator, we handle the rest](https://mintcdn.com/slng-new-docs/6zppblsblwuOw1hJ/heroshots/execution-layer-configurations.png?fit=max\&auto=format\&n=6zppblsblwuOw1hJ\&q=85\&s=bd5c30164555ed8c18a502be4c727cf8)

> [!TIP]
>
> Not sure? Start with **Voice in, voice out**. SLNG runs speech on both sides
> of the pipeline, and your own LLM can still sit behind the Context Router.
> Each section's **Include** toggle changes this later.

**3. Name the project and pick its region**

Enter a **Project name**, then choose the **Region** and **Primary
language**. The region decides where the project's API keys work, so pick it
with care: see [Use the key in its region](#use-the-key-in-its-region). The
language filters the model lists, and you can change it later.

![Configure your project step with the Region dropdown open](https://mintcdn.com/slng-new-docs/_MURdOw87SJsfVag/heroshots/new-project-region.png?fit=max\&auto=format\&n=_MURdOw87SJsfVag\&q=85\&s=479129dc9a72fe51226c5d9a9df6a9ed)

Click **Create project**. The project overview lists what runs where, with a
link to each section.

## Use the key in its region

A project's API keys live in the project's region and work only there. Your code
names the region in two base URLs, and both must use the region's gateway code:

| Service | Base URL | In code |
| - | - | - |
| Speech to text and text to speech | `{region}.api.slng.ai` | LiveKit: `slng_base_url`. Pipecat: `world_part`, the code alone. |
| Context Router | `https://{region}.context-router.slng.ai/v1` | The OpenAI client's `base_url`. |

The dashboard labels regions by country. Find the matching code on the
[regions map](/guides/regions/regions-map#regions-and-base-urls): for a project
in the **Netherlands**, the code is `eu-north`, and for the **United Kingdom** it
is `gb`. A request to another region's router answers `401`. If you move the
project to another region in its settings, change the code in both URLs too.

## Set up Listen and Speak

Open **Execution Layer** > **Listen** to choose the speech to text model, and
**Speak** to choose the text to speech model and voice. Each model dropdown has an
**SLNG models** tab and a **BYOK** tab for a model on your own provider key. The
**Integrate** prompt writes these choices into your code, and your code sends the
model ID with each request. For the dropdowns and toggles, see
[Listen](/guides/agents/configure/listen) and [Speak](/guides/agents/configure/speak).

## Set up the LLM

The Context Router answers with a model from one of three places. Pick one:

| The router's model | Where you set it | Template |
| - | - | - |
| An SLNG model, only in **Use your orchestrator, we handle the rest** | **Think** > **SLNG models** tab | `speech_router.py` |
| Your own model, saved in the dashboard | **Think** > **BYOK** tab > **Add new key** | `speech_router.py` |
| Your own model, sent with each request | `slng_config` in the request body. Nothing in **Think**. | `speech_router_byok.py` |

Always send `"model": "slng/auto"`. Without `slng_config`, the router answers with
the **Think** model. With `slng_config`, it picks from the models the config names,
so you write the model name in one place only.

In every other configuration the LLM is yours, so the router refuses an SLNG model
with `403 project_mode_forbidden` and asks for your own credentials.

You can also skip the router and call your LLM directly, as `speech.py` does:
your speech still runs on SLNG, but the LLM step gets no caching, routing, or
failover.

### Save your model in the dashboard

**1. Open Think**

Open **Execution Layer** > **Think**. Keep **Context Router** set to
**Enabled**. Under **LLM model**, click **Select model**, then open the
**BYOK** tab.

![Think section with the LLM model dropdown open on the BYOK tab, a saved openai/gpt-5.6-luna model, and Add new key at the bottom](https://mintcdn.com/slng-new-docs/ciyRldk3d_g8rRBE/heroshots/think-byok-model.png?fit=max\&auto=format\&n=ciyRldk3d_g8rRBE\&q=85\&s=d2b6fd3af34e378f60aeef9c905c8e54)

**2. Add your provider key**

Click **Add new key**. In **Add an LLM provider**, enter the **Model** name
your provider expects, such as `gpt-4o`, your provider's **API key**, and its
**Endpoint URL**, such as `https://api.openai.com/v1`. Leave **Provider** on
**Auto-detect**, or pick one. Click **Add key**.

![The Add an LLM provider dialog with Model, API key, Endpoint URL, and Provider fields](https://mintcdn.com/slng-new-docs/fgzHSTXC7k7AhIuF/images/byok/add-llm-provider.png?fit=max\&auto=format\&n=fgzHSTXC7k7AhIuF\&q=85\&s=e00808fdae460afd50055f0894285bc8)

The key is stored encrypted in your organization's BYOK vault, so your other
projects can use it too. See
[Add a custom LLM model](/guides/models/bring-your-own-key#add-a-custom-llm-model).

**3. Choose it and save**

Pick the model from the **BYOK** tab and click **Save changes**. Requests
with this project's key and `"model": "slng/auto"` now answer with it.

### Send your model with each request

To keep the model and its key in your code instead, send them in the
`slng_config` body field. The router answers that request with them, whatever
**Think** holds, and the project's other settings still apply:

```json slng_config theme={null}
{
  "tiers": {
    "1": [
      {
        "model": "gpt-5.6-luna",
        "weight": 100,
        "endpoint": {
          "provider": "openai-compat",
          "url": "https://api.openai.com/v1",
          "api_key": "sk-..."
        },
        "kwargs": {"reasoning_effort": "none"}
      }
    ]
  }
}
```

Send it with `"model": "slng/auto"`: the router picks `gpt-5.6-luna` from the
config, so the model name is written once. The `api_key` inside `endpoint` is your
provider key. Your SLNG key stays in the `Authorization` header. `kwargs` holds
request settings for this model, here a voice-friendly `reasoning_effort`. For every field, other providers, and two-tier routing,
see [BYOK Context Router](/guides/execution-layer/llm/integration-guide#byok-context-router-slng_config).

## Create the project API key

**1. Open the project's API keys**

In the project, open **Project Vault** > **SLNG API key** and click **Create
API key**.

**2. Name the key**

Enter an **API name**, such as `wayne-enterprises-prod`. The **Project** field
shows this project and the layers the key can call, for example **Execution
Layer** `stt` `tts`. Set an **Expiration date**, or leave it empty.

![Create API key dialog with the project's layers, Execution Layer stt tts, shown under Project](https://mintcdn.com/slng-new-docs/6zppblsblwuOw1hJ/heroshots/project-api-key-create.png?fit=max\&auto=format\&n=6zppblsblwuOw1hJ\&q=85\&s=bc81d0ab83f6a877e74a3b0ac96091e6)

**3. Generate and copy the key**

Click **Generate key** and copy it: you only see it once. Store it as
`SLNG_API_KEY`, as in
[Store and use your key](/guides/get-started/quickstart#store-and-use-your-key).

## Send an agent ID and a session ID

Every request your agent makes carries two IDs: one for the agent, one for the
call. Send the same two values to speech to text, the LLM, and text to speech, so
SLNG ties each call's usage, cache, and logs together.

* **Agent ID** names the agent. Use one stable value, such as
  `wayne-concierge-v1`. It also scopes the Context Router cache, so give the agent
  a new ID, such as `wayne-concierge-v2`, when you change its prompt in a
  meaningful way.
* **Session ID** names one call. Use a value unique to the call, like a UUID or
  your room or call ID, and keep it the same on every turn of that call.

| Where | Agent ID | Session ID |
| - | - | - |
| SLNG speech plugins, LiveKit and Pipecat | `external_agent_id` | `external_session_id` |
| Context Router | `X-Slng-Agent-Id` header | `X-Slng-Session-Id` header |

The router requires both and returns `400 missing_client_id` without them. For
the value rules, see
[Agent and session headers](/guides/execution-layer/llm/context-router#agent-and-session-headers).

## Fill per-call values with template variables

Write values that change per call, such as the caller's name, as `{{name}}`
placeholders in your prompt. Send their values with each request in
`template_variables`, and the Context Router fills them in before the model sees
the prompt:

```json Request body (excerpt) theme={null}
{
  "model": "slng/auto",
  "messages": [
    {"role": "system", "content": "You are the front desk of {{company_name}}, speaking with {{caller_name}}."}
  ],
  "template_variables": {"company_name": "Wayne Enterprises", "caller_name": "Bruce"}
}
```

The cached answer keeps the placeholders, so callers with different names can
share it. Keep a value that changes what the answer says, such as the reply
language, in the prompt itself. A placeholder with no value returns
`422 missing_template_variables`. The [LiveKit](/guides/integrate/livekit) and
[Pipecat](/guides/integrate/pipecat) guides show where each framework sends them,
and [Per-call values](/guides/execution-layer/llm/integration-guide#per-call-values-template_variables)
covers the syntax and limits.

## Check your setup

The [SLNG templates](https://github.com/slng-ai/slng-templates) cover each LLM
option for both frameworks. Each template's `smoke.py` runs one spoken turn,
caller audio to transcript to reply to audio, with no microphone:

```bash Run one turn theme={null}
git clone https://github.com/slng-ai/slng-templates
cd slng-templates/livekit   # or pipecat
cp .env.example .env        # set SLNG_API_KEY, SLNG_REGION, OPENAI_API_KEY
uv sync
uv run python smoke.py speech_router
```

| Error | What to check |
| - | - |
| `403 project_mode_forbidden`, "…a project on the Execution Layer" | The key is not from an **Improve my agent** project. Create one in the project's **Project Vault**. |
| `403 project_mode_forbidden`, "…customer-provided credentials…" | The project's LLM is yours, but **Think** names an SLNG model. Choose a model from the **BYOK** tab, or send `slng_config`. |
| `401` | The base URLs use another region than the project's. |
| `400 missing_client_id` | The agent or session ID is missing. |
| `422 missing_template_variables` | The prompt has a `{{name}}` placeholder with no value in `template_variables`. |

## Write the code

> **[LiveKit](/guides/integrate/livekit)**
>
> Use SLNG speech models and the Context Router in a LiveKit agent.

> **[Pipecat](/guides/integrate/pipecat)**
>
> Use SLNG speech models and the Context Router in a Pipecat pipeline.

> **[Custom LLM](/guides/integrate/custom)**
>
> Route your existing OpenAI-compatible LLM calls through the Context Router.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.