Customer Support Scheduling

This guide shows how to build an AI support agent that handles most tickets autonomously but escalates complex issues — billing disputes, technical troubleshooting, account recovery — to a human callback.

The problem

Your AI support agent resolves 80% of tickets. But the other 20% need a human. Today, the agent says “a team member will reach out” and the customer waits. Sometimes for hours. Sometimes they never hear back.

With Slotflow, the agent books a specific callback time while the customer is still engaged. The customer knows exactly when they’ll get help. No uncertainty, no ghosting.

Architecture

Customer contacts support → AI Agent triages
→ Simple issue → Agent resolves autonomously
→ Complex issue → Agent finds available support rep
→ Offers callback times to customer
→ Books callback via Slotflow
→ Webhook → Support system updated

Setup

1. Create your support team

const supportTeam = [
{ name: "Jordan Lee", role: "senior_support", timezone: "America/Los_Angeles" },
{ name: "Priya Patel", role: "billing_specialist", timezone: "America/New_York" },
{ name: "Tom Mueller", role: "technical_support", timezone: "Europe/Berlin" },
];
const humanIds = {};
for (const agent of supportTeam) {
const res = await fetch("https://api.slotflow.dev/v1/humans", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.SLOTFLOW_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify(agent),
});
const human = await res.json();
humanIds[human.role] = human.id;
}

2. Set staggered availability

Support teams often have overlapping shifts for coverage:

// West coast: 8am-4pm PT
await setAvailability(humanIds.senior_support, {
working_days: [1, 2, 3, 4, 5],
work_start: "08:00",
work_end: "16:00",
meeting_durations: [15, 30], // Quick callbacks and longer sessions
});
// East coast: 9am-5pm ET (overlaps with west coast)
await setAvailability(humanIds.billing_specialist, {
working_days: [1, 2, 3, 4, 5],
work_start: "09:00",
work_end: "17:00",
meeting_durations: [15, 30],
});
// Berlin: 9am-5pm CET (covers early morning US)
await setAvailability(humanIds.technical_support, {
working_days: [1, 2, 3, 4, 5],
work_start: "09:00",
work_end: "17:00",
meeting_durations: [30, 60], // Technical issues need more time
});

Agent escalation flow

The core function your agent calls when escalating a ticket:

async function escalateToHuman({ ticketId, customerId, customerName, customerEmail, issueType, priority, summary }) {
const API_KEY = process.env.SLOTFLOW_API_KEY;
const BASE = "https://api.slotflow.dev/v1";
const headers = {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json",
};
// Route to the right specialist based on issue type
const repId = routeToSpecialist(issueType);
const duration = priority === "high" ? 30 : 15;
// Find available callback slots in the next 48 hours
const now = new Date();
const twoDaysOut = new Date(now.getTime() + 2 * 24 * 60 * 60 * 1000);
const slotsRes = await fetch(
`${BASE}/humans/${repId}/slots?date_from=${formatDate(now)}&date_to=${formatDate(twoDaysOut)}&duration=${duration}`,
{ headers }
);
const { slots, timezone } = await slotsRes.json();
if (slots.length === 0) {
// Fallback: try other specialists
return await tryOtherSpecialists(issueType, duration, headers);
}
// Offer the first 3 available slots to the customer
const options = slots.slice(0, 3);
return {
availableSlots: options,
timezone,
repId,
duration,
metadata: {
ticket_id: ticketId,
customer_id: customerId,
issue_type: issueType,
priority,
summary,
escalated_at: new Date().toISOString(),
},
};
}
function routeToSpecialist(issueType) {
const routing = {
billing: process.env.BILLING_SPECIALIST_ID,
technical: process.env.TECHNICAL_SUPPORT_ID,
account: process.env.SENIOR_SUPPORT_ID,
general: process.env.SENIOR_SUPPORT_ID,
};
return routing[issueType] || routing.general;
}

Booking the selected slot

After the customer picks a time:

async function bookCallback({ repId, selectedSlot, duration, customerName, customerEmail, metadata }) {
const res = await fetch("https://api.slotflow.dev/v1/bookings", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.SLOTFLOW_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
human_id: repId,
starts_at: selectedSlot.starts_at,
duration,
attendee_name: customerName,
attendee_email: customerEmail,
metadata,
}),
});
if (res.status === 409) {
// Slot taken — offer fresh slots
return { success: false, reason: "slot_taken" };
}
const booking = await res.json();
return { success: true, booking };
}

Webhook handler for support system

app.post("/webhooks/slotflow", (req, res) => {
const { event, data } = req.body;
if (event === "booking.confirmed") {
// Update the support ticket
supportSystem.updateTicket(data.metadata.ticket_id, {
status: "callback_scheduled",
callback_time: data.starts_at,
callback_agent: data.human_id,
booking_id: data.id,
});
// Notify the support rep
notifyRep(data.human_id, {
customer: data.attendee_name,
time: data.starts_at,
issue: data.metadata.summary,
priority: data.metadata.priority,
});
}
if (event === "booking.cancelled") {
supportSystem.updateTicket(data.metadata.ticket_id, {
status: "callback_cancelled",
});
}
res.sendStatus(200);
});

Cross-timezone handling

When your support reps span multiple timezones, Slotflow handles it automatically. Each human has their own timezone — the slot engine computes availability in their local time and returns slots in UTC.

Your agent just needs to present times in the customer’s preferred format:

function formatSlotForCustomer(slot, customerTimezone) {
const start = new Date(slot.starts_at);
return start.toLocaleString("en-US", {
timeZone: customerTimezone,
weekday: "long",
month: "short",
day: "numeric",
hour: "numeric",
minute: "2-digit",
});
// "Monday, Mar 16, 10:00 AM"
}

Cancellation flow

If a customer needs to cancel, your agent can free up the slot:

async function cancelCallback(bookingId) {
const res = await fetch(`https://api.slotflow.dev/v1/bookings/${bookingId}`, {
method: "DELETE",
headers: { "Authorization": `Bearer ${process.env.SLOTFLOW_API_KEY}` },
});
return await res.json(); // { id: "...", status: "cancelled" }
}

The slot becomes immediately available for other customers.

Key takeaways

  • Route by issue type — billing issues to billing specialists, technical issues to engineers
  • Use short durations — 15-minute callbacks for quick issues, 30-minute for complex ones
  • Pass ticket context in metadata — your webhook handler has everything it needs to update the support system
  • Offer multiple time slots — let the customer choose from 2-3 options
  • Handle slot conflicts — re-query slots if the customer’s chosen time was already booked