← Integrations
SMS

Seshn + Twilio

Send SMS confirmations, appointment reminders, and waitlist alerts using Twilio and Seshn webhooks.

Install

1npm install twilio

Webhook events

SMS works best for time-sensitive, short-form notifications:

booking.createdConfirm the booking via text
booking.cancelledNotify of cancellation
booking.hold_expiredAlert that their hold timed out
waitlist.promotedThey got a spot — act fast

Webhook handler

A Next.js API route that receives Seshn webhooks and sends SMS via Twilio.

app/api/webhooks/seshn/route.ts
1import twilio from 'twilio';
2import { createHmac } from 'crypto';
3
4const client = twilio(
5 process.env.TWILIO_ACCOUNT_SID,
6 process.env.TWILIO_AUTH_TOKEN
7);
8const FROM_NUMBER = process.env.TWILIO_PHONE_NUMBER!;
9const WEBHOOK_SECRET = process.env.SESHN_WEBHOOK_SECRET!;
10
11export async function POST(req: Request) {
12 const body = await req.text();
13 const signature = req.headers.get('x-webhook-signature');
14
15 const expected = 'sha256=' +
16 createHmac('sha256', WEBHOOK_SECRET).update(body).digest('hex');
17
18 if (signature !== expected) {
19 return new Response('Invalid signature', { status: 401 });
20 }
21
22 const event = req.headers.get('x-webhook-event');
23 const payload = JSON.parse(body);
24
25 // Only send SMS if the contact has a phone number
26 if (!payload.contact?.phone) {
27 return new Response('No phone number', { status: 200 });
28 }
29
30 let message: string | null = null;
31
32 switch (event) {
33 case 'booking.created':
34 const date = new Date(payload.slot.startTime);
35 message = `Booking confirmed: ${payload.service.name} on ${date.toLocaleDateString()} at ${date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}. ${payload.seats} seat(s).`;
36 break;
37
38 case 'booking.cancelled':
39 message = `Your booking for ${payload.service.name} has been cancelled.`;
40 break;
41
42 case 'booking.hold_expired':
43 message = `Your held spot for ${payload.service.name} has expired. Book again at ${payload.bookingUrl ?? 'our website'}.`;
44 break;
45
46 case 'waitlist.promoted':
47 message = `A spot opened up! You've been booked for ${payload.service.name}.`;
48 break;
49 }
50
51 if (message) {
52 await client.messages.create({
53 body: message,
54 from: FROM_NUMBER,
55 to: payload.contact.phone,
56 });
57 }
58
59 return new Response('OK', { status: 200 });
60}

Register the webhook

terminal
1curl -X POST https://api.seshn.net/v1/webhooks \
2 -H "Authorization: Bearer sk_live_..." \
3 -H "Content-Type: application/json" \
4 -d '{
5 "url": "https://yourdomain.com/api/webhooks/seshn",
6 "events": [
7 "booking.created",
8 "booking.cancelled",
9 "booking.hold_expired",
10 "waitlist.promoted"
11 ]
12 }'

Next steps

  • Use Twilio Messaging Services for number pooling and compliance at scale.
  • Add WhatsApp via Twilio for international customers by changing the from to a whatsapp: prefix.
  • Rate-limit SMS sends to avoid surprising customers with duplicate texts on retries.