← Integrations
Email

Seshn + Resend

Send booking confirmations, cancellation notices, and reminders using Resend and Seshn webhooks.

Install

1npm install resend

Webhook events

Register a Seshn webhook for the events you want to send emails for. The most common:

booking.createdSend a confirmation email
booking.confirmedNotify after payment completes
booking.cancelledSend a cancellation notice
booking.rescheduledNotify of new date/time
waitlist.promotedLet them know they got a spot

Webhook handler

A Next.js API route that receives Seshn webhooks and sends emails via Resend.

app/api/webhooks/seshn/route.ts
1import { Resend } from 'resend';
2import { createHmac } from 'crypto';
3
4const resend = new Resend(process.env.RESEND_API_KEY);
5const WEBHOOK_SECRET = process.env.SESHN_WEBHOOK_SECRET!;
6
7export async function POST(req: Request) {
8 const body = await req.text();
9 const signature = req.headers.get('x-webhook-signature');
10
11 // Verify the webhook signature
12 const expected = 'sha256=' +
13 createHmac('sha256', WEBHOOK_SECRET).update(body).digest('hex');
14
15 if (signature !== expected) {
16 return new Response('Invalid signature', { status: 401 });
17 }
18
19 const event = req.headers.get('x-webhook-event');
20 const payload = JSON.parse(body);
21
22 switch (event) {
23 case 'booking.created':
24 await resend.emails.send({
25 from: 'bookings@yourdomain.com',
26 to: payload.contact.email,
27 subject: 'Booking confirmed',
28 html: `
29 <h1>You're booked!</h1>
30 <p><strong>${payload.service.name}</strong></p>
31 <p>${new Date(payload.slot.startTime).toLocaleString()}</p>
32 <p>${payload.seats} seat(s) reserved.</p>
33 `,
34 });
35 break;
36
37 case 'booking.cancelled':
38 await resend.emails.send({
39 from: 'bookings@yourdomain.com',
40 to: payload.contact.email,
41 subject: 'Booking cancelled',
42 html: `
43 <h1>Booking cancelled</h1>
44 <p>Your booking for <strong>${payload.service.name}</strong> has been cancelled.</p>
45 `,
46 });
47 break;
48
49 case 'booking.rescheduled':
50 await resend.emails.send({
51 from: 'bookings@yourdomain.com',
52 to: payload.contact.email,
53 subject: 'Booking rescheduled',
54 html: `
55 <h1>New time confirmed</h1>
56 <p><strong>${payload.service.name}</strong></p>
57 <p>${new Date(payload.newSlot.startTime).toLocaleString()}</p>
58 `,
59 });
60 break;
61
62 case 'waitlist.promoted':
63 await resend.emails.send({
64 from: 'bookings@yourdomain.com',
65 to: payload.contact.email,
66 subject: 'You got a spot!',
67 html: `
68 <h1>Good news!</h1>
69 <p>A spot opened up for <strong>${payload.service.name}</strong>
70 and you've been automatically booked.</p>
71 `,
72 });
73 break;
74 }
75
76 return new Response('OK', { status: 200 });
77}

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.confirmed",
9 "booking.cancelled",
10 "booking.rescheduled",
11 "waitlist.promoted"
12 ]
13 }'

Next steps

  • Use React Email for beautiful, type-safe templates.
  • Add a booking.hold_expired handler to notify customers their hold timed out.
  • Use the x-webhook-id header as an idempotency key for Resend.