← Integrations
Notifications
Seshn + Slack
Post booking notifications to Slack channels. New bookings, cancellations, payment alerts — right where your team already works.
Setup
No SDK required. Slack Incoming Webhooks accept a simple HTTP POST — you can wire this up with just fetch.
- Go to your Slack workspace settings and create an Incoming Webhook.
- Choose the channel (e.g.
#bookings). - Copy the webhook URL.
Webhook events
Choose which events matter for your team's ops:
booking.createdNew booking notificationbooking.cancelledAlert on cancellationspayment.failedPayment issues that need attentionwaitlist.joinedDemand signal for popular slotsWebhook handler
app/api/webhooks/seshn/route.ts
1import { createHmac } from 'crypto';23const SLACK_WEBHOOK_URL = process.env.SLACK_WEBHOOK_URL!;4const WEBHOOK_SECRET = process.env.SESHN_WEBHOOK_SECRET!;56export async function POST(req: Request) {7 const body = await req.text();8 const signature = req.headers.get('x-webhook-signature');910 const expected = 'sha256=' +11 createHmac('sha256', WEBHOOK_SECRET).update(body).digest('hex');1213 if (signature !== expected) {14 return new Response('Invalid signature', { status: 401 });15 }1617 const event = req.headers.get('x-webhook-event');18 const payload = JSON.parse(body);1920 let text: string | null = null;2122 switch (event) {23 case 'booking.created':24 const date = new Date(payload.slot.startTime);25 text = [26 '*New booking*',27 `>${payload.service.name}`,28 `>${payload.contact.name} · ${payload.seats} seat(s)`,29 `>${date.toLocaleDateString()} at ${date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}`,30 ].join('\n');31 break;3233 case 'booking.cancelled':34 text = [35 '*Booking cancelled*',36 `>${payload.service.name}`,37 `>${payload.contact.name}`,38 payload.reason ? `>Reason: ${payload.reason}` : null,39 ].filter(Boolean).join('\n');40 break;4142 case 'payment.failed':43 text = [44 '*Payment failed*',45 `>Booking ${payload.bookingId}`,46 `>${payload.contact.name} · ${payload.contact.email}`,47 `>Amount: ${(payload.amount / 100).toFixed(2)} ${payload.currency.toUpperCase()}`,48 ].join('\n');49 break;5051 case 'waitlist.joined':52 text = [53 '*Waitlist joined*',54 `>${payload.service.name}`,55 `>${payload.contact.name} is waiting for a spot`,56 ].join('\n');57 break;58 }5960 if (text) {61 await fetch(SLACK_WEBHOOK_URL, {62 method: 'POST',63 headers: { 'Content-Type': 'application/json' },64 body: JSON.stringify({ text }),65 });66 }6768 return new Response('OK', { status: 200 });69}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 "payment.failed",10 "waitlist.joined"11 ]12 }'Next steps
- Use Block Kit for richer message formatting with buttons and fields.
- Route different events to different channels — e.g.
#bookingsfor new bookings,#alertsfor payment failures. - Add a daily digest using a cron job that queries
GET /v1/bookingsand posts a summary.