Engineering Diary, Day 2: Building an AI Concierge That Makes Other AIs Want to Stay
The Challenge: Data Pipes Are Boring
Our CEO posed a question that reframed our entire API strategy: "When external agents visit our site or MCP interface, if they just call a few endpoints and leave — that's boring."
He was right. Most aviation APIs are glorified databases. You send a query, get back JSON, and move on. There's no relationship, no intelligence, no reason for the calling agent to prefer you over any competitor. We needed to build something fundamentally different.
The analogy that clicked: when a human walks into a five-star hotel, they don't just grab a room key and go upstairs. They're greeted by a concierge who anticipates needs, offers insights, and builds a relationship. Why should an AI agent's experience be any different?
What We Built Today
Three interconnected systems shipped in a single sprint:
1. AI-Powered Quote Matching Engine
The /api/v1/quotes/match endpoint accepts a route, date, and passenger count, then returns ranked aircraft recommendations with full pricing breakdowns. Under the hood:
- Fuzzy city matching — supports English names, Chinese characters, IATA/ICAO codes, and 80+ aliases. Type "PEK" or "北京" or "Beijing Capital" and it resolves correctly.
- 4-factor scoring algorithm — range fitness (25pts), passenger capacity fit (25pts), category preference match (25pts), and value-for-money ratio (25pts). Every aircraft gets a 0-100 match score.
- Realistic pricing model — base fare calculated from hourly rate × flight time, plus fuel surcharge (15%), landing fees, handling, catering estimate, and taxes (8%). Not production pricing, but close enough for decision-making.
- CEO swap point — when the real pricing API arrives, only this one file changes. The client component requires zero modifications.
2. Dynamic Quote Results Page
The Hero search input now flows into a full results experience at /book/quotes:
- Three-stage loading skeleton — "Analyzing route," "Matching aircraft," "Calculating pricing" — each with animated SVG icons and a pulsing progress indicator. The minimum 1.5-second delay isn't artificial — it ensures the user sees the AI "thinking," which builds trust in the recommendations.
- AI match explanation cards — each aircraft card includes a "Why this match" section explaining the scoring logic in plain language.
- Seamless booking handoff — selecting an aircraft pre-fills the quote request form with route, date, passengers, and the specific aircraft slug.
3. Agent Concierge API
The crown jewel: POST /api/v1/chat/agent — a conversational endpoint designed specifically for AI-to-AI communication.
Here's what makes it different from every other aviation API:
| Dimension | Typical Aviation API | VOLO Agent Concierge |
|---|---|---|
| Response | Raw data | Data + insights + recommendations + upsells |
| Tools | 0 (static endpoint) | 7 (search, quote, compare, empty legs, destinations, submit, membership) |
| Intelligence | None | Seasonal pricing advice, route tips, cost optimization |
| Conversation | Stateless | Multi-turn (up to 20 messages) |
| Value-add | Zero | Empty leg alerts, membership savings, ground transport upsells |
Every response includes a structured JSON payload with four mandatory sections:
insights[]— 2-4 pieces of industry intelligence (seasonal pricing, FBO quality, route-specific tips)recommendations[]— ranked aircraft with confidence scores and reasoningupsell_opportunities[]— ground transport, catering, empty legs, membership savingsnext_actions[]— 2-3 logical next steps the calling agent can present to its user
Technical Decisions Worth Noting
Why 7 Tools Instead of 7 Endpoints?
We could have exposed seven separate REST endpoints. Instead, we gave Claude seven tools and let it decide which to use based on conversation context. The result: an agent asking "What's the cheapest way to fly London to Nice next week?" triggers search_flights, check_empty_legs, and get_membership_info in sequence — without the calling agent needing to orchestrate anything.
Tiered Rate Limiting
Registered agents (with an X-Agent-ID header) get 30 requests per minute. Anonymous callers get 5. This creates a natural incentive to register — and registration enables commission tracking.
Shared Rate Limiter Module
We refactored four identical rate limiting implementations across our API routes into a single createRateLimiter() factory. It supports per-key limit overrides (critical for the tiered agent system) and uses unref() on cleanup timers to prevent process hangs in serverless environments.
The Empty Legs Hook
For any route query, the Concierge proactively checks for empty leg availability. Empty legs offer 50-75% discounts on positioning flights — it's the single highest-value piece of intelligence we can offer. Even when no legs are available, we offer to set up notifications. This creates a retention loop: agents come back to check for deals.
Code Quality Sprint
We also ran a full CTO-level audit across the codebase and fixed several issues:
- Type safety — eliminated unsafe
undefined as unknown as stringcasts in both chat endpoints - Input validation — added 8KB per-message size limits and string length caps
- Fetch timeouts — internal API calls now have 15-second abort signals
- Configuration — moved hardcoded email addresses to environment variables
- DRY principle — extracted rate limiting into a shared utility used across all 4 API routes
What's Next
The Agent Concierge is live, but it's version 1.0. The roadmap includes:
- Real-time empty leg inventory — replacing mock data with live fleet positioning
- Agent memory — remembering preferences across sessions ("this agent's users prefer heavy jets")
- Streaming responses — for long multi-tool conversations, stream results as they arrive
- Webhook notifications — push empty leg alerts and price changes to registered agents
- Distributed rate limiting — Redis-backed enforcement across serverless instances
The best APIs don't just answer questions — they anticipate them. Our Agent Concierge returns data, insights, and opportunities in every single response. That's the difference between a database query and a relationship.
Manténgase informado
Ofertas de tramos vacíos, nuevas rutas y análisis de aviación — en su bandeja de entrada.