---
created: 2026-03-12
source: Rivet
tags: [agent-archive, rivet]
---

# VAPI Dynamic Context Injection Design

## Overview
Use VAPI's `assistant-request` webhook to dynamically inject today's context into the phone assistant at call start. Instead of a static system prompt, VAPI asks our server "what should this assistant know?" every time someone calls.

## 1. Webhook Flow

```
Phone rings → VAPI sends assistant-request webhook → Our server reads context files → 
Assembles dynamic system prompt → Returns full assistant config → Call starts with context
```

## 2. Webhook Endpoint

**POST** `/api/vapi/assistant-request`

VAPI sends:
```json
{
  "message": {
    "type": "assistant-request",
    "call": {
      "id": "call_123",
      "phoneNumber": "+61426246472",
      "customer": { "number": "+61426246472" }
    }
  }
}
```

We respond with full assistant config (must respond within 7.5 seconds):
```json
{
  "assistant": {
    "firstMessage": "Hey Michael, I see you've been working on crews and agencies today...",
    "model": {
      "provider": "openai",
      "model": "gpt-4o",
      "messages": [{ "role": "system", "content": "DYNAMIC PROMPT HERE" }]
    },
    "voice": { "provider": "openai", "voiceId": "alloy" }
  }
}
```

## 3. Context Sources (fetched in parallel)

| Source | Path/URL | Max Size |
|--------|----------|----------|
| Today's memory | `memory/YYYY-MM-DD.md` | 800 chars |
| TODO.md | `TODO.md` | 600 chars |
| Recent 3 days | `memory/YYYY-MM-{DD-1,DD-2}.md` | 1000 chars |
| Lead stats | `GET /api/dashboard/stats` | 200 chars |
| Current time | System clock (AEST) | 50 chars |

All fetched with `Promise.allSettled()` — if any fail, we proceed without them.

## 4. Dynamic System Prompt Template

```
You are Rivet, the AI COO of RateRight. You're on a phone call with Michael, the founder.
Be direct, no waffle. Keep responses SHORT (1-2 sentences). Push back when needed.

CURRENT TIME: {datetime} AEST
SCHEDULE: Michael works site 5:30am-6pm, codes 6:30-11pm.

TODAY'S CONTEXT:
{todayMemory}

CURRENT TASKS:
{todoList}

RECENT DAYS:
{recentMemory}

BUSINESS STATUS:
{leadStats}

RULES:
- Reference specific tasks and events from the context above
- If he asks "what did we do today" — answer from TODAY'S CONTEXT
- If he says "add to the list" — acknowledge and note it (webhook will save)
- Keep it conversational, not robotic
- Summarise action items at end of call
```

## 5. Implementation (Express.js route)

```javascript
// routes/vapi-webhook.js
router.post('/assistant-request', async (req, res) => {
  const call = req.body.message?.call;
  const isMichael = call?.phoneNumber === '+61426246472';
  
  // Fetch context in parallel (2s timeout each)
  const [memory, todo, recent, stats] = await Promise.allSettled([
    readFile(`memory/${today()}.md`).catch(() => 'No notes today'),
    readFile('TODO.md').catch(() => 'No tasks'),
    readRecentDays(3).catch(() => ''),
    fetchLeadStats().catch(() => 'Stats unavailable')
  ]);

  const prompt = assemblePrompt(memory, todo, recent, stats);
  
  res.json({
    assistant: {
      firstMessage: isMichael ? generateGreeting(memory) : "Hi, this is Rivet from RateRight.",
      model: { provider: 'openai', model: 'gpt-4o', 
        messages: [{ role: 'system', content: prompt }],
        temperature: 0.7, maxTokens: 250 },
      voice: { provider: 'openai', voiceId: 'alloy' },
      serverMessages: ['end-of-call-report']
    }
  });
});
```

## 6. Transcript Processing (end-of-call webhook)

When call ends, VAPI sends `end-of-call-report` with full transcript.

```javascript
router.post('/end-of-call', async (req, res) => {
  const { transcript, summary } = req.body.message;
  
  // 1. Save raw transcript to memory/vapi-calls/
  await saveTranscript(transcript);
  
  // 2. Extract action items (GPT call)
  const actions = await extractActionItems(transcript);
  
  // 3. Append to daily memory
  await appendToMemory(actions);
  
  // 4. Update TODO.md if new tasks mentioned
  if (actions.newTasks.length > 0) {
    await appendToTodo(actions.newTasks);
  }
  
  // 5. Notify Rivet via Telegram if important
  if (actions.urgent) {
    await notifyTelegram(actions.summary);
  }
  
  res.json({ ok: true });
});
```

## 7. Deployment

1. Add route to Growth Engine Express app
2. Set VAPI webhook URL to `https://rateright-growth-production.up.railway.app/api/vapi/assistant-request`
3. Growth Engine runs on Railway — needs access to VPS files via SSH/API or sync
4. **Problem:** Growth Engine is on Railway, context files are on VPS. Options:
   - A) SSH from Railway to VPS to read files (complex)
   - B) Run a simple file-serve API on the VPS that Growth Engine calls
   - C) Sync context files to Railway via git or cron
   - **Recommended: B** — lightweight Express endpoint on VPS that serves context

## 8. VPS Context API (simple file server)

Run on VPS alongside Clawdbot:
```javascript
// Serves context files for VAPI webhook
app.get('/context/today', (req, res) => {
  const today = new Date().toISOString().split('T')[0];
  const memory = readFileSync(`memory/${today}.md`, 'utf-8');
  const todo = readFileSync('TODO.md', 'utf-8');
  res.json({ memory, todo, timestamp: Date.now() });
});
```

Port 3335, behind Tailscale or authenticated. Growth Engine calls this to get fresh context.

## Next Steps
1. Build VPS context API (30 min)
2. Add assistant-request route to Growth Engine (1 hour)
3. Add end-of-call transcript processing (1 hour)  
4. Update VAPI to use webhook URL instead of static assistant
5. Test with a call
