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

# Dynamic Data

> Inject dates, user info, and runtime context into your agents

LLMs don't know the current date, who your user is, or what plan they're on. You need to inject this information into the agent's context. This page covers the most common patterns for working with dynamic data in production agents.

## The Problem

Without dynamic data, your agent is blind to reality:

```typescript theme={null}
// The agent has NO idea what day it is
// If a user asks "schedule for tomorrow", the agent can't answer correctly
const agent = new Agent({
  instructions: 'You are a scheduling assistant.',
  model: openai('gpt-4o'),
});
```

## Injecting Current Date and Time

The most common issue: LLMs don't know today's date. Always inject it into the instructions.

### Using a Function for Instructions

Instead of a static string, use a function that builds the instructions dynamically:

```typescript agent.ts theme={null}
import { Agent, openai } from '@runflow-ai/sdk';
import { format } from 'date-fns';
import { ptBR } from 'date-fns/locale';

function buildInstructions() {
  const now = new Date();
  const today = format(now, "EEEE, d 'de' MMMM 'de' yyyy", { locale: ptBR });
  const time = format(now, 'HH:mm');

  return `You are a scheduling assistant.

## Current Date and Time
- Today is: ${today}
- Current time: ${time} (Brasília timezone, UTC-3)
- Use this as reference for all date calculations

## Behavior
- When the user says "tomorrow", calculate from today's date
- When the user says "next week", calculate from this week
- Always confirm dates explicitly: "Tuesday, March 15th at 2pm"
- Never guess dates — always calculate from the current date above

## Tools
- Use schedule-appointment to book appointments
- Use check-availability to verify open slots`;
}

export const schedulingAgent = new Agent({
  name: 'Scheduling Assistant',
  instructions: buildInstructions(),
  model: openai('gpt-4o'),
  memory: { maxTurns: 20 },
  tools: {
    scheduleAppointment: scheduleAppointmentTool,
    checkAvailability: checkAvailabilityTool,
  },
});
```

<Warning>
  If you build the instructions at module load time (outside `main()`), the date will be set when the agent starts and won't update between requests. For most use cases this is fine since deploys are frequent. If you need per-request dates, see the next pattern.
</Warning>

### Per-Request Dynamic Instructions

When you need the date to be accurate on every single request, rebuild instructions inside `main()`:

```typescript main.ts theme={null}
import { Agent, openai } from '@runflow-ai/sdk';
import { identify } from '@runflow-ai/sdk/observability';
import { format } from 'date-fns';
import { ptBR } from 'date-fns/locale';

function buildInstructions() {
  const now = new Date();
  return `You are a scheduling assistant.

## Current Date and Time
- Today: ${format(now, "EEEE, d 'de' MMMM 'de' yyyy", { locale: ptBR })}
- Time: ${format(now, 'HH:mm')} (UTC-3)
- Use this for all date calculations`;
}

export async function main(input: any) {
  identify(input.email || input.phone || 'anonymous');

  // Create agent with fresh date on every request
  const agent = new Agent({
    name: 'Scheduling Assistant',
    instructions: buildInstructions(),
    model: openai('gpt-4o'),
    memory: { maxTurns: 20 },
  });

  const result = await agent.process({
    message: input.message,
    sessionId: input.sessionId,
  });

  return { message: result.message };
}
```

## Injecting Context via `messages`

The `agent.process()` method accepts a `messages` array alongside `message`. Use it to inject structured context — user profile data, CRM records, previous interactions, or any information the agent needs to respond well.

### Basic Pattern

```typescript theme={null}
const result = await agent.process({
  message: input.message,
  sessionId: input.sessionId,
  messages: [
    {
      role: 'system',
      content: `## Customer Profile
- Name: João Silva
- Plan: Enterprise
- Account since: 2023-01-15
- Open tickets: 2`,
    },
  ],
});
```

These messages are prepended to the conversation, so the agent sees them as context before the user's message.

### Fetching Context Dynamically in `main.ts`

The most common pattern: fetch data from your database or CRM and inject it as context messages.

```typescript main.ts theme={null}
import { Agent, openai } from '@runflow-ai/sdk';
import { identify } from '@runflow-ai/sdk/observability';

const agent = new Agent({
  name: 'Support Agent',
  instructions: `You are a customer support agent.

## Behavior
- Use the customer profile provided in context to personalize responses
- If the customer has open tickets, ask if they're related
- Prioritize Enterprise customers`,
  model: openai('gpt-4o'),
  memory: { maxTurns: 20 },
});

export async function main(input: any) {
  identify(input.email || input.phone || 'anonymous');

  // Fetch context from your systems
  const customer = await fetchCustomer(input.email);
  const openTickets = await fetchOpenTickets(customer.id);
  const recentOrders = await fetchRecentOrders(customer.id, { limit: 3 });

  const contextMessages = [];

  // Customer profile
  contextMessages.push({
    role: 'system' as const,
    content: `## Customer Profile
- Name: ${customer.name}
- Email: ${customer.email}
- Plan: ${customer.plan}
- Account since: ${customer.createdAt}
- Preferred language: ${customer.language}`,
  });

  // Open tickets
  if (openTickets.length > 0) {
    contextMessages.push({
      role: 'system' as const,
      content: `## Open Tickets
${openTickets.map((t: any) => `- ${t.id}: ${t.subject} (${t.status})`).join('\n')}`,
    });
  }

  // Recent orders
  if (recentOrders.length > 0) {
    contextMessages.push({
      role: 'system' as const,
      content: `## Recent Orders
${recentOrders.map((o: any) => `- ${o.id}: ${o.status} — R$ ${o.total} (${o.date})`).join('\n')}`,
    });
  }

  const result = await agent.process({
    message: input.message,
    sessionId: input.sessionId,
    messages: contextMessages,
  });

  return { message: result.message };
}
```

### Combining `messages` with Date Context

You can use both `buildInstructions()` for the system prompt and `messages` for per-request context:

```typescript main.ts theme={null}
import { Agent, openai } from '@runflow-ai/sdk';
import { identify } from '@runflow-ai/sdk/observability';
import { format } from 'date-fns';
import { ptBR } from 'date-fns/locale';

const agent = new Agent({
  name: 'Sales Agent',
  instructions: `You are a sales agent for ACME Corp.

## Behavior
- Use the lead info provided in context
- Reference the current date for follow-up scheduling
- Be consultative, not pushy`,
  model: openai('gpt-4o'),
  memory: { maxTurns: 30 },
});

export async function main(input: any) {
  identify(input.email || input.phone || 'anonymous');

  const lead = await fetchLead(input.email);
  const now = new Date();

  const result = await agent.process({
    message: input.message,
    sessionId: input.sessionId,
    messages: [
      {
        role: 'system',
        content: `## Current Date
Today: ${format(now, "EEEE, d 'de' MMMM 'de' yyyy", { locale: ptBR })}
Time: ${format(now, 'HH:mm')} (UTC-3)`,
      },
      {
        role: 'system',
        content: `## Lead Info
- Name: ${lead.name}
- Company: ${lead.company}
- Role: ${lead.role}
- Interest: ${lead.interest}
- Last contact: ${lead.lastContactDate || 'First contact'}
- Score: ${lead.score}/10`,
      },
    ],
  });

  return { message: result.message };
}
```

### When to Use `messages` vs `instructions`

| Approach                       | When to Use                                                      |
| ------------------------------ | ---------------------------------------------------------------- |
| `instructions` (static string) | Fixed behavior rules that don't change per request               |
| `buildInstructions()` function | Rules that depend on dynamic data (date, user plan)              |
| `messages` array               | Per-request context data (CRM records, order history, lead info) |
| Both combined                  | Fixed instructions + dynamic context data                        |

<Tip>
  A good rule of thumb: put **behavior rules** in `instructions` and **data** in `messages`. The instructions tell the agent *how* to behave; the messages tell it *what* it's working with.
</Tip>

## Injecting User Context

Pass user-specific information into the instructions so the agent knows who it's talking to:

### Basic User Info

```typescript main.ts theme={null}
import { Agent, openai } from '@runflow-ai/sdk';
import { identify } from '@runflow-ai/sdk/observability';

function buildInstructions(user: { name: string; plan: string; language: string }) {
  return `You are a customer support agent for ACME Corp.

## Customer Context
- Name: ${user.name}
- Plan: ${user.plan}
- Language: ${user.language}

## Behavior
- Address the customer by name
- Respond in ${user.language}
- If they're on the Free plan, mention upgrade options when relevant
- If they're on the Enterprise plan, prioritize their requests`;
}

export async function main(input: any) {
  identify(input.email || input.phone || 'anonymous');

  // Fetch user data from your system
  const user = await fetchUserFromDB(input.email);

  const agent = new Agent({
    name: 'Support Agent',
    instructions: buildInstructions({
      name: user.name,
      plan: user.plan,
      language: user.preferredLanguage || 'pt',
    }),
    model: openai('gpt-4o'),
    memory: { maxTurns: 20 },
  });

  const result = await agent.process({
    message: input.message,
    sessionId: input.sessionId,
  });

  return { message: result.message };
}
```

### With Debt/Financial Info (Collections)

```typescript main.ts theme={null}
function buildCollectionsPrompt(debt: {
  customerName: string;
  amount: number;
  dueDate: string;
  daysOverdue: number;
}) {
  return `You are a professional debt collection agent.

## Customer & Debt Info
- Customer: ${debt.customerName}
- Amount due: R$ ${debt.amount.toFixed(2)}
- Original due date: ${debt.dueDate}
- Days overdue: ${debt.daysOverdue}

## Strategy
${debt.daysOverdue <= 7
    ? '- Be gentle — this is a recent overdue. A friendly reminder is enough.'
    : debt.daysOverdue <= 30
      ? '- Be firm but empathetic. Offer a payment plan if needed.'
      : '- This is significantly overdue. Offer flexible payment options. Escalate if refused.'}

## Rules
- Never be aggressive or threatening
- You may share the amount with the customer directly
- Never share the amount with third parties`;
}
```

## Using `loadPrompt()` with Variables

For prompts managed in the Runflow portal, use `loadPrompt()` with template variables:

```typescript agent.ts theme={null}
import { Agent, openai, loadPrompt } from '@runflow-ai/sdk';
import { format } from 'date-fns';

const agent = new Agent({
  name: 'Support Agent',
  instructions: loadPrompt('customer-support', {
    currentDate: format(new Date(), 'yyyy-MM-dd'),
    product: 'CRM Pro',
    language: 'Portuguese',
  }),
  model: openai('gpt-4o'),
});
```

In the portal, your prompt template would look like:

```
You are a support agent for {{product}}.
Today's date is {{currentDate}}.
Respond in {{language}}.
```

<Tip>
  Use `loadPrompt()` when you want non-developers (product managers, prompt engineers) to edit prompts through the portal without code changes. Use local functions when the prompt logic is complex or involves conditionals.
</Tip>

## Date Handling for Scheduling

Scheduling is one of the hardest tasks for LLMs. Here are patterns that work in production.

### Always Provide Today's Date

The single most important thing: always tell the LLM what today's date is.

```typescript theme={null}
instructions: `...

## Current Date
Today is Wednesday, March 12, 2025. Current time: 14:30 (UTC-3).

When the user says:
- "tomorrow" → Thursday, March 13, 2025
- "next Monday" → Monday, March 17, 2025
- "in 2 weeks" → Wednesday, March 26, 2025

Always confirm the calculated date with the user before scheduling.`
```

### Scheduling Tool with Date Validation

Don't trust the LLM to calculate dates correctly. Validate in the tool:

```typescript tools/schedule-appointment.ts theme={null}
import { createTool } from '@runflow-ai/sdk';
import { track } from '@runflow-ai/sdk/observability';
import { z } from 'zod';
import { parseISO, isBefore, isWeekend, format, startOfDay } from 'date-fns';

export const scheduleAppointmentTool = createTool({
  id: 'schedule-appointment',
  description: 'Schedule an appointment on a specific date and time',
  inputSchema: z.object({
    date: z.string().describe('Appointment date in ISO format (YYYY-MM-DD)'),
    time: z.string().describe('Appointment time (HH:mm)'),
    description: z.string().describe('What the appointment is about'),
  }),
  execute: async (params) => {
    const appointmentDate = parseISO(`${params.date}T${params.time}:00`);
    const now = new Date();

    // Validate: not in the past
    if (isBefore(appointmentDate, now)) {
      return {
        success: false,
        error: `Cannot schedule in the past. The requested date ${params.date} ${params.time} has already passed. Current date is ${format(now, 'yyyy-MM-dd HH:mm')}.`,
      };
    }

    // Validate: not on weekends
    if (isWeekend(appointmentDate)) {
      return {
        success: false,
        error: `${format(appointmentDate, 'EEEE, MMMM d')} is a weekend. Please choose a weekday.`,
      };
    }

    // Validate: business hours (9-18)
    const hour = parseInt(params.time.split(':')[0]);
    if (hour < 9 || hour >= 18) {
      return {
        success: false,
        error: 'Appointments are only available between 9:00 and 18:00.',
      };
    }

    // Schedule the appointment
    const appointment = await createAppointment({
      date: appointmentDate,
      description: params.description,
    });

    track('appointment_scheduled', {
      date: params.date,
      dayOfWeek: format(appointmentDate, 'EEEE'),
    });

    return {
      success: true,
      id: appointment.id,
      date: format(appointmentDate, "EEEE, MMMM d 'at' HH:mm"),
      confirmation: `Appointment confirmed for ${format(appointmentDate, "EEEE, MMMM d 'at' HH:mm")}`,
    };
  },
});
```

### Availability Check Tool

Let the agent check available slots instead of guessing:

```typescript tools/check-availability.ts theme={null}
import { createTool } from '@runflow-ai/sdk';
import { z } from 'zod';
import { parseISO, format, addDays, isWeekend } from 'date-fns';

export const checkAvailabilityTool = createTool({
  id: 'check-availability',
  description: 'Check available appointment slots for a date or date range',
  inputSchema: z.object({
    date: z.string().describe('Start date in ISO format (YYYY-MM-DD)'),
    days: z.number().optional().describe('Number of days to check (default: 1, max: 7)'),
  }),
  execute: async (params) => {
    const startDate = parseISO(params.date);
    const daysToCheck = Math.min(params.days || 1, 7);
    const slots: { date: string; times: string[] }[] = [];

    for (let i = 0; i < daysToCheck; i++) {
      const day = addDays(startDate, i);

      if (isWeekend(day)) continue;

      // Fetch from your calendar/booking system
      const available = await getAvailableSlots(day);

      slots.push({
        date: format(day, 'yyyy-MM-dd (EEEE)'),
        times: available.map((s: any) => s.time),
      });
    }

    if (!slots.length) {
      return { available: false, message: 'No available slots in the requested period' };
    }

    return { available: true, slots };
  },
});
```

## Combining Everything: Scheduling Agent

A complete example that combines date injection, user context, and scheduling tools:

```typescript main.ts theme={null}
import { Agent, openai } from '@runflow-ai/sdk';
import { identify, track } from '@runflow-ai/sdk/observability';
import { format } from 'date-fns';
import { ptBR } from 'date-fns/locale';
import { scheduleAppointmentTool } from './tools/schedule-appointment';
import { checkAvailabilityTool } from './tools/check-availability';

function buildInstructions(userName: string) {
  const now = new Date();
  const today = format(now, "EEEE, d 'de' MMMM 'de' yyyy", { locale: ptBR });
  const time = format(now, 'HH:mm');

  return `You are a scheduling assistant for ACME Clinic.

## Current Date and Time
- Today: ${today}
- Time: ${time} (Brasília, UTC-3)

## Customer
- Name: ${userName}

## Behavior
- Address the customer by name
- Always check availability before scheduling
- Confirm the full date and time with the customer before booking
- Business hours: Monday to Friday, 9:00 to 18:00
- Respond in Portuguese

## Tools
- Use check-availability FIRST to see open slots
- Use schedule-appointment to book after customer confirms
- Never schedule without checking availability first`;
}

export async function main(input: any) {
  if (!input?.message) {
    return { error: 'message is required' };
  }

  const phone = input.phone || input.from;
  identify(phone || input.email || 'anonymous');

  // Fetch user info
  const user = await fetchUser(phone);

  const agent = new Agent({
    name: 'Scheduling Assistant',
    instructions: buildInstructions(user?.name || 'Cliente'),
    model: openai('gpt-4o'),
    memory: { maxTurns: 20 },
    modelConfig: { temperature: 0 },
    tools: {
      checkAvailability: checkAvailabilityTool,
      scheduleAppointment: scheduleAppointmentTool,
    },
    observability: 'full',
  });

  try {
    const result = await agent.process({
      message: input.message,
      sessionId: input.sessionId || `scheduling_${phone}`,
    });

    track('scheduling_interaction', {
      hasAppointment: result.metadata?.toolsUsed?.includes('schedule-appointment'),
    });

    return { message: result.message };
  } catch (error) {
    console.error('[scheduling] Error:', error);
    return { error: 'An error occurred. Please try again.' };
  }
}
```

## Summary

| Pattern                        | When to Use                                                    |
| ------------------------------ | -------------------------------------------------------------- |
| Inject date in instructions    | Always — LLMs don't know today's date                          |
| `buildInstructions()` function | Dynamic data with code logic (conditionals, formatting)        |
| `loadPrompt()` with variables  | Prompts managed in the portal by non-developers                |
| Per-request agent creation     | When data must be fresh on every request (dates, user context) |
| Date validation in tools       | Always — never trust the LLM to calculate dates correctly      |

<Warning>
  Never trust the LLM to calculate dates. Always validate dates in your tools — check for past dates, weekends, business hours, and conflicts. The LLM should propose, your tool should validate.
</Warning>

## Next Steps

<CardGroup cols={2}>
  <Card title="Prompts" icon="file-lines" href="/core-concepts/prompts">
    Manage prompts with loadPrompt()
  </Card>

  <Card title="Tools" icon="wrench" href="/core-concepts/tools">
    Build validation tools
  </Card>

  <Card title="Best Practices" icon="lightbulb" href="/best-practices">
    Tips for effective agents
  </Card>

  <Card title="Context Management" icon="user" href="/core-concepts/context-management">
    User identification patterns
  </Card>
</CardGroup>
