ChanlChanl
Knowledge & Memory

Agent warmup: preload customer context before the first word

Most agents ask for information they already have. Here's how to preload the right customer context before an interaction starts, so your agent sounds like it knows the customer from word one.

DGDean GroverCo-founderFollow
June 22, 2026
12 min read
Timeline diagram showing customer context loading in parallel with call connection, before the first agent utterance

The customer says "I'm calling about my order" and your agent says "Sure, could you give me your account number?" The customer has called three times this week. Their order number is in the CRM. Their last ticket is still open. The agent has access to all of it. It just hasn't loaded it yet.

Warmup is the period between "interaction started" and "agent has context." For most agents, it's longer than it should be and affects more of the conversation than teams realize. The fix isn't complicated, but it does require thinking about context loading as a first-class part of your agent's architecture.

Why the first 10 seconds matter most

The first few seconds of a customer interaction set the tone for everything that follows. When an agent sounds like it knows the customer from the first exchange, the customer starts from a position of trust. When it asks for information the company already has, it signals the opposite: that this is a system that doesn't remember them, doesn't value their time, and will probably make them repeat themselves.

The practical consequence is that agents that ask for known information consistently get worse customer satisfaction scores, even when they eventually resolve the issue. The first impression is sticky.

The good news: nearly everything you need to know about a customer before they speak can be loaded from your existing systems in under 500 milliseconds. The only question is whether you start loading before the first exchange or after.

What warmup means in agent systems

Warmup is the deliberate process of preloading customer context before or immediately when an interaction begins, so the agent has what it needs before it needs it.

In a well-designed system, warmup starts the moment you know an interaction is about to happen. For voice, that's when the call is connected. For chat, it's when the user opens the session. For an email agent, it's when the email arrives in the queue. In each case, you have a window between "something is about to happen" and "the agent needs to respond." Warmup fills that window.

What goes into a warmup payload depends on your product, but a typical CX agent warmup includes account basics (name, tier, status), recent interaction history, active support tickets, current orders, and customer preferences. The goal isn't to load everything. It's to load the things the agent is most likely to need in the first two minutes.

The warmup trigger

The warmup trigger is the signal that tells your system to start loading context. Getting the trigger right matters more than getting the payload right, because everything downstream depends on starting early enough.

For voice calls, the trigger is an inbound webhook from your telephony provider. When VAPI or Twilio fires a call.started event, the call hasn't connected to the agent yet. You have a window of several hundred milliseconds before the agent needs to speak.

warmup-trigger.ts·typescript
// VAPI webhook handler
app.post('/webhooks/vapi', async (req, res) => {
  const event = req.body;
 
  if (event.type === 'call.started') {
    // Fire and forget -- warmup runs in parallel with call setup
    preloadContext(event.call.id, event.call.customer.number).catch(console.error);
  }
 
  res.sendStatus(200);
});
 
async function preloadContext(callId: string, phoneNumber: string): Promise<void> {
  const customer = await lookupCustomerByPhone(phoneNumber);
  if (!customer) return; // Unknown number -- no warmup, agent will ask
 
  // Load tier 1 and tier 2 in parallel
  const [account, recentCalls, activeTickets] = await Promise.all([
    crm.getAccount(customer.id),
    callHistory.getRecent(customer.id, { limit: 5 }),
    helpdesk.getActiveTickets(customer.id),
  ]);
 
  // Store under the call ID so the agent can retrieve it at session start
  await chanl.memory.set({
    key: `warmup:${callId}`,
    value: { customer, account, recentCalls, activeTickets },
    ttl: 300, // 5-minute TTL -- call won't last longer than this
  });
}

For chat, the trigger is the session initialization event. If the user is logged in, you have their identity immediately. If not, you can use a session token to correlate with previous anonymous sessions.

For email agents, you have even more time. Email agents typically have seconds to minutes before they need to reply. Warmup can be more thorough, pulling full account history and detailed issue context that would be too slow for a voice call.

Prioritizing the warmup payload

Not all context is equally urgent. Some data is critical to the first sentence. Some is only needed if the conversation goes in a specific direction. Structure your warmup in priority tiers so the most important data is always ready first.

Tier 1 (target: under 100ms): Must be ready before the agent starts. Customer name, account status, whether there's an active critical ticket. This is the data the agent uses in its very first response.

Tier 2 (target: under 500ms): Should be ready before the first substantive exchange. Recent call history, current orders, known issues. Most conversations don't start from "Hi." They start from context the customer expects you to have.

Tier 3 (on-demand): Fetch when needed. Historical data from more than 30 days ago, detailed transaction logs, archived tickets. Load these only if the conversation heads in that direction.

tiered-warmup.ts·typescript
async function preloadContext(callId: string, customerId: string): Promise<void> {
  // Tier 1: fire immediately, must complete fast
  const tier1 = Promise.all([
    crm.getAccountStatus(customerId),        // Under 20ms from cache
    helpdesk.getCriticalTickets(customerId), // Under 30ms
  ]).then(([status, criticalTickets]) =>
    chanl.memory.set({
      key: `warmup:${callId}:tier1`,
      value: { status, criticalTickets },
      ttl: 300,
    })
  );
 
  // Tier 2: start immediately, best-effort before first exchange
  const tier2 = Promise.all([
    callHistory.getRecent(customerId, { limit: 5 }),
    orders.getActiveOrders(customerId),
    crm.getFullProfile(customerId),
  ]).then(([recentCalls, orders, profile]) =>
    chanl.memory.set({
      key: `warmup:${callId}:tier2`,
      value: { recentCalls, orders, profile },
      ttl: 300,
    })
  );
 
  await tier1; // Block until tier 1 is ready
  tier2.catch(console.error); // Tier 2 loads in background
}

The agent retrieves tier 1 before its first response and tier 2 before its second. By the time the conversation is underway, both tiers are in memory and the agent has full context.

The latency versus freshness tradeoff

Warmup introduces a tension: you want to load context as early as possible, but loading early means the data might be slightly stale by the time the agent uses it.

For most CX use cases, a 5-minute staleness window is acceptable. A customer's open ticket from 4 minutes ago is still the right ticket to reference. The exception is data that changes in real time: payment processing status, live inventory, active shipment tracking. These need a different strategy.

Lazy loading for real-time data: Don't include it in warmup. When the agent needs it, fetch it at that moment. The first call is slower, but subsequent calls can be cached within the session.

Dual-layer caching for frequently changing data: Cache a "last known good" version with a short TTL (30-60 seconds) and refresh in the background. The agent gets a fast response from cache, and the cache self-heals between calls.

dual-layer-cache.ts·typescript
async function getShipmentStatus(orderId: string): Promise<ShipmentStatus> {
  // Serve from cache immediately
  const cached = await redis.get(`shipment:${orderId}`);
  if (cached) {
    // Refresh in the background without blocking
    refreshShipmentStatus(orderId).catch(() => {});
    return JSON.parse(cached) as ShipmentStatus;
  }
 
  // Cache miss: fetch live and cache for 60 seconds
  const status = await shipping.getStatus(orderId);
  await redis.setEx(`shipment:${orderId}`, 60, JSON.stringify(status));
  return status;
}

Making warmup part of your system prompt

Once the warmup payload is loaded, the agent needs to actually use it. The most direct path: inject a "customer context" block into the system prompt before the agent's first response.

system-prompt-builder.ts·typescript
import { Chanl } from '@chanl/sdk';
 
const chanl = new Chanl({ apiKey: process.env.CHANL_API_KEY });
 
async function buildSystemPrompt(callId: string): Promise<string> {
  const [tier1, tier2] = await Promise.all([
    chanl.memory.get({ key: `warmup:${callId}:tier1` }),
    chanl.memory.get({ key: `warmup:${callId}:tier2` }),
  ]);
 
  const contextBlock = tier1 ? `
## Customer context
- **Name**: ${tier1.status?.name}
- **Account status**: ${tier1.status?.accountStatus}
- **Open critical tickets**: ${tier1.criticalTickets?.map((t: { id: string; subject: string }) => `#${t.id}: ${t.subject}`).join(', ') || 'None'}
${tier2 ? `- **Last contact**: ${tier2.recentCalls?.[0]?.date}: ${tier2.recentCalls?.[0]?.summary}
- **Active orders**: ${tier2.orders?.map((o: { id: string; status: string }) => `${o.id} (${o.status})`).join(', ') || 'None'}` : ''}
 
Do not ask for information already present in the customer context above.
` : '';
 
  return `${BASE_SYSTEM_PROMPT}\n${contextBlock}`;
}

The instruction "do not ask for information already present in the customer context" is load-bearing. Without it, agents will often still ask for the account number or confirm details the customer already told them, even when the warmup context clearly contains the answers. The instruction anchors the agent to the preloaded data.

Customer service representative

Customer Memory

4 memories recalled

Sarah Chen
Premium
Last call
2 days ago
Prefers
Email follow-up
Session Memory

“Discussed upgrading to Business plan. Budget approved at $50k. Follow up next Tuesday.”

85% relevance

Measuring if warmup is actually working

Two metrics tell you whether warmup is having the impact you want.

Time-to-first-contextual-reference: How many seconds into the interaction before the agent mentions something specific about the customer (their name, their recent order, their open ticket)? Lower is better. If this is consistently above 30 seconds, the agent isn't using warmup data effectively, even if the data loaded correctly.

"Can I have your account number" rate: What percentage of calls include the agent asking for information it already had in the warmup context? This should trend toward zero for customers with successful warmup loads. If it stays high, the problem is usually context injection: the data loaded but didn't make it into the system prompt in a usable form.

Chanl's conversation analytics tracks both of these. The time-to-first-contextual-reference shows up in conversation intelligence dashboards alongside your warmup load success rate so you can see the correlation directly.

Testing warmup before it goes live

Warmup failures are silent in production. If context doesn't load, the agent just sounds uninformed. It doesn't error out. That silence makes warmup failures easy to miss in QA if you don't test for them explicitly.

Three scenarios to add to your pre-production checklist.

Warmup hit: A customer with full history starts a conversation. Assert the agent greets them by name within the first two exchanges and references their most recent interaction without being asked.

Warmup miss (unknown caller): A customer with no account starts a conversation. Assert the agent handles the absence of warmup context gracefully and asks for the right identifying information rather than failing silently or hallucinating account details.

Partial failure: Simulate a slow CRM response that causes tier 2 to not load before the conversation starts. Assert the agent starts with tier 1 context and doesn't fail when tier 2 is missing. Tier 2 should load in the background and become available by the second exchange.

Chanl's scenario testing lets you run these as automated scenarios with synthetic caller personas that have defined warmup states. You can run the same suite against every deployment so warmup regressions don't reach production.

What a production warmup stack looks like

The pieces connect in a specific order, and getting that order right is what makes warmup reliable.

  1. Trigger: telephony webhook fires when a call starts (VAPI, Retell, Twilio all support this)
  2. Lookup: phone number or session token resolves to a customer ID
  3. Parallel load: tier 1 and tier 2 data load in parallel with defined priorities
  4. Storage: context stored in Chanl's memory under the call ID with a short TTL
  5. Injection: system prompt builder reads from memory and adds the context block
  6. Monitoring: conversation analytics tracks time-to-first-contextual-reference and context-ask rate

None of these pieces is novel. What makes warmup work is connecting them in the right order, making the loads parallel, and building in fallback paths for every tier.

If you're building on VAPI or Retell, the webhook and warmup load happen in your backend. Chanl handles the memory storage and retrieval so the context is available wherever your agent is running, regardless of which platform manages the conversation.

For a deeper look at how agent memory works across session types and time horizons, the memory system building guide covers the storage patterns. And if you're dealing with context that needs to carry across channels, where a customer starts on chat and moves to a voice call, channel context handoff covers that specific case.

The warmup window is short: a few hundred milliseconds for voice, a few seconds for chat. But it's the most impactful moment in any customer interaction. Load the right context before the first word, and every exchange that follows starts from a position of trust.

Give your agents a head start on every call

Chanl's memory system loads customer context before the conversation starts, so your agent sounds like it knows the customer from word one.

Start Building
DG

Co-founder

Building the platform for AI agents at Chanl — tools, testing, and observability for customer experience.

Learn Agentic AI

Weekly. Patterns for shipping agents that work. MCP, scorecards, regression tests, prompts, model comparisons.

500+ builders subscribed

Frequently Asked Questions