Use this skill when the user wants to book a table, make a restaurant reservation, call a restaurant to check availability, or place a dining reservation by phone. This MVP version starts the reservation call immediately, returns right away, and checks the final result in a follow-up user message.
This skill handles restaurant reservation phone calls through a local backend and Vapi.
This MVP uses a simple 2-message flow:
This design keeps the interaction simple and avoids making OpenClaw wait while the phone call is still in progress.
Trigger this skill when the user wants to:
Trigger this skill only when the request is specifically about a restaurant reservation phone call that should use the local backend and Vapi flow.
Do not trigger this skill for:
This skill should be used in 2 steps.
When the user provides reservation details, OpenClaw should:
POST http://localhost:3000/start-reservationcall_idOpenClaw should not wait for the full phone call to finish in the same response.
Suggested response style:
I started the reservation call for the restaurant. Ask me again shortly and I’ll check the result.
When the user follows up with something like:
OpenClaw should:
call_id from this conversationGET http://localhost:3000/reservation-result/{call_id}pending, tell the user the call is still in progresscompleted, analyze the transcript and return the final reservation outcomeThe skill expects the following fields when starting a reservation:
restaurant_namerestaurant_phonereservation_nameparty_sizedatepreferred_timefallback_timesspecial_requestsuser_callbackExample input:
{
"restaurant_name": "Dragon Village",
"restaurant_phone": "+16145551234",
"reservation_name": "Alex",
"party_size": 5,
"date": "Friday",
"preferred_time": "7:00 PM",
"fallback_times": ["7:30 PM", "8:00 PM", "8:30 PM"],
"special_requests": "Indoor seating if possible",
"user_callback": "+17404048938"
}
OpenClaw sends:
curl -X POST http://localhost:3000/start-reservation \
-H "Content-Type: application/json" \
-d '{
"restaurant_name": "Dragon Village",
"restaurant_phone": "+16145551234",
"reservation_name": "Alex",
"party_size": 5,
"date": "Friday",
"preferred_time": "7:00 PM",
"fallback_times": ["7:30 PM", "8:00 PM", "8:30 PM"],
"special_requests": "Indoor seating if possible",
"user_callback": "+17404048938"
}'
Example response:
{
"ok": true,
"status": "started",
"call_id": "019d3081-418d-7000-afe9-0e38881c0ee4",
"message": "Reservation call started."
}
OpenClaw sends:
curl http://localhost:3000/reservation-result/YOUR_CALL_ID
If the call is still in progress, the backend returns:
{
"ok": true,
"status": "pending",
"call_id": "019d3081-418d-7000-afe9-0e38881c0ee4",
"message": "Reservation result not ready yet."
}
If the call has finished, the backend returns:
{
"ok": true,
"status": "completed",
"call_id": "019d3081-418d-7000-afe9-0e38881c0ee4",
"result": {
"call_id": "019d3081-418d-7000-afe9-0e38881c0ee4",
"status": "completed",
"event_type": "end-of-call-report",
"request": {
"restaurant_name": "Dragon Village",
"restaurant_phone": "+16145551234",
"reservation_name": "Alex",
"party_size": 5,
"date": "Friday",
"preferred_time": "7:00 PM",
"fallback_times": ["7:30 PM", "8:00 PM", "8:30 PM"],
"special_requests": "Indoor seating if possible",
"user_callback": "+17404048938"
},
"transcript": "assistant: ...\nuser: ...",
"summary": null,
"assistant_name": "Restaurant Reservation Agent",
"customer_number": "+17404048938",
"vapi_number": "+16504510603",
"received_at": "2026-03-27T19:51:17.466710+00:00"
}
}
When the backend returns status: completed, OpenClaw should analyze:
result.requestresult.transcriptresult.summary, if availableOpenClaw should then return structured JSON with these keys:
reservation_statusrestaurant_namereservation_nameparty_sizedaterequested_timeconfirmed_timespecial_requestsnotesconfidenceAllowed values for reservation_status:
confirmedunavailablecallback_neededvoicemail_leftbook_onlineunknownSuggested interpretation instruction:
You are analyzing the final result of a restaurant reservation phone call.
You are given:
1. The original reservation request
2. The final phone call transcript
3. Optional summary metadata
Your task is to determine the final reservation outcome and return structured JSON.
Return ONLY valid JSON with exactly these keys:
- reservation_status
- restaurant_name
- reservation_name
- party_size
- date
- requested_time
- confirmed_time
- special_requests
- notes
- confidence
Allowed values for reservation_status:
- confirmed
- unavailable
- callback_needed
- voicemail_left
- book_online
- unknown
Rules:
- Use the transcript as the source of truth for the final outcome.
- Interpret noisy phone transcripts semantically rather than literally.
- Use the summary only if it helps clarify the transcript.
- Use request metadata for unchanged reservation fields.
- If the requested time was unavailable but another time was accepted and confirmed, set reservation_status to "confirmed" and set confirmed_time to the accepted time.
- If no reservation was completed, set confirmed_time to null.
- Do not invent details not supported by the transcript or request.
- confidence must be one of: high, medium, low.
- Output JSON only.
OpenClaw should save the returned call_id from the first message and reuse it in the follow-up message.
For this MVP, the follow-up message should check the most recent reservation call in the current conversation unless the user explicitly refers to a different call_id.
server/app.pyMain backend entrypoint used by OpenClaw.
Responsible for:
server/vapi_client.pyInternal helper used by the backend to:
server/webhook_handler.pyInternal helper used by the backend to:
call_idcd skills/restaurant-reservation/server
uvicorn app:app --reload --port 3000
ngrok http 3000
Set the Vapi server/webhook URL to: