How to Send Acuity New-Appointment Alerts to a Shared Room
Verify one Acuity scheduled webhook and send fixed shared-phone copy without forwarding client or appointment details.

To send an Acuity new-appointment alert to a shared group of phones, configure one scheduled webhook, verify Acuity's raw-body signature in a small relay, filter one calendar and appointment type, then send fixed copy to a PingRoom incoming webhook. Acuity remains the appointment record; the Room learns only that a booking is waiting there.
This is a webhook recipe you configure, not a native Acuity integration. The destination Room's owner needs PingRoom Pro. The signed version below also requires access to the Acuity API key used as the webhook signing secret.
Start with Acuity's own notifications
Acuity already offers email and Admin-app push notifications for new, rescheduled, and canceled appointments. Use those first when everyone who needs the update is an appropriate Acuity user.
| What people need | Better starting point |
|---|---|
| Client, time, service, intake answers, or booking detail | Acuity |
| Ordinary alerts for signed-in Acuity users | Acuity's native Admin-app push |
| One fixed signal for a small, stable group | Signed Acuity webhook → relay → private Room |
| Assignment, response ownership, or escalation | Your approved scheduling or operations system |
The Room is an attention path, not another calendar. A Ping does not assign the appointment, prove that somebody opened it, contact the client, or confirm that the booking is valid.
Keep the webhook body small
Acuity's appointment webhook is form encoded. For a new booking, its documented body contains four useful fields:
action=scheduled&id=4001&calendarID=17&appointmentTypeID=29
That is enough for this workflow. Do not fetch the appointment by ID. The relay needs no client name, email, phone number, appointment time, intake answer, payment detail, or meeting link.
Choose one calendar ID and one appointment-type ID before you begin. The relay will ignore every other combination. Also subscribe to scheduled only. Acuity warns that using its catch-all changed webhook alongside scheduled, rescheduled, or canceled can create duplicate requests.
Check the plan and permission boundary
Acuity says webhooks are available on every plan, while API access and credentials are available on Premium or Powerhouse. Account owners and contributors with administrator access can configure webhooks.
Acuity signs webhook requests with the main admin's API key for static webhooks, or the relevant user's API key for dynamic webhooks. This recipe therefore requires authorized access to that signing key. If you cannot obtain it under your account and plan, use Acuity's native notifications or an approved integration instead of accepting an unverifiable public request.
Prepare one private Room
Create or choose a private PingRoom Room for the small group that genuinely needs this signal. Open Settings → Connections → From another app, add an incoming webhook named Acuity, keep Quick Ping on Custom message, save it, and store the complete secret URL as PINGROOM_WEBHOOK_URL in your relay's secret store.
The incoming-webhook guide covers the Room-side setup. The webhook reference documents the accepted fields, idempotency behavior, cooldown, and errors. The full URL is a credential; keep it out of Acuity fields, browser code, source control, screenshots, and logs.
Store these relay settings separately:
ACUITY_API_KEY: the signing key Acuity documents for this webhook.ACUITY_CALENDAR_ID: the one calendar allowed to create a Ping.ACUITY_APPOINTMENT_TYPE_ID: the one appointment type allowed to create a Ping.PINGROOM_WEBHOOK_URL: the complete incoming webhook URL for the selected Room.
In Acuity's Integrations area, configure an HTTPS appointment webhook that sends scheduled events to your relay. Use a disposable calendar and non-sensitive test booking for the rehearsal.
Verify before parsing
Acuity sends a Base64 HMAC-SHA256 digest in x-acuity-signature. Calculate the digest over the untouched request bytes and compare the decoded values in constant time before parsing the form body.
import { createHmac, timingSafeEqual } from "node:crypto"; function validAcuitySignature(rawBody, supplied, secret) { if (!/^[A-Za-z0-9+/]{43}=$/.test(supplied ?? "")) return false; const actual = Buffer.from(supplied, "base64"); if (actual.length !== 32) return false; const expected = createHmac("sha256", secret).update(rawBody).digest(); return timingSafeEqual(expected, actual); }
After the signature passes, require all of these conditions:
- The content type is
application/x-www-form-urlencoded. - The body is no larger than your small documented limit; our fixture uses 8 KiB.
- The body has exactly one each of
action,id,calendarID, andappointmentTypeID, with no extra fields. actionis exactlyscheduled.- The calendar and appointment-type IDs exactly match the configured values.
- The appointment ID and both filter IDs are positive decimal integers.
The documented signature has no timestamp field, so do not claim a freshness check. A legitimate retry of the same valid booking should resolve to the same event key and create no second Ping.
Queue fixed copy before acknowledging Acuity
Hash the appointment ID into one stable key, then put a fixed job into an atomic, durable queue before returning success to Acuity:
import { createHash } from "node:crypto"; const digest = createHash("sha256").update(appointmentId).digest("hex"); const eventKey = `acuity-appointment:${digest}`; await queue.enqueueOnce(eventKey, { eventKey, request: { action: null, title: "New Acuity appointment", message: "One new appointment is waiting in Acuity.", correlation_id: eventKey, }, });
The queue stores neither the raw webhook body nor the appointment ID. Its worker reuses eventKey as PingRoom's Idempotency-Key. Return a 2xx response only after the durable enqueue succeeds; return 500 for a temporary queue failure so Acuity can retry.
Acuity's webhook documentation says it retries network failures and HTTP 500 responses with backoff. It does not promise retries for every error status. Reject a bad signature, malformed body, or irrelevant scope without pretending it was a temporary failure.

Pingo the Pigeon, PingRoom's fictional Chief Marketing Officer, is watching the stopped replay in the illustration. He cannot inspect Acuity, enter a private Room, or send the Ping; people choose the calendar, appointment type, recipients, copy, and response process.
Rehearse the privacy, replay, and retry boundaries
Use a disposable Acuity calendar, a private test Room, and made-up appointment data. This is an illustrative rehearsal, not a delivery guarantee.
- A correctly signed
scheduledevent for the selected calendar and appointment type queues one job. - Replaying that exact request queues no second job and produces no second Ping.
- A different appointment ID produces a different event key.
- An invalid signature is rejected before the body is trusted.
- A
rescheduled,canceled, orchangedevent creates no Ping. - A different calendar, appointment type, extra field, duplicate field, invalid ID, wrong content type, or oversized body is rejected or ignored as designed.
- A queue failure returns
500, leaving Acuity able to retry under its documented policy. - The worker sends only fixed copy and the hashed event key. A PingRoom redirect, throttle, or other non-success remains a retryable queue failure.
- A locked test phone shows no client, service, time, location, intake, payment, or meeting detail.
Our dependency-free fixture passed 29 deterministic assertions with signed Acuity-shaped form bodies, an in-memory queue stand-in, and a local mock of PingRoom's documented contract. It did not use a live Acuity account, public relay, PingRoom Room, client booking, or phone.
Read success and failure correctly
Acuity retries qualifying failures over a documented backoff window and can disable a webhook that keeps failing. Monitor the subscription and keep Acuity authoritative. A durable queue protects the handoff between Acuity and your worker; it does not make the end-to-end workflow exactly once.
PingRoom HTTP 200 with success: true means the worker's request was accepted and created or replayed the Ping for that key. It does not prove every phone displayed an alert. Connectivity, notification permission, Focus modes, battery settings, and Room preferences still affect presentation.
If every appointment would interrupt the same people, use Acuity's native notification or summary options instead. The shared Room route is for one selected booking class whose arrival genuinely changes what a small group should do.
When that boundary fits, create the private destination Room, test one disposable appointment, and leave the booking details in Acuity. If you use Calendly instead, follow the separate Calendly signed-booking recipe.
The illustrations are AI-generated editorial scenes featuring Pingo the Pigeon, PingRoom's official mascot and fictional Chief Marketing Officer, and approved PingRoom avatars. The people and scenario are illustrative, not customers or product screens; the human team remains responsible for product claims and decisions.
Sources
PingRoom
Short-form communication.


