AI Scheduling
Let callers book appointments over the phone without talking to a human. DialStack handles the voice conversation — greeting, slot negotiation, callback collection — and calls your system to read availability and write the booking.
AI Scheduling is powered by the AI Agents API. Each AI agent auto-provisions a voice app and an extension, has a configurable persona and FAQ, and optionally calls your scheduling endpoints during the conversation.
What you build
Three HTTPS endpoints under a base URL you give us:
| Path | Purpose |
|---|---|
POST {base}/customers/lookup | Match the caller's phone number to an existing customer record, so the agent can personalize the conversation. |
POST {base}/availability/search | Return open slots in a date range. |
POST {base}/bookings | Create a booking for the slot the caller picked. |
These are request/response calls made during the conversation, not fire-and-forget notifications: what you return is what the agent works with. They are separate from webhook endpoints, and are not configured through the Webhook Endpoints API.
The Webhook Protocol reference defines each request and the response shape the agent understands best.
Create the agent
Configure the agent via POST /v1/ai-agents. Point scheduling.webhook_url at the base URL that hosts the three paths above. Scheduling is on for an agent while scheduling.webhook_url is set; each agent has its own.
curl -X POST https://api.dialstack.ai/v1/ai-agents \
-H 'Authorization: Bearer sk_live_YOUR_KEY' \
-H 'DialStack-Account: acct_01h2xcejqtf2nbrexx3vqjhp41' \
-H 'Content-Type: application/json' \
-d '{
"name": "Front Desk Receptionist",
"extension_number": "200",
"persona_name": "Tony",
"greeting_name": "Jones Family Dental",
"instructions": "You are the receptionist. Be warm and professional. Offer to book an appointment if the caller does not specify a reason.",
"faq_responses": [
{ "question": "What are your business hours?", "answer": "We are open Monday through Friday, 9 AM to 5 PM." },
{ "question": "Do you take walk-ins?", "answer": "Walk-ins are welcome, but appointments are prioritized." }
],
"scheduling": {
"webhook_url": "https://api.clinic.io/dialstack"
}
}'
The response includes the agent's id and voice_app, the voice app it manages for you. Extension 200 now routes callers to the agent.
How a booking call plays out
The AI handles disambiguation ("which Tuesday?"), date math, and re-prompting. You only see the resolved request.
The requests
Every request is a POST with a JSON body and these headers:
| Header | Description |
|---|---|
Content-Type | application/json |
X-DialStack-Signature | HMAC-SHA256 signature of the request |
X-DialStack-Account-Id | The account whose caller the agent is talking to |
Every body carries account_id, the same account as the header.
/customers/lookupsendscustomer.phone, the caller's number in E.164. Return whether you found the caller and, if so, their name and any upcoming appointment./availability/searchsendsquery.filter.start_at_rangewithstart_atandend_at, the dates the caller asked about (YYYY-MM-DD). Return the open slots in that range./bookingssendsidempotency_keyand abookingwithstart_at(ISO 8601),duration_minutes,customer(phone,name) and, when the caller gave one,noteswith the reason for the visit. Return the confirmed booking.
The full schemas and response shapes are in the Webhook Protocol reference.
Verifying requests
Requests are signed with the secret of the agent's voice app, using the same scheme as every DialStack webhook: see Signature verification. Read the secret with GET /v1/voice-apps/{voice_app_id}, using the agent's voice_app. If you rotate the voice app's secret, the agent signs with the new one from the next call.
Testing your handler
Sign a test request yourself and send it to your handler, with the voice app's secret:
TIMESTAMP=$(date +%s)
PAYLOAD='{"account_id":"ACCOUNT_ID","query":{"filter":{"start_at_range":{"start_at":"2026-04-28","end_at":"2026-04-28"}}}}'
SECRET="your_voice_app_secret"
SIGNATURE=$(printf '%s' "${TIMESTAMP}.${PAYLOAD}" | openssl dgst -sha256 -hmac "${SECRET}" | cut -d' ' -f2)
curl -X POST http://localhost:3000/dialstack/availability/search \
-H "Content-Type: application/json" \
-H "X-DialStack-Signature: t=${TIMESTAMP},v1=${SIGNATURE}" \
-H "X-DialStack-Account-Id: ACCOUNT_ID" \
-d "${PAYLOAD}"
Responses and errors
- Return
2xxwith a JSON object. The agent reads the object you return as is, so any extra fields you include (account-specific context, a time-sensitive promotion) are available to it in the conversation. - Respond within 10 seconds. A timeout, an unreachable URL, or a non-
2xxstatus is reported to the agent as a failed request, with the status code but not your response body. - For a booking you can't make, such as a slot taken since the search, return a non-
2xxstatus so the agent doesn't confirm it to the caller.
Idempotency
Booking requests carry an idempotency_key. The agent sends the same key if it books the same slot for the same caller twice in one call, so treat a repeated key as the same booking:
- Store the idempotency key when processing a booking.
- If the same key arrives again, return the original booking response.
- Keys can be safely expired after 24 hours.
Route calls to the agent
Callers reach the agent by its extension. To put it in a dial plan, point a voice_app node at the agent's voice app:
{
id: 'ai_receptionist',
type: 'voice_app',
config: { voice_app: 'va_01h2xcejqtf2nbrexx3vqjhp49' }, // the agent's voice_app
}
An agent's voice app always takes control of the call, so use the default control mode: a notify node can't reference it.
Typical patterns:
- After-hours fallback —
schedulenode →ring_all_usersduring business hours, avoice_appnode for the agent when closed. - Overflow —
ring_all_userswith a short timeout,nextpointing at the agent's node when no one picks up. - Direct line — an inbound number routed straight at the agent's extension for a 24/7 booking line.
Updating the agent
Agent config (persona, instructions, FAQs, scheduling URL) can be updated via POST /v1/ai-agents/{id}. Changes take effect on the next call.
See also
- Webhook Protocol reference — request and response schemas for the three endpoints.
- Voice Apps — the primitive AI Agents are built on.
- Dial Plans — how to route calls to the agent.
- BYO VoiceAI — if you want to run the conversation yourself instead of using DialStack's AI.
- AI Agents API — full REST surface.