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

# Rendering Prompts

> Render a released prompt version from your application at runtime, fill its variables, and send its messages and tools to the model.

Because `render` resolves whatever version the current [environment](/docs/deploy/environments) points at, the prompt your application serves changes the moment you release a new version in the dashboard, with no redeploy. A promotion or rollback takes effect on the next request.

The examples show both the [TypeScript](/docs/reference/typescript-sdk) SDK (`@to11ai/sdk`) and the [Python](/docs/reference/python-sdk) SDK (`to11ai-sdk`), which expose the same `prompts` surface (TypeScript in camelCase, Python in snake\_case).

## Create the client

The SDK's `prompts.*` methods talk to the to11 API, the control plane. Create one client with the API base URL, your key, the project id, and the environment your application runs in, so `render` can default to them:

<CodeGroup>
  ```ts TypeScript theme={null}
  import { createClient } from "@to11ai/sdk";

  const to11 = createClient({
    baseUrl: process.env.TO11_API_URL,    // the to11 control-plane API
    apiKey: process.env.TO11_API_KEY,
    projectId: process.env.TO11_PROJECT_ID,
    env: process.env.TO11_ENV,            // "prod", "staging", …
  });
  ```

  ```python Python theme={null}
  import os
  from to11ai_sdk import create_client

  to11 = create_client(
      base_url=os.environ["TO11_API_URL"],       # the to11 control-plane API
      api_key=os.environ["TO11_API_KEY"],
      project_id=os.environ["TO11_PROJECT_ID"],
      env=os.environ["TO11_ENV"],                # "prod", "staging", …
  )
  ```
</CodeGroup>

The `env` set here is the default label for every `render` call; you can override it per call with `label` (env is just a label value). You can also omit `env` and let the SDK read the `TO11_ENV` environment variable (in the TypeScript SDK `baseUrl`/`apiKey`/`projectId` likewise default from `TO11_API_URL`/`TO11_API_KEY`/`TO11_PROJECT_ID`, so the explicit values above are optional). A render with no label resolvable — no per-call `label`, no client `env`, and no `TO11_ENV` — is an error, so a misconfigured deployment fails loudly instead of serving the wrong prompt.

## Render a prompt

Call `prompts.render` with the prompt's slug and the [variables](/docs/platform/prompts/authoring#variables) it needs:

<CodeGroup>
  ```ts TypeScript theme={null}
  const rendered = await to11.prompts.render("weather-concierge", {
    variables: {
      assistant_name: "Roker",
      city: "New York",
      units: "fahrenheit",
      user_message: "Do I need a jacket?",
      tier: "vip",   // a non-renderable variable: drives conditions, never rendered
    },
  });
  ```

  ```python Python theme={null}
  rendered = to11.prompts.render(
      "weather-concierge",
      variables={
          "assistant_name": "Roker",
          "city": "New York",
          "units": "fahrenheit",
          "user_message": "Do I need a jacket?",
          "tier": "vip",   # a non-renderable variable: drives conditions, never rendered
      },
  )
  ```
</CodeGroup>

You pass every value the prompt needs in one `variables` object. A value fills the `{{ placeholders }}` it's referenced in and can also drive [conditions](/docs/platform/prompts/authoring#conditions); a value the prompt marks [non-renderable](/docs/platform/prompts/authoring#renderable-variables) (like `tier` here) drives conditions but is never rendered. See [Variables](/docs/platform/prompts/authoring#variables).

The result carries everything needed to call the model:

| Field                                    | What it holds                                                                                                       |
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| `rendered.messages`                      | The rendered messages, with variables substituted and non-matching conditional blocks dropped.                      |
| `rendered.tools`                         | The [tool definitions](/docs/platform/prompts/authoring#tool-definitions) for this version, in a provider-neutral shape. |
| `rendered.toolChoice`                    | The tool-choice directive (`auto`, `none`, `required`, or a specific tool).                                         |
| `rendered.promptId` / `rendered.version` | Which prompt and version resolved. Useful for the model-config lookup below and for your own logging.               |

In Python the same fields use snake\_case: `rendered.tool_choice`, `rendered.prompt_id`.

### The developer role

Blocks carry a [role](/docs/platform/prompts/overview#message-roles), and the author's roles outrank the end user's. The **`developer`** role is OpenAI's name for that application-author layer. OpenAI describes a developer message as "instructions provided by the application developer, prioritized ahead of user messages," and offers an analogy: a developer message is like a function definition that sets the rules and business logic, while a user message supplies the arguments those rules run against. Historically this layer was the `system` role, and many models still use that name; OpenAI's newer models accept `developer` as a distinct role. See OpenAI's [text generation guide](https://developers.openai.com/api/docs/guides/text) for the details.

A resolved prompt returns your authored roles **as-is** — a `developer` block comes back as a `developer` message. Converting that to what each provider expects is the SDK's job:

* **OpenAI** accepts `developer` (its modern name for the author layer), so it is passed through unchanged.
* **Anthropic** has no `developer` role, so it goes in Anthropic's top-level `system` field: `toAnthropicSystem` (`to_anthropic_system` in Python) folds the `system` and `developer` turns into that string, and `toAnthropicMessages` omits them from the message list.

So you don't pick a role mapping — you author `developer` blocks, and the SDK places them correctly for the provider you call.

## Model configuration

The model id and generation settings (temperature, token limit) are authored on the version in the editor's **Config** pane, separate from the rendered messages. Read them with `getVersion`:

<CodeGroup>
  ```ts TypeScript theme={null}
  const version = await to11.prompts.getVersion({
    promptId: rendered.promptId,
    versionNumber: rendered.version,
  });

  const cfg = version.modelConfig ?? {}; // { model, temperature, max_tokens }
  ```

  ```python Python theme={null}
  version = to11.prompts.get_version(
      project_id=os.environ["TO11_PROJECT_ID"],
      prompt_id=rendered.prompt_id,
      version_number=rendered.version,
  )

  cfg = version.model_config_ or {}   # {"model", "temperature", "max_tokens"}; note the trailing underscore
  ```
</CodeGroup>

<Note>
  The TypeScript SDK defaults `projectId` on every `prompts.*` method from the client (`createClient({ projectId })`), so you omit it per call. The Python SDK currently still takes `project_id` on each lifecycle/version call — parity is in progress.
</Note>

## Send it to the model

Rendered messages, tools, and tool choice are provider-neutral. The SDK's `gateway` entry point converts them to your provider's request shape and supplies the [gateway](/docs/deploy/overview) auth headers, so you call your normal provider client pointed at the gateway:

<CodeGroup>
  ```ts TypeScript theme={null}
  import OpenAI from "openai";
  import {
    toOpenAIMessages,
    toOpenAITools,
    toOpenAIToolChoice,
    gatewayAuthHeaders,
  } from "@to11ai/sdk/gateway";

  const openai = new OpenAI({
    baseURL: process.env.TO11_GATEWAY_URL,   // data plane: the gateway, not the API
    apiKey: process.env.OPENAI_API_KEY,      // your provider key, or configured in the project
    defaultHeaders: gatewayAuthHeaders({
      apiKey: process.env.TO11_API_KEY,
      projectId: process.env.TO11_PROJECT_ID,
      env: process.env.TO11_ENV,
    }),
  });

  const completion = await openai.chat.completions.create({
    model: `${process.env.TO11_PROVIDER}::${cfg.model ?? "gpt-4o"}`,
    temperature: cfg.temperature,
    max_tokens: cfg.max_tokens,
    messages: toOpenAIMessages(rendered.messages),
    tools: toOpenAITools(rendered.tools),
    tool_choice: toOpenAIToolChoice(rendered.toolChoice),
  });
  ```

  ```python Python theme={null}
  from openai import OpenAI
  from to11ai_sdk.gateway import (
      to_openai_messages,
      to_openai_tools,
      to_openai_tool_choice,
      gateway_auth_headers,
  )

  openai = OpenAI(
      base_url=os.environ["TO11_GATEWAY_URL"],   # data plane: the gateway, not the API
      api_key=os.environ["OPENAI_API_KEY"],      # your provider key, or configured in the project
      default_headers=gateway_auth_headers(
          api_key=os.environ["TO11_API_KEY"],
          project_id=os.environ["TO11_PROJECT_ID"],
          env=os.environ["TO11_ENV"],
      ),
  )

  completion = openai.chat.completions.create(
      model=f"{os.environ['TO11_PROVIDER']}::{cfg.get('model', 'gpt-4o')}",
      temperature=cfg.get("temperature"),
      max_tokens=cfg.get("max_tokens"),
      messages=to_openai_messages(rendered.messages),
      tools=to_openai_tools(rendered.tools),
      tool_choice=to_openai_tool_choice(rendered.tool_choice),
  )
  ```
</CodeGroup>

Every part of the request (messages, tools, tool choice, and model settings) now comes from the released prompt version; nothing is hardcoded. The model string is prefixed with your project's provider slug so the gateway routes it. `toOpenAIToolChoice` returns `undefined` when the version leaves tool choice unset, letting the provider apply its own default. For the Anthropic request shape, use `toAnthropicMessages` / `toAnthropicTools` / `toAnthropicToolChoice` instead. The Python SDK exposes the same converters as `to_openai_*` / `to_anthropic_*`.

<Note>
  The **control plane** (`createClient({ baseUrl: TO11_API_URL, … })`) is where you render prompts; the **data plane** (`baseURL: TO11_GATEWAY_URL`) is where the model call runs. They're different hosts; pointing one at the other is a common setup mistake.
</Note>

### Tools and tool choice

`rendered.tools` and `rendered.toolChoice` mirror the version's [Tools pane](/docs/platform/prompts/authoring#tool-definitions). A specific tool-choice that names a tool no longer present in the resolved version is dropped rather than sent, so a stale directive can't force a call to a tool that isn't offered.

## Resolution and fallbacks

* **Environment resolution.** `render` resolves the single version the environment currently serves. For a [weighted release](/docs/platform/prompts/versions-and-releases#releasing-to-environments), pass a stable `subject` (a user or session id) so the same subject deterministically lands on the same variant; `rendered.variantName` tells you which one served.
* **Offline fallback.** If you pass a `fallback` and the request fails on a network error, the SDK serves the fallback instead of throwing, so a control-plane blip degrades gracefully rather than taking your feature down.

## Errors

`render` throws typed errors you can branch on: a missing prompt, no label supplied, a label that doesn't exist, an unregistered label, or a policy violation among them. See the [TypeScript SDK reference](/docs/reference/typescript-sdk) for the full list and their fields.

## Migration notes

Both SDKs expose only `render()`; the older label-addressed `resolve()` was removed in 2.0. Pass the label as `label` (which defaults to the client's `env`) rather than a `labels` list. See the [TypeScript](/docs/reference/typescript-sdk) and [Python](/docs/reference/python-sdk) SDK references.

<Note>
  Earlier TypeScript SDK builds (0.8.x) accepted a separate `context` argument on `render`. It's been removed: pass those values in `variables` instead, turning off [renderable](/docs/platform/prompts/authoring#renderable-variables) on the ones that shouldn't appear in the text. An old build still sending `context` is accepted without error, but the values are ignored, so blocks that gated on them stop gating until you migrate.
</Note>

## Next steps

<CardGroup cols={2}>
  <Card title="Authoring prompts" icon="pencil" href="/docs/platform/prompts/authoring">
    Build the variables, tools, and config that shape what you render.
  </Card>

  <Card title="TypeScript SDK" icon="code" href="/docs/reference/typescript-sdk">
    Full `prompts.*` method and error reference.
  </Card>

  <Card title="Versions & releases" icon="git-branch" href="/docs/platform/prompts/versions-and-releases">
    Control which version an environment resolves to.
  </Card>

  <Card title="Observe traces" icon="activity" href="/docs/observe/traces">
    See the prompt and version stamped on each model call.
  </Card>
</CardGroup>
