P
PepoSmart Docs

Webhooks

Webhooks push a signed JSON message to a URL you choose the moment something happens in PepoSmart — a booking is created, cancelled, rescheduled or paid, or the AI notetaker finishes a meeting. They are the raw form of the Zapier and Make integrations: use them whenever you own the receiving system and want retries, signatures and a delivery log.

Availability

Webhooks are included on the Professional plan and above (the same tier as Zapier and Make). Each account can register up to 10 endpoints. Prefer to react to events with a no-code tool? Use Zapier or Make instead. Prefer to pull data on demand? See the REST API.

Registering an endpoint

  1. In the app, open Integrations → Webhooks and click Add endpoint
  2. Enter your HTTPS URL and an optional description
  3. Choose a scope: every event type on your account, or only bookings of one event type
  4. Tick the events you want, or keep Subscribe to all events
  5. Copy the signing secret shown after saving (it can be revealed or rotated later from the endpoint card) and use it to verify every request
  6. Click Send test to deliver a sample event and see the response your endpoint returned

Try it without writing code

Paste a URL from webhook.site as the endpoint, click Send test, and inspect the exact headers and body PepoSmart sends. Use ngrok to receive events on a server running on your laptop.

Events

EventSent whenGroup
booking.createdSomeone books a meeting (also sent for the new booking of a reschedule)Bookings
booking.cancelledA booking is cancelled by the invitee or a hostBookings
booking.rescheduledA booking moves to a new time — sent for the replacement booking, with the original attachedBookings
booking.paidA paid event's Stripe or PayPal payment is confirmed and the booking is createdPayments
meeting-notes.completedThe AI transcript, summary, analysis and action items are readyMeeting notes
action-item.createdAI extracts an action item from a meetingMeeting notes
followup-draft.generatedAI drafts a follow-up email after a meetingFollow-ups
followup-draft.sentA follow-up email draft is sent to the inviteeFollow-ups

A reschedule produces two messages: booking.created for the new booking, then booking.rescheduled for that same booking with a rescheduledFrom block pointing at the original. Cancellations fire whether the invitee cancels from their email link or a host cancels from the Meetings page. Team events are delivered to the event owner's endpoints.

The request

Every delivery is a POST with a JSON body. The envelope is the same for every event; only data changes.

POST https://example.com/webhooks/peposmart
Content-Type: application/json
User-Agent: PepoSmart-Webhooks/1.0
X-PepoSmart-Event: booking.created
X-PepoSmart-Delivery: 6f1c3c1e-2c2b-4a4e-9d3a-0f6c1d5b1a2e
X-PepoSmart-Signature: t=1756720800,v1=9f2c8a7d…e41b

{
  "id": "6f1c3c1e-2c2b-4a4e-9d3a-0f6c1d5b1a2e",
  "type": "booking.created",
  "createdAt": "2026-09-01T10:00:00.000Z",
  "data": { … }
}
  • id is the delivery id. It stays the same across retries — store it and ignore a message you have already processed.
  • type is the event name; it is also sent as the X-PepoSmart-Event header so you can route before parsing the body.
  • Respond with any 2xx status within 10 seconds. Do the real work after responding if it is slow.

Booking payload (data)

Every booking.* event carries the same object. Field names match the Zapier and Make payloads (bookingId, attendee, eventDetails…), plus host, payment, cancellation and, on a reschedule, rescheduledFrom.

{
  "bookingId": "abc123",
  "eventId": "event456",
  "status": "confirmed",
  "startTime": "2026-09-20T14:00:00.000Z",
  "endTime": "2026-09-20T14:30:00.000Z",
  "timezone": "America/New_York",
  "location": "google-meet",
  "meetingUrl": "https://meet.google.com/abc-defg-hij",
  "attendee": {
    "name": "Jamie Rivera",
    "firstName": "Jamie",
    "lastName": "Rivera",
    "email": "[email protected]",
    "phone": "+1 555 010 0100",
    "timezone": "America/New_York"
  },
  "additionalGuests": [],
  "customAnswers": { "What would you like to discuss?": "Onboarding" },
  "eventDetails": { "id": "event456", "title": "Discovery call", "description": null, "duration": 30, "type": "one-on-one" },
  "host": { "id": "user789", "name": "Jane Host", "email": "[email protected]" },
  "payment": null,
  "cancellation": null,
  "createdAt": "2026-09-01T10:00:00.000Z"
}
  • booking.cancelled fills cancellation: { "cancelledAt", "cancelledBy", "reason" } and sets status to cancelled.
  • booking.rescheduled adds rescheduledFrom: { "bookingId", "startTime", "endTime", "timezone" } of the original booking.
  • booking.paid fills payment: { "status": "paid", "amount": 5000, "currency": "usd", "sessionId": "…" } (amount in the smallest currency unit).

Meeting-notes and follow-up payloads

meeting-notes.completed, action-item.created, followup-draft.generated and followup-draft.sent use the same data shapes as the Zapier integration — see the Zapier payload reference. One difference: transcripts longer than 100,000 characters are cut and the payload carries "transcriptTruncated": true.

Verifying the signature

Each request carries X-PepoSmart-Signature: t=<unix seconds>,v1=<hex> where v1 is HMAC-SHA256 of the string <t>.<raw request body>, keyed with the endpoint's secret. Verify it before trusting a message:

  1. Read the raw body exactly as received — do not re-serialise parsed JSON
  2. Recompute the HMAC over t + "." + body with your secret
  3. Compare with v1 using a constant-time comparison
  4. Reject timestamps older than a few minutes to block replays
// Node.js
import crypto from "node:crypto";

export function verifyPepoSmartWebhook(secret, signatureHeader, rawBody) {
  const parts = Object.fromEntries(signatureHeader.split(",").map((p) => p.split("=")));
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${parts.t}.${rawBody}`)
    .digest("hex");
  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
  return fresh && crypto.timingSafeEqual(Buffer.from(expected, "hex"), Buffer.from(parts.v1, "hex"));
}
# Python
import hmac, hashlib, time

def verify(secret: str, header: str, raw_body: bytes) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    expected = hmac.new(secret.encode(), f"{parts['t']}.".encode() + raw_body, hashlib.sha256).hexdigest()
    return abs(time.time() - int(parts["t"])) < 300 and hmac.compare_digest(expected, parts["v1"])

Keep the secret private

Anyone holding the secret can forge events. Store it like a password, never in client-side code, and rotate it from the endpoint card if it leaks — deliveries after a rotation are signed with the new secret immediately.

Retries, failures and the delivery log

  • A non-2xx response, a redirect, or no response within 10 seconds counts as a failure and is retried with exponential backoff, up to 7 attempts. Redirects are never followed.
  • After the last attempt the delivery is marked Failed. You can resend it from Recent deliveries once it has settled.
  • An endpoint whose deliveries fail 25 attempts in a row (about four events' worth of retries) is switched off automatically. Fix the receiver, then re-enable it with the toggle; test sends never count toward this.
  • Recent deliveries lists every delivery for 30 days with its attempt count, the payload sent and the first 1 KB of your response. Click a row for details.

Design for at-least-once delivery

A retry can arrive after your endpoint processed the first attempt but failed to answer in time. Treat the delivery id as an idempotency key and make your handler safe to run twice.

Limits and rules

  • Up to 10 endpoints per account; each has its own secret, scope and event list
  • Endpoint URLs must be https:// on a public hostname — localhost, private network addresses, IPv6 literals and PepoSmart's own domains are refused
  • Test sends and resends are rate-limited per account (20 and 60 per minute)
  • Endpoints keep their configuration if you downgrade below Professional, but deliveries pause until the plan is restored

Webhooks, Zapier or the API?

NeedUse
Push events into a system you build or hostWebhooks (this page)
Connect to 5,000+ apps without codeZapier or Make
Read availability or create bookings from your backendREST API
Show the booking page inside your productEmbedding