OfferPilot is a persistent AI negotiation backend built for the MongoDB Persistent Context Sprint Hackathon. It lets a voice agent identify a caller, recover an active negotiation, reason over policy and similar case memory, write durable checkpoints, and resume naturally after a dropped call.
The app runs as a Next.js API service with an in-browser demo console. It can run fully in seeded in-memory demo mode, or connect to MongoDB Atlas, Fireworks, and ElevenLabs when credentials are configured.
- Persistent caller identity and negotiation session recovery.
- LangGraph negotiation flow with explicit checkpoint writes.
- MongoDB collections for customers, cases, sessions, turns, offers, checkpoints, policy snippets, and case memories.
- Atlas Search for policy retrieval.
- Atlas Vector Search for similar resolved-case memory.
- Fireworks-powered reasoning with deterministic fallback logic.
- ElevenLabs text-to-speech and speech-to-text helper actions.
- Local demo UI plus shell scripts for repeatable scenario walkthroughs.
- Next.js 16 and React 19
- TypeScript
- MongoDB Atlas
- LangGraph
- Fireworks via the OpenAI-compatible SDK
- ElevenLabs voice APIs
Use Node.js >=22.0.0.
npm install
cp .env.example .env.local
npm run devOpen http://localhost:3000 for the demo UI.
The app falls back to seeded in-memory data when MONGODB_URI is not set, so the local demo works before any cloud services are configured.
Copy .env.example to .env.local and fill in only the services you want to enable.
MONGODB_URI=mongodb+srv://USERNAME:PASSWORD@CLUSTER.mongodb.net/?retryWrites=true&w=majority
MONGODB_DATABASE=offerpilot
FIREWORKS_API_KEY=
FIREWORKS_MODEL=accounts/fireworks/models/deepseek-v4-flash
FIREWORKS_EMBEDDING_MODEL=accounts/fireworks/models/qwen3-embedding-8b
ELEVENLABS_API_KEY=
ELEVENLABS_VOICE_ID=21m00Tcm4TlvDq8ikWAMNever commit .env.local or real service credentials. The repository ignores local env files and keeps only the placeholder .env.example.
All actions are handled by:
POST /api/offerpilotHealth check:
GET /api/offerpilot{
"action": "identify",
"phone": "+14155550123"
}{
"action": "resume",
"phone": "+14155550123"
}{
"action": "turn",
"sessionId": "sess_demo",
"text": "That $10 is too low. I missed dinner and this is not fair."
}{
"action": "disconnect",
"sessionId": "sess_demo"
}{
"action": "close",
"sessionId": "sess_demo",
"accepted": true
}{
"action": "tts",
"text": "I can increase the offer to $25."
}{
"action": "stt",
"audioBase64": "..."
}Default database:
offerpilotCollections:
customers
cases
negotiation_sessions
conversation_turns
offer_events
agent_checkpoints
policy_snippets
case_memoriesCore MongoDB usage:
customers: caller identity by phone number.negotiation_sessions: active, interrupted, and closed negotiation state.conversation_turns: immutable customer and agent transcript records.offer_events: audit trail for offered, accepted, and max-reached events.agent_checkpoints: durable LangGraph progress checkpoints.policy_snippets: policy text retrieved with Atlas Search.case_memories: similar outcomes retrieved with Atlas Vector Search.
Create an Atlas Search index named policy_search on policy_snippets.
Suggested searchable fields:
text
tags
categoryCreate an Atlas Vector Search index named case_memory_vector on case_memories.
Suggested vector settings:
path: embedding
filter field: case_typeConfigure ElevenLabs webhook tools against POST /api/offerpilot.
Recommended tools:
identify_customer -> { action: "identify", phone }
resume_session -> { action: "resume", phone }
record_turn -> { action: "turn", sessionId, text }
calculate_next_offer -> { action: "turn", sessionId, text }
mark_session_disconnected -> { action: "disconnect", sessionId }
close_session -> { action: "close", sessionId, accepted }Suggested voice agent guardrails:
You are OfferPilot, a calm and professional negotiation agent.
Never make an offer without calling the backend turn action.
Never exceed the maximum offer returned by the backend.
If resume_session returns prior context, acknowledge it naturally and continue.Start the app locally, then run:
./scripts/demo-chat.sh
./scripts/demo-sentiments.shTo point a script at a deployed app:
API_BASE=https://your-app.example.com ./scripts/demo-chat.shnpm run lint
npm run buildDeploy as a standard Next.js app on Vercel or another Node-compatible host.
Set production environment variables in the host dashboard. Use this webhook URL for ElevenLabs:
https://YOUR_APP_DOMAIN/api/offerpilotBefore pushing publicly:
- Keep
.env.localand other.env.*files untracked. - Commit
.env.examplewith placeholders only. - Do not commit
node_modules,.next,.wrangler,.vinext,dist, logs, or local hackathon notes. - Rotate any credentials that were ever stored locally before publishing the repository.