--- title: "Prompts" sidebarTitle: "Prompts" description: "Define prompt templates as code, version them on deploy, and override from the dashboard without redeploying." --- ## Overview AI Prompts let you define prompt templates in your codebase alongside your tasks. When you deploy, Trigger.dev automatically versions your prompts. You can then: - View all prompt versions in the dashboard - Create **overrides** to change the prompt text or model without redeploying - Track every generation that used each prompt version - See token usage, cost, and latency metrics per prompt - Manage prompts programmatically via SDK methods ## Defining a prompt Use `prompts.define()` to create a prompt with typed variables: ```ts import { prompts } from "@trigger.dev/sdk"; import { z } from "zod"; export const supportPrompt = prompts.define({ id: "customer-support", description: "System prompt for customer support interactions", model: "gpt-4o", config: { temperature: 0.7 }, variables: z.object({ customerName: z.string(), plan: z.string(), issue: z.string(), }), content: `You are a support agent for Acme SaaS. ## Customer context - **Name:** {{customerName}} - **Plan:** {{plan}} - **Issue:** {{issue}} Respond to the customer's issue. Be concise and helpful.`, }); ``` ### Options | Option | Type | Required | Description | |--------|------|----------|-------------| | `id` | `string` | Yes | Unique identifier (becomes the prompt slug) | | `description` | `string` | No | Shown in the dashboard | | `model` | `string` | No | Default model (e.g. `"gpt-4o"`, `"claude-sonnet-4-6"`) | | `config` | `object` | No | Default config (temperature, maxTokens, etc.) | | `variables` | Zod/ArkType schema | No | Schema for template variables (enables validation and dashboard UI) | | `content` | `string` | Yes | The prompt template with `{{variable}}` placeholders | ### Template syntax Templates use Mustache-style placeholders: - `{{variableName}}` — replaced with the variable value - `{{#conditionalVar}}...{{/conditionalVar}}` — content only included if the variable is truthy ```ts export const prompt = prompts.define({ id: "summarizer", model: "gpt-4o-mini", variables: z.object({ text: z.string(), maxSentences: z.string().optional(), }), content: `Summarize the following text{{#maxSentences}} in {{maxSentences}} sentences or fewer{{/maxSentences}}: {{text}}`, }); ``` ## Resolving a prompt ### Via prompt handle Call `.resolve()` on the handle returned by `define()`: ```ts const resolved = await supportPrompt.resolve({ customerName: "Alice", plan: "Pro", issue: "Cannot access billing dashboard", }); console.log(resolved.text); // The compiled prompt with variables filled in console.log(resolved.version); // e.g. 3 console.log(resolved.model); // "gpt-4o" console.log(resolved.labels); // ["current"] or ["override"] ``` ### Via standalone prompts.resolve() Resolve any prompt by slug without needing a handle. Pass the prompt handle as a type parameter for full type safety: ```ts import { prompts } from "@trigger.dev/sdk"; import type { supportPrompt } from "./prompts"; // Fully typesafe — ID and variables are checked at compile time const resolved = await prompts.resolve("customer-support", { customerName: "Alice", plan: "Pro", issue: "Cannot access billing dashboard", }); ``` Without the generic, the function still works but accepts any string slug and `Record` variables. ### Resolve options You can resolve a specific version or label: ```ts // Resolve a specific version const v2 = await supportPrompt.resolve(variables, { version: 2 }); // Resolve by label const current = await supportPrompt.resolve(variables, { label: "current" }); ``` By default, `resolve()` returns the **override** version if one is active, otherwise the **current** (latest deployed) version. Both `promptHandle.resolve()` and `prompts.resolve()` call the Trigger.dev API when a client is configured. During local dev with `trigger dev`, this means you'll always get the server version (including overrides). ## Using with the AI SDK The resolved prompt integrates with the [Vercel AI SDK](https://ai-sdk.dev) via `toAISDKTelemetry()`. This links AI generation spans to the prompt in the dashboard. ### generateText ```ts import { task } from "@trigger.dev/sdk"; import { generateText, stepCountIs } from "ai"; import { anthropic } from "@ai-sdk/anthropic"; export const supportTask = task({ id: "handle-support", run: async (payload) => { const resolved = await supportPrompt.resolve({ customerName: payload.name, plan: payload.plan, issue: payload.issue, }); const result = await generateText({ model: openai(resolved.model ?? "gpt-4o"), system: resolved.text, prompt: payload.issue, ...resolved.toAISDKTelemetry(), }); return { response: result.text }; }, }); ``` ### streamText ```ts import { streamText } from "ai"; export const streamTask = task({ id: "stream-support", run: async (payload) => { const resolved = await supportPrompt.resolve({ customerName: payload.name, plan: payload.plan, issue: payload.issue, }); const result = streamText({ model: openai(resolved.model ?? "gpt-4o"), system: resolved.text, prompt: payload.issue, ...resolved.toAISDKTelemetry(), stopWhen: stepCountIs(15), }); let fullText = ""; for await (const chunk of result.textStream) { fullText += chunk; } return { response: fullText }; }, }); ``` ### Custom telemetry metadata Pass additional metadata to `toAISDKTelemetry()` that will appear on the generation span: ```ts const result = await generateText({ model: anthropic("claude-sonnet-4-5"), prompt: resolved.text, ...resolved.toAISDKTelemetry({ "task.type": "summarization", "customer.tier": "enterprise", }), }); ``` ## Using with chat.agent() Prompts integrate with `chat.agent()` via `chat.prompt` — a run-scoped store for the resolved prompt. Store a prompt once in a lifecycle hook, then access it anywhere during the run. ### chat.prompt.set() and chat.prompt() ```ts import { chat } from "@trigger.dev/sdk/ai"; import { prompts } from "@trigger.dev/sdk"; import { streamText, createProviderRegistry } from "ai"; import { anthropic } from "@ai-sdk/anthropic"; const registry = createProviderRegistry({ anthropic }); const systemPrompt = prompts.define({ id: "my-chat-system", model: "anthropic:claude-sonnet-4-5", config: { temperature: 0.7 }, variables: z.object({ name: z.string() }), content: `You are a helpful assistant for {{name}}.`, }); export const myChat = chat.agent({ id: "my-chat", onChatStart: async ({ clientData }) => { const resolved = await systemPrompt.resolve({ name: clientData.name }); chat.prompt.set(resolved); }, run: async ({ messages, signal }) => { return streamText({ ...chat.toStreamTextOptions({ registry }), messages, abortSignal: signal, stopWhen: stepCountIs(15), }); }, }); ``` ### chat.toStreamTextOptions() Returns an options object ready to spread into `streamText()`. When a prompt is stored via `chat.prompt.set()`, it includes: - `system` — the compiled prompt text - `model` — resolved via the `registry` when provided - `temperature`, `maxTokens`, etc. — from the prompt's `config` - `experimental_telemetry` — links generations to the prompt in the dashboard ```ts // With registry — model is resolved automatically const options = chat.toStreamTextOptions({ registry }); // { system: "...", model: LanguageModel, temperature: 0.7, experimental_telemetry: { ... } } // Without registry — model is not included const options = chat.toStreamTextOptions(); // { system: "...", temperature: 0.7, experimental_telemetry: { ... } } ``` When the user provides a `registry` and the prompt has a `model` string (e.g. `"anthropic:claude-sonnet-4-5"`), the model is resolved via `registry.languageModel()` and included in the returned options. This means `streamText` uses the prompt's model by default — no manual model selection needed. ### Reading the prompt Access the stored prompt from anywhere in the run: ```ts run: async ({ messages, signal }) => { const prompt = chat.prompt(); // Throws if not set console.log(prompt.text); // The compiled prompt console.log(prompt.model); // "anthropic:claude-sonnet-4-5" console.log(prompt.version); // 3 return streamText({ ...chat.toStreamTextOptions({ registry }), messages, abortSignal: signal, stopWhen: stepCountIs(15), }); }, ``` You can also set a plain string if you don't need the full prompt system: ```ts chat.prompt.set("You are a helpful assistant."); ``` ## Prompt management SDK The `prompts` namespace includes methods for managing prompts programmatically. These work both inside tasks and outside (e.g. scripts, API handlers) as long as an API client is configured. ### List prompts ```ts const allPrompts = await prompts.list(); ``` ### List versions ```ts const versions = await prompts.versions("customer-support"); ``` ### Create an override Create a new override that takes priority over the deployed version: ```ts const result = await prompts.createOverride("customer-support", { textContent: "New prompt template: Hello {{customerName}}!", model: "gpt-4o-mini", commitMessage: "Shorter prompt", }); ``` ### Update an override ```ts await prompts.updateOverride("customer-support", { textContent: "Updated template: Hi {{customerName}}!", model: "gpt-4o", }); ``` ### Remove an override Remove the active override, reverting to the deployed version: ```ts await prompts.removeOverride("customer-support"); ``` ### Promote a version ```ts await prompts.promote("customer-support", 2); ``` ### All management methods | Method | Description | |--------|-------------| | `prompts.list()` | List all prompts in the current environment | | `prompts.versions(slug)` | List all versions for a prompt | | `prompts.resolve(slug, variables?, options?)` | Resolve a prompt by slug | | `prompts.promote(slug, version)` | Promote a version to current | | `prompts.createOverride(slug, body)` | Create an override | | `prompts.updateOverride(slug, body)` | Update the active override | | `prompts.removeOverride(slug)` | Remove the active override | | `prompts.reactivateOverride(slug, version)` | Reactivate a removed override | ## Overrides Overrides let you change a prompt's template or model from the dashboard or SDK without redeploying your code. When an override is active, `resolve()` returns the override version instead of the deployed version. ### How overrides work - Overrides take priority over the deployed ("current") version - Only one override can be active at a time - Creating a new override replaces the previous one - Removing an override reverts to the deployed version - Overrides are environment-scoped (dev, staging, production are independent) ### Creating an override (dashboard) 1. Go to the prompt detail page 2. Click **Create Override** 3. Edit the template text and/or model 4. Add an optional commit message 5. Click **Create override** ### Version resolution order When `resolve()` is called, versions are resolved in this order: 1. **Specific version** — if `{ version: N }` is passed 2. **Override** — if an override is active in this environment 3. **Label** — if `{ label: "..." }` is passed (defaults to `"current"`) 4. **Current** — the latest deployed version with the "current" label ## Dashboard ### Prompts list The prompts list page shows all prompts in the current environment with the current or override version, default model, and a usage sparkline. ### Prompt detail Click a prompt to see: - **Template panel** — the prompt template for the selected version - **Details tab** — slug, description, model, config, source file, and variable schema - **Versions tab** — all versions with labels, source, and commit messages - **Generations tab** — every AI generation that used this prompt, with live polling - **Metrics tab** — token usage, cost, and latency charts ### AI span inspectors When you use `toAISDKTelemetry()`, AI generation spans in the run trace get a custom inspector showing: - **Overview** — model, provider, token usage, cost, input/output preview - **Messages** — the full message thread - **Tools** — tool definitions and tool call details - **Prompt** — the linked prompt's metadata, input variables, and template content ## Type utilities ```ts import type { PromptHandle, PromptIdentifier, PromptVariables } from "@trigger.dev/sdk"; type Id = PromptIdentifier; // "customer-support" type Vars = PromptVariables; // { customerName: string; plan: string; issue: string } ```