All integrations

Whautomate

MESSAGING · MESSAGING

Clients, contacts, classes, appointments, broadcasts, and segments in the account they connected.

Acts as the person, not as itself

Each user connects their own account. Every call carries both identities — the agent and the person it is acting for — so the agent can never reach past what that individual can already do.

Credentials never touch the agent

Tokens live in the vault and attach server-side at call time. The agent holds a session, not a secret, and revoking access does not mean rotating a key.

Every call on the record

Who asked, which agent acted, which action ran, and the verdict that let it through — one audit trail across every integration, not one per vendor.

What an agent can do

Each action is granted on its own. An agent allowed to read is not thereby allowed to write, and the scope beside each row is what the acting user must have connected for it to run at all.

whautomate_delete_v1_broadcasts_by_broadcastidWRITE

Delete one broadcast, via DELETE /v1/broadcasts/{broadcastId}. PERMANENT, and it takes the campaign's REPORTING with it: `deliveryStats`, `processedStats` and the per-recipient delivery log are attached to this record, and 'List a broadcast's delivery log' has nothing to read afterwards. IT DOES NOT UNSEND ANYTHING -- messages already delivered stay delivered, and whether it stops a campaign that is scheduled or in flight is not documented and was not measured. Answers `{"success": true}`.

api
whautomate_delete_v1_classes_by_classidWRITE

Delete one class, via DELETE /v1/classes/{classId}. PERMANENT, and it is the delete most likely to affect people who do not know it happened: a class carries PARTICIPANTS, real clients with a booking, and the sealed sources do not say whether deleting the session notifies them or merely drops their bookings -- and no credential was available to measure it. Prefer cancelling each participant with 'Cancel a class participant', which fires `class_participant_cancelled` and therefore does tell them, before deleting the session. Answers `{"success": true}`.

api
whautomate_delete_v1_clients_by_clientidWRITE

Delete one client, via DELETE /v1/clients/{clientId}. PERMANENT, and it is the widest delete on this provider: a client is the record every appointment and every class booking is attached to, and Whautomate publishes no recycle bin and no restore endpoint. What happens to that client's existing appointments and class bookings is NOT documented anywhere in the sealed sources and was NOT measured -- no Whautomate credential was available to this build -- so treat the booking history as at risk rather than assuming it survives. Answers `{"success": true}` and nothing else, so confirm by asking for the client again.

api
whautomate_delete_v1_clienttags_by_clienttagidWRITE

Delete one client tag, via DELETE /v1/clientTags/{clientTagId}. PERMANENT, and WIDER THAN IT LOOKS: the tag disappears from every client carrying it, and any SEGMENT whose criteria name it loses that criterion's meaning -- which silently changes the audience of every broadcast aimed at that segment. Whautomate publishes no undo. Answers `{"success": true}`. Deleting the tag does not delete the clients.

api
whautomate_delete_v1_contacts_by_contactidWRITE

Delete one contact, via DELETE /v1/contacts/{contactId}. PERMANENT, with no recycle bin. A contact owns a CONVERSATION -- the message history 'Get a contact's message history' reads -- and the sealed sources do not say whether that history survives the contact, and it was not measured because no credential was available, so treat the conversation as lost. Deleting a contact does NOT delete the client of the same person, if one exists: they are separate records. Answers `{"success": true}`.

api
whautomate_delete_v1_contacttags_by_contacttagidWRITE

Delete one contact tag, via DELETE /v1/contactTags/{contactTagId}. PERMANENT, and WIDER THAN IT LOOKS: the tag disappears from every contact carrying it, and any SEGMENT naming it in its criteria silently changes audience -- which changes who an unsent broadcast reaches. No undo is published. Answers `{"success": true}`. The contacts themselves are untouched.

api
whautomate_delete_v1_segments_by_segmentidWRITE

Delete one segment, via DELETE /v1/segments/{segmentId}. PERMANENT, and there is no update tool to undo it with -- this provider offers segment create, read and delete but no PUT, so a deleted segment must be rebuilt from scratch and its criteria are gone with it. The clients or contacts it selected are untouched. Broadcasts already sent are unaffected; a broadcast still holding this segment id loses its audience. Answers `{"success": true}`.

api
whautomate_delete_v1_servicecategories_by_servicecategoryidWRITE

Delete one service category, via DELETE /v1/serviceCategories/{serviceCategoryId}. PERMANENT. The SERVICES filed under it are not deleted -- a category is a grouping, not a container -- but they lose that grouping, which changes how the business's own booking menus render. Whautomate publishes no undo. Answers `{"success": true}`.

api
whautomate_delete_v1_staffs_by_staffid_availabilityblocks_by_availabilityblockidWRITE

Remove one day's availability for a staff member, via DELETE /v1/staffs/{staffId}/availabilityBlocks/{availabilityBlockId}. PERMANENT, and it CLOSES A REAL CALENDAR: after this the staff member has no declared availability for that day, so online booking and the slot search will offer nothing. Appointments already booked in those hours are NOT cancelled by it -- they simply sit outside any declared block, which is the confusing state to land in by accident. Answers `{"success": true}`; re-create the day with 'Set a staff member's availability for a day' rather than expecting an undo.

api
whautomate_delete_v1_webhooks_by_webhookidWRITE

Delete one webhook subscription, via DELETE /v1/webhooks/{webhookId}. PERMANENT, and it SILENTLY BREAKS whatever integration was listening: Whautomate simply stops calling that endpoint, with no error anywhere for the receiving side to notice. Prefer 'Update a webhook subscription' with `active: false` when you only want to pause delivery. Answers `{"success": true}`.

api
whautomate_get_v1_account_infoREAD

Read the Whautomate account this connection authenticates as, via GET /v1/account-info. Returns the account `name` and `ownerEmail` and nothing else -- it is a small identity call rather than a settings dump. THIS IS ALSO THE CONNECTION'S HEALTH CHECK and the probe this provider declares: a working key answers 200, and a missing or wrong key answers 403 `{"message":"Forbidden"}` (measured 2026-09-25 on both the Global and India hosts). Whautomate never answers 401 here. A 403 does NOT distinguish a missing header from an incorrect key -- both bodies are identical -- and a 429 `{"message":"Too Many Requests"}` means the account's rate budget is spent, not that the key is bad; on a trial account that budget is 100 requests per DAY.

api
whautomate_get_v1_appointmentsREAD

Search one-to-one appointments, via GET /v1/appointments. An APPOINTMENT is a one-to-one booking between a client and a staff member; a group session is a CLASS and has its own tools. Filter by `clientId`, `staffId`, `locationId` and a `startDate`/`endDate` window -- all optional, and they compose. Each entry carries the client, service, staff, location, the local `date`/`time` plus server-computed `startTime`/`endTime` and their UTC twins, `status` and `paymentStatus`. A bare array with no pagination envelope.

api
whautomate_get_v1_appointments_by_appointmentidREAD

Read one appointment, via GET /v1/appointments/{appointmentId}. The full booking: `client`, `service` and any `addOnServices` with their durations and prices, `staff`, `location`, the local `date`/`time` and `timezone`, the computed `startTime`/`endTime` and their `...UTC` twins, `appointmentType`, `bookedFrom`, `status` and `paymentStatus`. Read `status` before rescheduling: an already cancelled appointment is not a candidate.

api
whautomate_get_v1_appointments_slotsREAD

Find bookable times for a service and staff member, via GET /v1/appointments/slots. CALL THIS BEFORE 'Create an appointment'. It answers an array of `{date, timezone, slots: [{startTime, endTime}]}` computed from the staff member's availability blocks, the service's duration and its booking window -- which is why booking a time this did not offer needs `overrideTimeSlotValidation` and double-books a real calendar. It returns nothing when the staff member has no availability block for that day; check with 'List a staff member's availability blocks'. `nextThreeDays` widens a single-day query to a short window.

api
whautomate_get_v1_broadcastsREAD

List bulk messaging campaigns, via GET /v1/broadcasts. A BROADCAST is a bulk send to a segment, a class roster or a previous broadcast's audience. Each entry carries `status`, `scheduleDateTime`, `startedAt`/`completedAt`, a `processedStats` block (`totalCount`/`successCount`/`failureCount`) and a `deliveryStats` block (`sentCount`, `deliveredCount`, `readCount`, `repliedCount`, `failedCount`) -- which is how you tell a campaign that is still going from one that has finished. Filter by date window, location and text. A bare array with no pagination envelope.

api
whautomate_get_v1_broadcasts_by_broadcastidREAD

Read one broadcast, via GET /v1/broadcasts/{broadcastId}. The campaign in full: its `type` and target (`segments`, `targetClass` + `targetClassStatus`, or `retargetBroadcast` + `retargetEngagementType`), its `template` and every parameter array, its `scheduleDateTime` and lifecycle timestamps, and both statistics blocks. This is the call to poll after creating a broadcast: `processedStats` tells you how far the fan-out has got, `deliveryStats` what the recipients did.

api
whautomate_get_v1_broadcasts_by_broadcastid_logsREAD

List per-recipient delivery records for a broadcast, via GET /v1/broadcasts/{broadcastId}/logs. The per-recipient detail behind the aggregate `deliveryStats` on 'Get a broadcast' -- who it went to and what happened. This is where a campaign that reports failures is diagnosed. A bare array with no pagination envelope and, in the sealed export, no element schema, so read the first page before writing code against its shape. Paged with `page`/`limit`: a large campaign's log is the biggest read on this provider and a trial account has 100 requests a DAY.

api
whautomate_get_v1_classesREAD

Search group sessions, via GET /v1/classes. A CLASS is a group session -- a service, a staff member, a location, a date and a capacity -- as opposed to an APPOINTMENT, which is one-to-one. Each entry carries `bookedParticipants` against `numberOfParticipants`, which is the pair to read before booking anybody in. Filter by `staffId`, `locationId` and a `startDate`/`endDate` window. A bare array with no pagination envelope.

api
whautomate_get_v1_classes_by_classidREAD

Read one class, via GET /v1/classes/{classId}. The session's `service`, `staff`, `location`, local `date`/`time` and `timezone`, the computed `startTime`/`endTime` and their UTC twins, `status`, and `bookedParticipants` against `numberOfParticipants`. It does NOT list who is booked -- 'List a class's participants' does that.

api
whautomate_get_v1_classes_by_classid_participantsREAD

List who is booked into a class, via GET /v1/classes/{classId}/participants. The clients booked into this session, each with a participant `id` that is DISTINCT FROM THE CLIENT ID -- it is the booking's id, and it is what 'Cancel a class participant' takes. Also carries `status` (booked, waitlisted, cancelled, visited, no-show) and `paymentStatus`. A bare array; the export declares no element schema for it, so the per-participant reader is the documented shape.

api
whautomate_get_v1_classes_by_classid_participants_by_participantidREAD

Read one class booking, via GET /v1/classes/{classId}/participants/{participantId}. Both ids are required and positional. `participantId` is the BOOKING's id from 'List a class's participants', not the client's. The reply describes the session -- `service`, `staff`, `location`, the local and UTC times, `status` and the capacity counters -- which is the shape the export declares for this route.

api
whautomate_get_v1_classes_clients_by_clientid_bookingsREAD

List the classes one client is booked into, via GET /v1/classes/clients/{clientId}/bookings. The other direction from 'List a class's participants': that one answers who is in a session, this one answers which sessions a person is in. Takes a CLIENT id -- class bookings belong to clients, not to contacts. Narrow with `startDate`/`endDate` and `locationId`. A bare array with no pagination envelope. NOTE THE PATH SHAPE: `clients` is nested under `/v1/classes`, so this route is a sibling of the class ids and is mounted before them.

api
whautomate_get_v1_clientsREAD

Search the account's clients, via GET /v1/clients. A CLIENT is Whautomate's booking-and-billing person -- `fullName`, `phone` plus a separate `countryCode`, `dob`, emergency contact, `primaryStaff` -- and it is NOT the same record as a CONTACT, which is the messaging-inbox person with its own ids, its own tags and its own list tool. Mixing the two is the most common mistake on this provider. Every filter is optional and they compose; `tags` filters on the CLIENT tag namespace (`/v1/clientTags`). The reply is a bare array with no pagination envelope.

api
whautomate_get_v1_clients_by_clientidREAD

Read one client in full, via GET /v1/clients/{clientId}. The whole record: name, contact details, `dob`, `gender`, `maritalStatus`, address, `identificationNumber`, the emergency contact, `primaryLocation`, `primaryStaff`, `referralSource`, `registrationDate`, `tags` and `customFields`. NOTE THE PHOTO FIELD ASYMMETRY: reads return `photo`, writes take `photoUrl`. This returns a CLIENT; for the messaging record use 'Get a contact'.

api
whautomate_get_v1_clienttagsREAD

List the CLIENT tag vocabulary, via GET /v1/clientTags. The tags that may be applied to CLIENTS. THERE ARE TWO SEPARATE TAG NAMESPACES on this provider and they do not share names: this one, and `/v1/contactTags` for contacts. A tag that exists here is not usable on a contact and vice versa. Each entry carries `id`, `name`, `canonical` and timestamps. A bare array with no pagination envelope.

api
whautomate_get_v1_clienttags_by_clienttagidREAD

Read one client tag, via GET /v1/clientTags/{clientTagId}. Its `name`, its `canonical` form and its timestamps. This addresses the CLIENT namespace: a contact tag id here will not resolve.

api
whautomate_get_v1_contactsREAD

Search the account's messaging contacts, via GET /v1/contacts. A CONTACT is Whautomate's messaging-inbox person -- `name`, a single `phoneNumber` that INCLUDES the country code, and a funnel `stage` -- and it is NOT the same record as a CLIENT, which is the booking and billing person with a split phone/countryCode and its own list tool. Contact ids are what every send tool takes. `channel` narrows to one messaging channel and `tags` filters the CONTACT tag namespace (`/v1/contactTags`). A bare array with no pagination envelope.

api
whautomate_get_v1_contacts_by_contactidREAD

Read one contact, via GET /v1/contacts/{contactId}. `name`, `phoneNumber` (country code included), `location`, funnel `stage`, `tags` and `customFields`. This is the MESSAGING record; for the booking record use 'Get a client'. The id here is what 'Get a contact's message history' and every send tool take.

api
whautomate_get_v1_contacttagsREAD

List the CONTACT tag vocabulary, via GET /v1/contactTags. The tags that may be applied to CONTACTS. This is the other of the two separate tag namespaces -- `/v1/clientTags` holds the client one -- and a name in one is not usable in the other. Each entry carries `id`, `name`, `canonical` and timestamps. A bare array with no pagination envelope.

api
whautomate_get_v1_contacttags_by_contacttagidREAD

Read one contact tag, via GET /v1/contactTags/{contactTagId}. Its `name`, its `canonical` form and its timestamps. This addresses the CONTACT namespace; a client tag id here will not resolve.

api
whautomate_get_v1_locationsREAD

List the account's business locations, via GET /v1/locations. START HERE ON A NEW CONNECTION. Whautomate models a business as one or more physical locations, and a `location.id` is required on every send, every broadcast, every appointment, every class and every segment -- so almost nothing else on this provider can be called until you have read one from here. Each entry carries `id`, `title`, `addressLine1`/`addressLine2`, `city`, `district`, `postalCode` and `phone`. The reply is a BARE ARRAY with no pagination envelope; walk `page` until a short or empty page comes back.

api
whautomate_get_v1_locations_by_locationidREAD

Read one business location, via GET /v1/locations/{locationId}. The same shape 'List business locations' returns, for a single id: `title`, the two address lines, `city`, `district`, `postalCode` and `phone`. Useful for confirming which site an id refers to before booking into it or broadcasting from it.

api
whautomate_get_v1_messages_by_contactidREAD

Read the conversation with one contact, via GET /v1/messages/{contactId}. Takes a CONTACT id, not a client id -- conversations belong to contacts. Each message carries its `channel`, `text`, `mediaUrl`, `type`, `status`, `errorMessage`, `sentBy`, any `options` buttons, and `isIncoming`, which is the field that says who spoke. THIS IS THE ONLY READ ON THIS SURFACE THAT RETURNS CUSTOMER MESSAGE CONTENT, so a connection granted it can read every conversation in the account. Paged with `page`/`limit` and a bare array; no pagination envelope.

api
whautomate_get_v1_segmentsREAD

List saved audiences, via GET /v1/segments. A SEGMENT is a saved audience and is what a BROADCAST is aimed at, so this is the call that decides who a campaign reaches. Each segment declares `type`: `client` or `contact`, which population it selects from -- and because clients and contacts have separate tag namespaces, a segment of the wrong type quietly addresses the wrong people. A bare array with no pagination envelope.

api
whautomate_get_v1_segments_by_segmentidREAD

Read one segment, via GET /v1/segments/{segmentId}. READ THIS BEFORE BROADCASTING TO IT. The sealed export declares no properties on this operation's 200 response -- unusually for this provider -- so what comes back is whatever the segment model holds: its `name`, its `type` and its `criteria`. It does NOT return the members; there is no tool on this surface that enumerates who a segment resolves to, which is why the criteria are worth reading carefully before a send.

api
whautomate_get_v1_servicecategoriesREAD

List the groupings services are filed under, via GET /v1/serviceCategories. Service categories are how a business groups its services for its own menus and booking pages. Each carries `id`, `name`, a server-derived `slug`, its `location` and timestamps. The ids are what a service's `categories` array takes. A bare array with no pagination envelope.

api
whautomate_get_v1_servicecategories_by_servicecategoryidREAD

Read one service category, via GET /v1/serviceCategories/{serviceCategoryId}. Its `name`, `slug`, `location` and timestamps. It does not list the services filed under it -- use 'List services' and read each service's `categories`.

api
whautomate_get_v1_servicesREAD

List what the business sells, via GET /v1/services. A service is one bookable or billable thing, and its `serviceType` decides which tools can use it: `APPOINTMENT` for one-to-one bookings, `CLASS` for group sessions, `ADD_ON` for something billed alongside another service. Booking an `APPOINTMENT`-type service into a class, or the reverse, is the usual cause of a refused booking. Filter with `serviceType` and `locationId`. A bare array with no pagination envelope.

api
whautomate_get_v1_services_by_serviceidREAD

Read one service in full, via GET /v1/services/{serviceId}. Everything the listing carries plus the parts that decide how it is sold and delivered: `sellingPrice` and `costPrice`, `durationMinutes`, `credits`, the `categories` and `staffs` it is restricted to, its `addOnServices` and `addOnAppliesTo`, its `advanceSettings` booking window and buffer, and its `taxOverrides`. Read this before creating an appointment to confirm the duration and which staff may deliver it.

api
whautomate_get_v1_staffsREAD

List the account's staff members, via GET /v1/staffs. Each entry carries `id`, `firstName`, `lastName`, `email`, `phone` and `countryCode`, `gender`, `active`, and `locations` -- the ids of the sites that staff member works at. The ids are what every booking tool takes as `staff.id` and what 'List staff availability blocks' and 'List available appointment slots' filter on. A bare array with no pagination envelope.

api
whautomate_get_v1_staffs_by_staffidREAD

Read one staff member, via GET /v1/staffs/{staffId}. Name, contact details, `gender`, whether they are `active`, and the `locations` they work at. Read this before assigning an appointment: a staff member who does not work at the booking's location, or who is not `active`, is the usual cause of a booking the calendar refuses.

api
whautomate_get_v1_staffs_by_staffid_availabilityblocksREAD

List when a staff member is available, via GET /v1/staffs/{staffId}/availabilityBlocks. An availability block is one DAY's working slots for one staff member: a `date`, a `timezone` and a list of `{startTime, endTime}` with their `...UTC` twins. This is the roster the appointment slot search runs against, so it is the right place to look when 'List available appointment slots' returns nothing. Narrow with `startDate`/`endDate`; a bare array comes back.

api
whautomate_get_v1_staffs_by_staffid_availabilityblocks_by_availabilityblockidREAD

Read one availability block, via GET /v1/staffs/{staffId}/availabilityBlocks/{availabilityBlockId}. One day's slots for one staff member: `date`, `timezone`, the `slots` array and the `staff` the block belongs to. Both ids are required and both are positional -- the block id alone will not address it.

api
whautomate_get_v1_webhooksREAD

List the account's webhook subscriptions, via GET /v1/webhooks. Each subscription carries its `name`, the `serverUrl` Whautomate posts to, the `events` it is subscribed to, whether it is `active`, and its `webhookHeaders`. THOSE HEADERS ARE READ BACK IN FULL, including any shared secret stored in one -- this API key can therefore read every webhook secret on the account. A bare array with no pagination envelope.

api
whautomate_get_v1_webhooks_by_webhookidREAD

Read one webhook subscription, via GET /v1/webhooks/{webhookId}. The subscription's `name`, `serverUrl`, `events`, `active` flag, `webhookHeaders` and timestamps. As with the listing, the header VALUES come back in full, so anything stored there is readable by any holder of this key.

api
whautomate_post_v1_appointmentsWRITE

Create a one-to-one appointment, via POST /v1/appointments. BOOKS A REAL PERSON'S CALENDAR and, depending on the account's automations, sends the client a confirmation message. `client`, `service`, `location`, `date` and `time` are required; `client` takes either an existing `id` or `fullName` + `phone` + `countryCode`, in which case WHAUTOMATE CREATES THE CLIENT -- so a typo'd phone number produces a duplicate person rather than an error. Use 'List available appointment slots' to pick a `time` the calendar will accept. `overrideTimeSlotValidation` bypasses that check and double-books; send it only when a human asked for that exact time. Fires `appointment_scheduled`.

api
whautomate_post_v1_appointments_cancelWRITE

Cancel a booked appointment, via POST /v1/appointments/cancel. A POST TO A LITERAL PATH with `appointmentId` in the BODY, not a DELETE. THE APPOINTMENT RECORD SURVIVES with a cancelled `status` -- this is a state change rather than a deletion, which is why it is not marked destructive -- but it fires `appointment_cancelled`, and on an account with the usual automations that MESSAGES THE CLIENT. There is no uncancel: rebooking means 'Book an appointment' again. `cancellationReason` is free text and is worth sending, because it is what a human will read later. The declared 200 carries no properties; confirm with 'Get an appointment'.

api
whautomate_post_v1_appointments_rescheduleWRITE

Move an appointment to a new time, via POST /v1/appointments/reschedule. A POST TO A LITERAL PATH, not a PUT on the appointment: the id travels in the BODY as `appointmentId`. Moves the booking to `date` + `time` in `timezone` and fires `appointment_rescheduled`, which typically messages the client -- so this is visible to a real person, not a silent record edit. It does not take a new staff member or service; only the time moves. Check the new time against 'List available appointment slots' first, because this call has no override flag to fall back on.

api
whautomate_post_v1_broadcastsWRITE

Create and send a bulk messaging campaign, via POST /v1/broadcasts. THE HIGHEST-CONSEQUENCE CALL ON THIS PROVIDER, and the reason it carries the confirm gate although it is not a DELETE: it sends real WhatsApp messages to every member of a segment, a class roster or a previous campaign's audience, it is billable per message, and Whautomate publishes NO RECALL and no tool on this surface that stops a campaign once created -- `deliveryStats` is the only thing that comes back. Omitting `scheduleDateTime` sends IMMEDIATELY. `type` chooses the audience and decides which of the target fields is read: `client`/`contact` use `segments`, `class` uses `targetClass` + `targetClassStatus`, `retarget` uses `retargetBroadcast` + `retargetEngagementType`. READ 'Get a segment' FIRST -- there is no tool that enumerates a segment's members, so the audience size is not knowable from this API before you send. `deliveryStats` and `processedStats` are server-maintained; sending them sets nothing.

api
whautomate_post_v1_classesWRITE

Create a group session, via POST /v1/classes. `service`, `staff`, `location`, `date` and `time` are required, and the service should be one whose `serviceType` is `CLASS` -- an `APPOINTMENT` service scheduled as a class is the usual cause of a session nobody can book into. `numberOfParticipants` is the capacity, and it is what decides whether 'Add a participant to a class' books or waitlists. PUTS A SESSION ON A REAL STAFF MEMBER'S CALENDAR and, if the account allows online booking, makes it publicly bookable.

api
whautomate_post_v1_classes_by_classid_participants_addWRITE

Book a client into a class, via POST /v1/classes/{classId}/participants/add. BOOKS A REAL PERSON INTO A REAL SESSION and fires `class_participant_scheduled` (or `class_participant_waitlisted`), which on the usual account automations messages them. `client` takes either an existing `id` or `fullName` + `phone` + `countryCode`, in which case WHAUTOMATE CREATES THE CLIENT -- so a typo makes a duplicate person rather than an error. `status` is `BOOKED` or `WAITLISTED`; check `bookedParticipants` against `numberOfParticipants` on 'Get a class' to know which is appropriate.

api
whautomate_post_v1_classes_by_classid_participants_cancelWRITE

Cancel one client's place in a class, via POST /v1/classes/{classId}/participants/cancel. `participantId` is the BOOKING's id from 'List a class's participants', not the client's, and it travels in the body while the class id is in the path. The booking record SURVIVES with a cancelled `status` -- a state change rather than a deletion, which is why this is not marked destructive -- and it fires `class_participant_cancelled`, so on the usual account automations THE CLIENT IS TOLD. There is no uncancel; rebooking means 'Add a participant to a class' again, which may land them on the waitlist if the place has gone.

api
whautomate_post_v1_clientsWRITE

Create a client record, via POST /v1/clients. `fullName`, `phone` and `countryCode` are the required trio, and the last two are SEPARATE FIELDS -- putting the country code into `phone` produces a client whose number will not match an inbound WhatsApp thread. A client is the record appointments and class bookings are made against; if what you actually want is to message somebody, create a CONTACT instead. `tags` here are CLIENT tags and are set wholesale; `photoUrl` is fetched by Whautomate. The 200 carries the created record including the `id` the booking tools need.

api
whautomate_post_v1_clients_by_clientid_tags_addWRITE

Add tags to one client, via POST /v1/clients/{clientId}/tags/add. Incremental, unlike sending `tags` on 'Update a client', which replaces them. These are CLIENT tags -- the `/v1/clientTags` namespace -- and adding one fires the `client_tag_added` webhook event, which Whautomate automations commonly key on, so a tag added here can start a real workflow. The 200 answers the client's id and its full tag list afterwards. WHAUTOMATE'S EXPORTS DECLARE NO REQUEST BODY FOR THIS OPERATION: the `tags` field below is inferred from that response shape and the operation's name rather than read from a schema, and this call was never made live because no credential was available.

api
whautomate_post_v1_clients_by_clientid_tags_removeWRITE

Remove tags from one client, via POST /v1/clients/{clientId}/tags/remove. Takes the tags off this client only; the tag itself survives in the `/v1/clientTags` namespace and on every other client. Fires the `client_tag_removed` webhook event. Not marked destructive -- nothing is deleted -- but a client can drop out of a tag-based SEGMENT because of it, and therefore out of the audience of a broadcast that has not yet been sent. Same body caveat as 'Add tags to a client': the exports declare none, so the shape is inferred.

api
whautomate_post_v1_clienttagsWRITE

Add a tag to the CLIENT tag vocabulary, via POST /v1/clientTags. Only `name` matters; `canonical` and the timestamps are server-maintained and sending them changes nothing. Creating a tag does not apply it to anybody -- use 'Add tags to a client' for that. This adds to the CLIENT namespace only.

api
whautomate_post_v1_contactsWRITE

Create a messaging contact, via POST /v1/contacts. `name`, `phoneNumber` and `location` are required. `phoneNumber` INCLUDES the country code -- the opposite of a client, which splits them -- and it is the key Whautomate matches an inbound WhatsApp/Telegram/Instagram thread on, so a number in the wrong format produces a contact that never joins its own conversation. `stage` values are exact, spaces and parentheses included. Creating a contact fires the `contact_created` webhook event.

api
whautomate_post_v1_contacts_by_contactid_tags_addWRITE

Add tags to one contact, via POST /v1/contacts/{contactId}/tags/add. Incremental, unlike sending `tags` on 'Update a contact'. These are CONTACT tags -- the `/v1/contactTags` namespace -- and adding one fires `contact_tag_added`, which Whautomate automations key on, so a tag added here can start a real workflow and can pull the contact into a tag-based segment. TWO THINGS THE LEDGER GOT WRONG HERE, both corrected in this build: it declared a required path parameter named `proxy` that the URL has no slot for (inherited from the AWS API Gateway `{proxy+}` resource this route sits under), and it declared no request body at all -- so the `tags` field below is inferred from the documented response rather than read from a schema.

api
whautomate_post_v1_contacts_by_contactid_tags_removeWRITE

Remove tags from one contact, via POST /v1/contacts/{contactId}/tags/remove. Takes the tags off this contact only; the tag survives in the vocabulary and on every other contact. Fires `contact_tag_removed`. Not destructive -- nothing is deleted -- but it can drop the contact out of a tag-based SEGMENT and therefore out of an unsent broadcast's audience. Same two ledger corrections as 'Add tags to a contact': the spurious `proxy` path parameter is dropped and the body shape is inferred, because the exports declare none.

api
whautomate_post_v1_contacttagsWRITE

Add a tag to the CONTACT tag vocabulary, via POST /v1/contactTags. Only `name` matters; `canonical` and the timestamps are server-maintained. Creating a tag applies it to nobody -- use 'Add tags to a contact' for that. This adds to the CONTACT namespace only; the client vocabulary has its own create tool.

api
whautomate_post_v1_messages_instagram_sendmediaWRITE

Send a file over Instagram, via POST /v1/messages/instagram/sendmedia. SENDS A REAL MESSAGE, with no unsend. `contact`, `location`, `mediaUrl` and `mimeType` are required; contact id only, no client and no raw phone number. WHAUTOMATE FETCHES `mediaUrl` from its own infrastructure, so an unreachable or expired link produces a send that this call accepts and the recipient never receives.

api
whautomate_post_v1_messages_instagram_sendtemplateWRITE

Send a card carousel with buttons over Instagram, via POST /v1/messages/instagram/sendtemplate. NOT THE SAME KIND OF 'TEMPLATE' AS THE WHATSAPP ONE: there is no approved template to name here. This is Meta's generic-template message -- a list of `elements`, each a card with a `title`, optional `subtitle` and `image_url`, and `buttons`. The button `type` vocabulary is NARROWER than Messenger's: `web_url` and `postback` only, with no `phone_number`. `contact`, `location` and `elements` are required. Sends a real message, with no unsend.

api
whautomate_post_v1_messages_instagram_sendtextWRITE

Send a plain Instagram DM, via POST /v1/messages/instagram/sendtext. SENDS A REAL MESSAGE, with no unsend. `contact`, `location` and `textMessage` are required. UNLIKE THE WHATSAPP SENDS, this channel takes ONLY a contact id -- there is no `client` and no `recepient` phone-number option, because an Instagram thread exists only against a contact Whautomate already knows. Omit `scheduleDateTime` to send now.

api
whautomate_post_v1_messages_messenger_sendmediaWRITE

Send a file over Messenger, via POST /v1/messages/messenger/sendmedia. SENDS A REAL MESSAGE, with no unsend. `contact`, `location`, `mediaUrl` and `mimeType` are required. WHAUTOMATE FETCHES `mediaUrl` from its own infrastructure, so the link must be publicly reachable when it does; a dead link produces a send this call accepts and the recipient never receives.

api
whautomate_post_v1_messages_messenger_sendtemplateWRITE

Send a card carousel with buttons over Messenger, via POST /v1/messages/messenger/sendtemplate. Meta's generic-template message: a list of `elements`, each a card with a `title`, optional `subtitle` and `image_url`, and `buttons`. THE BUTTON VOCABULARY IS WIDER HERE THAN ON INSTAGRAM -- `web_url`, `postback` AND `phone_number` -- which is the one difference between the two otherwise identical bodies, so a card built for Messenger may not transfer to Instagram unchanged. `contact`, `location` and `elements` are required. Sends a real message, with no unsend.

api
whautomate_post_v1_messages_messenger_sendtextWRITE

Send a plain Messenger message, via POST /v1/messages/messenger/sendtext. SENDS A REAL MESSAGE, with no unsend. `contact`, `location` and `textMessage` are required; contact id only, with no client and no raw phone-number option. Meta applies its own 24-hour standard messaging window to Messenger, so a message to somebody who has not interacted recently may be refused at the channel rather than here.

api
whautomate_post_v1_messages_telegram_sendmediaWRITE

Send a file over Telegram, via POST /v1/messages/telegram/sendmedia. SENDS A REAL MESSAGE, with no unsend. `contact`, `location`, `mediaUrl` and `mimeType` are required. WHAUTOMATE FETCHES `mediaUrl` from its own infrastructure, so the link must be publicly reachable and still valid at fetch time; a dead link produces a send this call accepts and the recipient never receives.

api
whautomate_post_v1_messages_telegram_sendtemplateWRITE

Send a Telegram message carrying buttons and optional media, via POST /v1/messages/telegram/sendtemplate. A THIRD, DIFFERENT SHAPE OF 'TEMPLATE' ON THIS PROVIDER: not WhatsApp's approved-template reference and not Instagram/Messenger's card carousel, but a single `textMessage` with a `buttons` array and an optional media header. `buttons`, `contact`, `location` and `textMessage` are required. `headerType` (`IMAGE`, `VIDEO`, `DOCUMENT`) goes with `mediaUrl` and `mimeType` when there is a media header. Sends a real message, with no unsend.

api
whautomate_post_v1_messages_telegram_sendtextWRITE

Send a plain Telegram message, via POST /v1/messages/telegram/sendtext. SENDS A REAL MESSAGE, with no unsend. `contact`, `location` and `textMessage` are required; contact id only, with no client and no raw phone-number option. Telegram has no 24-hour messaging window, so plain text is usable to open a conversation on this channel where it is not on WhatsApp.

api
whautomate_post_v1_messages_whatsapp_sendmediaWRITE

Send a file over WhatsApp, via POST /v1/messages/whatsapp/sendmedia. SENDS A REAL MESSAGE, billable, with no unsend. `mediaUrl`, `mimeType` and `location` are required, plus one of `contact`, `client` or `recepient`. THE FILE IS FETCHED BY WHAUTOMATE from the URL you give -- nothing is uploaded through this API -- so the link must be reachable from the public internet and must still be valid when Whautomate fetches it, which is the usual cause of a send that this call reports as accepted and the recipient never sees. Same 24-hour window rule as the text send.

api
whautomate_post_v1_messages_whatsapp_sendtemplateWRITE

Send an approved WhatsApp template, via POST /v1/messages/whatsapp/sendtemplate. THE WAY TO START A WHATSAPP CONVERSATION. Outside the 24-hour customer-service window WhatsApp permits only approved templates, so this -- not 'Send a WhatsApp text message' -- is what reaches somebody who has not messaged recently. `template` (`name` + `language`) and `location` are required; the template must ALREADY EXIST AND BE APPROVED in Whautomate/Meta, because nothing on this surface creates one. Fill the template's placeholders in ORDER through `headerTextParameters`, `bodyTextParameters` and `buttonUrlParameters`; `headerMediaUrl`, `locationParameters` and `flowParameters` serve the media, location and Flow header types. One recipient only: `contact`, `client` or `recepient` (that spelling). Billable, no unsend.

api
whautomate_post_v1_messages_whatsapp_sendtextWRITE

Send a plain WhatsApp text, via POST /v1/messages/whatsapp/sendtext. SENDS A REAL MESSAGE TO A REAL PERSON, billable, with no unsend. `location` and `textMessage` are required, plus exactly one recipient: `contact` (a contact id), `client` (a client id) or `recepient` (a raw phone number) -- NOTE THE SPELLING `recepient`, which is Whautomate's own and must not be corrected. WhatsApp only permits free-form text inside an open 24-hour customer-service window; outside it a template is required, so use 'Send a WhatsApp template message' when you are starting the conversation. Omit `scheduleDateTime` to send now; nothing on this surface cancels a scheduled send.

api
whautomate_post_v1_segmentsWRITE

Create a saved audience, via POST /v1/segments. `name`, `type` and `location` are required. `type` -- `client` or `contact` -- is the decision that matters: it picks which population and which TAG NAMESPACE the criteria are read against, so a segment created as `contact` with client tag names selects nobody. `criteria.contactStage` accepts a SHORTER list of stages than a contact record does: `Engaged`, `Follow-up Required`, `Lost` and `Inactive` are settable on a contact but are not offered here. Every criterion is optional and they compose; a segment with no criteria matches the whole population.

api
whautomate_post_v1_servicecategoriesWRITE

Create a service grouping, via POST /v1/serviceCategories. `name` and `location` are required; `slug` is derived by Whautomate and sending one renames nothing. Creating a category files no services under it -- set a service's `categories` array with 'Update a service' for that. The 200 carries the created category and its id.

api
whautomate_post_v1_servicesWRITE

Create something the business sells, via POST /v1/services. `name`, `serviceType`, `sellingPrice` and `location` are required. `serviceType` is the decision that matters most and cannot be guessed later by the booking tools: `APPOINTMENT`, `CLASS` or `ADD_ON`. A service created with `allowOnlineBooking` and `acceptPaymentOnline` is IMMEDIATELY SELLABLE to the public at `sellingPrice`, so set the price deliberately. The declared 200 response carries no properties in the sealed export -- read the service back with 'Get a service' to learn its id.

api
whautomate_post_v1_staffs_by_staffid_availabilityblocksWRITE

Create an availability block for one staff member and day, via POST /v1/staffs/{staffId}/availabilityBlocks. Declares when this staff member can be booked on `date`: a `slots` array of `{startTime, endTime}` local to `timezone`. THIS DECIDES WHAT CUSTOMERS CAN BOOK -- narrowing a day's slots does not cancel appointments already made in the removed hours, and widening them opens real availability on a real person's calendar. IT ANSWERS AN ARRAY, not a single object: the one write on this surface that does, because it returns the day's blocks rather than just the one created.

api
whautomate_post_v1_webhooksWRITE

Subscribe an endpoint to Whautomate events, via POST /v1/webhooks. `name`, `serverUrl`, `events` and `active` are all required. `serverUrl` IS A URL WHAUTOMATE WILL CALL from its own infrastructure -- point it at something you control and that expects Whautomate's payloads. The event names are exact lower_snake_case; the four `invoice_*` ones are worth noting because this API has no invoicing tools, so a webhook is the only way to see invoicing activity here. Anything put in `webhookHeaders` is stored by Whautomate and read back by the two read tools above.

api
whautomate_put_v1_broadcasts_by_broadcastidWRITE

Change a broadcast, via PUT /v1/broadcasts/{broadcastId}. A PUT carrying the whole campaign, so send what you want it to end up as rather than a patch. ITS EFFECT ON A CAMPAIGN THAT IS SCHEDULED OR IN FLIGHT IS NOT DOCUMENTED in any sealed source and was not measured -- no Whautomate credential was available to this build -- so do not assume it can retarget or delay a send that is already moving. It is marked a write rather than destructive because it edits a record, but on a scheduled campaign it may change who gets messaged; read 'Get a broadcast' and check `status`, `startedAt` and `scheduleDateTime` first.

api
whautomate_put_v1_classes_by_classidWRITE

Change a class's time, staff or capacity, via PUT /v1/classes/{classId}. A PUT with the same required fields as the create, so send the whole session rather than a patch. MOVING A CLASS MOVES IT FOR EVERYONE ALREADY BOOKED, and lowering `numberOfParticipants` below `bookedParticipants` leaves the session oversubscribed -- the sealed sources do not say which participants lose their place, and no credential was available to measure it. Read 'Get a class' first and check the two counts.

api
whautomate_put_v1_clients_by_clientidWRITE

Replace a client's details, via PUT /v1/clients/{clientId}. A PUT, and the body carries the same required trio as a create (`fullName`, `phone`, `countryCode`), so treat this as REPLACING the record rather than patching it: send the fields you want the client to end up with, not only the ones you are changing. `tags` sent here replace the client's tags wholesale -- use 'Add tags to a client' and 'Remove tags from a client' to change them incrementally. Not marked destructive: the client survives, though a partial body can lose detail.

api
whautomate_put_v1_contacts_by_contactidWRITE

Replace a contact's details, via PUT /v1/contacts/{contactId}. A PUT with the same required fields as a create, so send the whole record rather than a patch. Changing `stage` is the ordinary use and is how a contact is moved through the funnel. `tags` sent here REPLACE the contact's tags -- use 'Add tags to a contact' and 'Remove tags from a contact' to change them one at a time, which is also what fires the tag webhook events.

api
whautomate_put_v1_services_by_serviceidWRITE

Change a service's price or settings, via PUT /v1/services/{serviceId}. CHANGING `sellingPrice` CHANGES WHAT EVERY FUTURE BOOKING IS CHARGED, and setting `active` to false removes the service from sale without cancelling anything already booked. THE SEALED EXPORTS DECLARE NO REQUEST BODY FOR THIS OPERATION AT ALL, while `POST /v1/services` declares the full Service model and this operation's own 200 response IS that model -- so the fields below are taken from those two places and NO field is marked required, because the export says nothing about which a PUT may omit. Send the whole service you want to end up with rather than a patch, and read it back afterwards.

api
whautomate_put_v1_webhooks_by_webhookidWRITE

Change a webhook subscription, via PUT /v1/webhooks/{webhookId}. A PUT carrying the whole subscription, so send the complete `events` list rather than the ones you are adding -- a short list silently UNSUBSCRIBES the rest, and the symptom is an integration that stops seeing some events with nothing logged anywhere. Setting `active` to false pauses delivery without losing the subscription, which is the safe way to stop a noisy endpoint.

api

Put Whautomate behind one governed endpoint.

Same permissions, same audit trail, whatever else you connect next.