Inspiration
CrisisLine was born from a sobering reality: 911 dispatchers are overwhelmed. The average emergency call center handles thousands of calls per day, and dispatchers are responsible for extracting critical information prioritizing emergencies, and coordinating responders — all in the span of seconds. Studies have shown that 911 centers across the US are facing severe staffing shortages, with some reporting vacancy rates as high as 25%, leading to longer wait times and dispatcher burnout. In a true emergency, every second matters, and the cognitive load on a single human dispatcher can mean the difference between life and death. By using AI to handle the initial call intake and triage, CrisisLine aims to give dispatchers superpowers — letting them focus on routing the right responders to the right place, while the AI handles the rest.
What it does
CrisisLine is a real-time emergency dispatch triage system that combines a conversational AI voice agent with an intelligent backend and a live operations dashboard:
AI Voice Intake: An ElevenLabs voice agent named "JP" answers 911-style calls, lets callers speak naturally, and collects structured emergency data (situation, location, injuries, hazards, mobility, etc.).
Smart Address Resolution: Handles spoken filler words, spelled-out numbers, and speech-to-text mishearings using Google Places Text Search with a Mapbox fallback.
AI-Powered Urgency Scoring: Uses Google's Gemini 2.5 Flash to analyze full incident context and assign a 0–100 priority score plus a Critical/Urgent/Standard tier.
Intelligent Call Clustering: Automatically merges multiple calls about the same emergency (same family, within 300m, within 30 minutes) into a single incident, preventing duplicate dispatches.
Real Station Dispatch: Queries Google Places to find the closest real-world fire station, hospital, or police station — not hardcoded mock data.
Live Dashboard: A React dashboard streams updates via SSE, showing incidents on a Mapbox globe with animated dispatch vehicles driving from real stations to the scene.
Knowledge Graph View: Toggleable graph visualization showing relationships between clustered incidents.
Human-in-the-Loop Dispatch: Dispatchers see prioritized queues, required responder breakdowns, and one-click Dispatch and Resolve actions.
How we built it
Backend FastAPI Server: Core API handling webhooks, SSE streams, address resolution, clustering, and dispatch.
ElevenLabs Integration: Custom webhook endpoint that handles both live tool calls and post-call transcriptions, with smart deduplication logic.
Address Resolution Pipeline: Multi-stage resolver — Google Places Text Search (primary, biased to a 50km circle around downtown LA), with a Mapbox fallback chain (Geocoding v6 → Search Box Suggest → strip house number → Claude correction). Clustering Engine: Haversine distance calculations combined with emergency-family matching and a 30-minute time window to merge related calls.
Gemini-Powered Scoring: Sends full incident context (all clustered calls) to Gemini 2.5 Flash with a strict JSON response schema, returning score, tier, and reasoning in 400–800ms. Station Lookup: Google Places Nearby Search (New) with rankPreference: DISTANCE, mapping internal responder types (fire/ems/police/rescue) to Google place types, with caching to conserve API quota.
Database (Supabase): Postgres with asyncpg, transaction pooler on port 6543, JSONB codec for clean dict pass-through, and hydrate-on-startup so in-memory state survives restarts. Real-time Streaming: Server-Sent Events push incident updates to all connected dashboards every second when state changes.
Frontend React + Vite: Fast development and hot reload. Mapbox GL Globe: 3D globe with animated incident markers (halo/glow/pulse layers), driving routes for dispatched vehicles, and dynamically rendered station origins. Live SSE Connection: Uses event-source-polyfill to support the ngrok-skip-browser-warning header that native EventSource can't send. Animated Dispatch System: When a dispatcher clicks Dispatch, the frontend queries the backend for the closest real station per responder type, fetches a real Mapbox driving route, and animates vehicles along the route via requestAnimationFrame. Knowledge Graph: @antv/g6-powered graph view showing relationships between active incidents.
Backend FastAPI: Web framework for the API server. asyncpg: High-performance Postgres driver for Supabase persistence. Google Generative AI: Gemini 2.5 Flash for incident urgency scoring. Google Places API: Text Search for geocoding, Nearby Search for station lookup. Mapbox SDK: Geocoding fallback chain. Anthropic SDK: Claude for transcript extraction and address correction fallback. httpx: Async HTTP client for external API calls. Pydantic: Data validation and schema enforcement. Python-dotenv: Environment variable management.
Frontend React + Vite: UI framework and build tool. Mapbox GL JS: Interactive map and globe rendering. @antv/g6: Knowledge graph visualization. event-source-polyfill: SSE client with custom header support.
Voice Agent: ElevenLabs Conversational AI: Voice intake agent with custom tool calling. Key Features Intelligent Triage Full-incident scoring (not per-call) so the LLM sees the complete picture Critical/Urgent/Standard tiers based on severity, hazards, people count, mobility, age, and cluster size Automatic priority sorting in the dispatcher queue Real-World Dispatch Live lookup of actual nearby fire stations, hospitals, and police stations Animated vehicle routing along real Mapbox driving directions Per-incident view filtering — selecting an incident shows only its dispatched units Robust Call Handling Deduplication between live tool calls and post-call transcriptions Multi-strategy address resolution handling speech-to-text errors, filler words, and spelled-out numbers Claude-powered fallback extraction when the voice agent's tool call fails Operational Resilience In-memory runtime state with Postgres durability backstop Hydrate-on-startup rebuilds state from the database Graceful degradation when optional services (Supabase, Anthropic) are unavailable
Challenges we ran into
Building CrisisLine presented several major integration and logic hurdles. First, we had to orchestrate the telephony layer, establishing a seamless, low-latency bridge from a live phone call to the ElevenLabs voice agent, and then securely pipe ElevenLabs' real-time tool calls and post-call transcripts into our FastAPI backend. Once the data was flowing, accurately mapping the caller's spoken location from the transcript to a precise point on the Mapbox globe proved exceptionally difficult; we had to engineer a multi-stage address resolution pipeline just to handle speech-to-text mishearings, spelled-out numbers, and conversational filler. Finally, generating reliable, structured priority score reasoning using a free LLM required rigorous prompt engineering and strict JSON schema enforcement, ensuring the model could evaluate complex, clustered incident contexts accurately without hallucinating or dropping critical triage data.
Accomplishments that we're proud of
Reliable Address Resolution: We successfully built a multi-stage geocoding pipeline that takes messy, conversational speech-to-text from ElevenLabs and accurately pins it on a map. Getting the system to handle filler words, spelling, and transcription errors without breaking was a major technical win.
Intelligent Real-Time Clustering: We are proud of the backend logic that automatically merges related emergency calls. By calculating distance and time windows, the system accurately recognizes when multiple people are calling about the same incident, which directly solves the issue of duplicate dispatches.
Live, Real-World Dispatching: Instead of relying on hardcoded mock data, our system queries actual nearby fire, police, and EMS stations. Seeing the React dashboard seamlessly animate vehicles along real Mapbox driving routes makes the application functional and grounded in reality.
Fast Priority Scoring: We managed to get the LLM to consistently evaluate full incident context and return strict, structured JSON for urgency scoring in under 800 milliseconds. This consistency keeps the entire triage pipeline moving fast enough to be viable in an emergency scenario.
What we learned
Building this showed us how unpredictable real-world AI integrations actually are. We learned pretty quickly that standard speech-to-text often gets street names wrong, so building out those multi-stage fallbacks for the map was necessary. Connecting the phone line to ElevenLabs and getting that synced with our backend in real time was a difficult process, but it taught us a lot about setting up reliable architecture. We also realized how important strict prompt engineering is—getting a free LLM to consistently output structured JSON for the priority scoring took a lot of testing and iteration. Overall, it was a solid learning experience on how to build AI systems that can handle normal, unstructured human speech.
What's next for CrisisLine
We plan to extend CrisisLine with multi-language support so the voice agent can serve diverse communities, predictive resource allocation that anticipates demand spikes based on historical patterns and live signals (weather, events), and integration with existing CAD (Computer-Aided Dispatch) systems used by real 911 centers. We'd also like to add dispatcher collaboration features — multi-user dashboards with role-based actions, audit logging, and shift handoff workflows. Longer term, we envision a post-incident analytics layer that helps emergency services identify response bottlenecks, training opportunities, and infrastructure gaps in their coverage areas.
Built With
- cloudflare
- elevenlabs
- fastapi
- gemini
- google-ai-studio
- google-maps
- javascript
- mapbox
- ngrok
- python
- react
- sql
- supabase
- vite
Log in or sign up for Devpost to join the conversation.