docs/ai/prompts.mdx
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:
Use prompts.define() to create a prompt with typed variables:
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.`,
});
| 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 |
Templates use Mustache-style placeholders:
{{variableName}} — replaced with the variable value{{#conditionalVar}}...{{/conditionalVar}} — content only included if the variable is truthyexport 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}}`,
});
Call .resolve() on the handle returned by define():
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"]
Resolve any prompt by slug without needing a handle. Pass the prompt handle as a type parameter for full type safety:
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<typeof supportPrompt>("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<string, unknown> variables.
You can resolve a specific version or label:
// 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.
The resolved prompt integrates with the Vercel AI SDK via toAISDKTelemetry(). This links AI generation spans to the prompt in the dashboard.
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 };
},
});
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 };
},
});
Pass additional metadata to toAISDKTelemetry() that will appear on the generation span:
const result = await generateText({
model: anthropic("claude-sonnet-4-5"),
prompt: resolved.text,
...resolved.toAISDKTelemetry({
"task.type": "summarization",
"customer.tier": "enterprise",
}),
});
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.
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),
});
},
});
Returns an options object ready to spread into streamText(). When a prompt is stored via chat.prompt.set(), it includes:
system — the compiled prompt textmodel — resolved via the registry when providedtemperature, maxTokens, etc. — from the prompt's configexperimental_telemetry — links generations to the prompt in the dashboard// 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: { ... } }
Access the stored prompt from anywhere in the run:
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:
chat.prompt.set("You are a helpful assistant.");
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.
const allPrompts = await prompts.list();
const versions = await prompts.versions("customer-support");
Create a new override that takes priority over the deployed version:
const result = await prompts.createOverride("customer-support", {
textContent: "New prompt template: Hello {{customerName}}!",
model: "gpt-4o-mini",
commitMessage: "Shorter prompt",
});
await prompts.updateOverride("customer-support", {
textContent: "Updated template: Hi {{customerName}}!",
model: "gpt-4o",
});
Remove the active override, reverting to the deployed version:
await prompts.removeOverride("customer-support");
await prompts.promote("customer-support", 2);
| 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 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.
When resolve() is called, versions are resolved in this order:
{ version: N } is passed{ label: "..." } is passed (defaults to "current")The prompts list page shows all prompts in the current environment with the current or override version, default model, and a usage sparkline.
Click a prompt to see:
When you use toAISDKTelemetry(), AI generation spans in the run trace get a custom inspector showing:
import type { PromptHandle, PromptIdentifier, PromptVariables } from "@trigger.dev/sdk";
type Id = PromptIdentifier<typeof supportPrompt>; // "customer-support"
type Vars = PromptVariables<typeof supportPrompt>; // { customerName: string; plan: string; issue: string }