Hotels lose guest satisfaction when the front desk is busy, off-shift, or stuck on the phone. A concierge voice and SMS line gives guests one number to call or text for room service, housekeeping, concierge help, or maintenance, and gives staff a clean queue of structured requests to work through.
The canonical code example is in the Telnyx code examples repo:
https://github.com/team-telnyx/telnyx-code-examples/tree/main/hotel-guest-services-python
What This Example Builds
This Python Flask example combines Telnyx Voice, AI Inference, Messaging, and Slack into a single concierge workflow:
- Inbound voice calls are answered, greeted by name when the caller ID matches a known room, and asked for the room number when it does not
- Inbound SMS from any guest phone is accepted the same way
- Each request is classified into
room_service,housekeeping,concierge, ormaintenancewith Telnyx AI Inference - Urgent phrases (
fire,flood,leak,locked out,gas,medical) override the LLM classification and route the request with priority - A Slack alert is posted for every new request, with department-specific emoji
- The guest gets an SMS confirmation on receipt and another SMS when staff mark the request complete
Why This Is A Useful Voice AI Example
This example is small enough to read in one sitting, but it covers the moving parts a developer needs for a real voice AI workflow:
- Call Control webhooks for
call.initiated,call.answered,call.speak.ended,call.gather.ended,call.hangup - Caller-ID-based room lookup and a regex-based room-number capture when the caller is unknown
- AI Inference with a constrained JSON system prompt for classification
- Slack integration for staff visibility
- SMS confirmation with idempotent webhook handling
That makes it a good starting point for any single-vertical phone service where one phone number handles a small set of structured requests.
Products Used
The example metadata lists:
telnyx_products: [Voice, AI Inference, Messaging]
integrations: [Slack]
language: python
framework: flask
Voice handles inbound calls and TTS. AI Inference classifies each request. Messaging sends the guest confirmations. Slack alerts staff.
Architecture
inbound phone call or sms
-> telnyx voice/sms webhook
-> flask app
-> look up room by caller id
-> (if unknown) ask for room number, extract it
-> ai inference: classify department + urgency
-> urgent phrases override classification
-> append to service_requests
-> sms confirmation to guest
-> slack alert to staff
-> staff: POST /requests/<id>/complete
-> sms fulfilment to guest
State lives in memory (ROOMS, service_requests, calls) for the demo. For production, replace the room and request stores with your PMS database (Opera, Mews, Cloudbeds).
Run The Example
git clone https://github.com/team-telnyx/telnyx-code-examples.git
cd telnyx-code-examples/hotel-guest-services-python
cp .env.example .env
pip install -r requirements.txt
python app.py
Fill in the environment variables:
TELNYX_API_KEY=KEY...
TELNYX_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----..."
MAIN_NUMBER=+1...
CONNECTION_ID=...
STAFF_SLACK_WEBHOOK=https://hooks.slack.com/services/...
AI_MODEL=moonshotai/Kimi-K2.6
PORT=5000
Expose the local webhook:
ngrok http 5000
Configure the Voice API application webhook URL:
https://<ngrok-id>.ngrok-free.app/webhooks/voice
Configure the Messaging Profile inbound webhook URL:
https://<ngrok-id>.ngrok-free.app/webhooks/sms
Demo Script
Call the Telnyx number from a phone whose caller ID is +15559001234 (room 101, guest Smith) or +15559005678 (room 205, guest Chen):
hi, this is room 205, can i order a club sandwich and a sparkling water please?
For the unknown-caller path, call from any other phone. When prompted for your room number, say "Room 205 please".
Expected result:
- Agent confirms by voice that the request is logged
curl http://localhost:5000/requestsshows the open request- Slack shows the new request in the configured channel
- Guest receives an SMS confirmation
When staff are ready:
curl -X POST http://localhost:5000/requests/0/complete
Guest receives the fulfilment SMS.
Edge Cases The Example Handles
- No speech detected on a gather — reprompt the caller
- Unknown caller — ask for room number, extract it from the next utterance
- AI Inference returns malformed JSON — fall back to
concierge / normalwith raw text as the summary - Slack webhook down — log and continue, SMS still goes out
- SMS send fails — log and continue, Slack still goes out
- Duplicate webhook delivery — event IDs are tracked for one hour and deduplicated
- Urgent phrases (
fire,flood,locked out,gas leak) — overridden tourgency: urgentand posted to Slack with a:rotating_light:prefix even if the LLM classifies them otherwise
Production Considerations
Before using this pattern in production, add:
- Persistent storage: replace
ROOMSandservice_requestswith your PMS database - Redis-backed deduplication so event IDs survive restarts
- Queued Slack and SMS side effects so webhook responses stay under Telnyx's timeout
- Call recording for quality assurance (
record_channels: "dual"on answer) - Real paging (PagerDuty, Opsgenie) for urgent requests instead of Slack
- Multi-language system prompts and TTS voices per property