Online
--
Registered
--
Browsers
--
Uptime
--
Status
--
Clinics
Clinic Data
| Loading... |
|---|
Total Bookings
--
Success Rate
--
Failed
--
Avg Duration
--
Daily Trend (7 days)
By Clinic
Booking Activity
| Time | Clinic | System | Action | Status | Error | Duration |
|---|---|---|---|---|---|---|
| Select a clinic or click Refresh | ||||||
Booking Records
| Time | Clinic | Patient | Contact | Appointment | System | Status |
|---|---|---|---|---|---|---|
| Loading... | ||||||
User Accounts
Instances
--
Online
--
Total Clinics
--
Total Browsers
--
Instances
All Clinics
| Clinic | Type | Instance | Status |
|---|---|---|---|
| Loading... | |||
Add Clinic to Fleet
Every request needs the header
x-api-key: YOUR_CLINIC_API_KEY — grab it from the Clinics tab. All bodies are JSON with Content-Type: application/json.
Base URL
https://admin.hookneural.com
Use this as the base URL for all webhook endpoints below. Example:
https://admin.hookneural.com/api/webhook/jane/bookGHL Webhooks — Jane
Voice AI webhook endpoints for Jane clinics. URL pattern:
/api/webhook/jane/{action}
POST
/api/webhook/jane/book
Book appointment
{
"customData": {
"staff_member": "info@drsiva.ca",
"treatment_name": "Free Initial Aesthetic Consult (Aesthetics) (30min)"
},
"calendar": {
"startTime": "{{datetime_formatter.1.datetime}}",
"endTime": "{{datetime_formatter.2.datetime}}",
"selectedTimezone": "America/Toronto",
"appointmentId": "{{appointment.id}}"
},
"first_name": "{{contact.first_name}}",
"last_name": "{{contact.last_name}}",
"email": "{{contact.email}}",
"phone": "{{contact.phone}}"
}
staff_member — staff email address or full name (e.g.
treatment_name — treatment name or partial match (e.g.
Auto-creates patient if not found (searches by email + name match first).
"info@drsiva.ca" or "Dr. Smith"). Resolved against Jane staff list. Numeric ID also accepted.treatment_name — treatment name or partial match (e.g.
"Free Initial Aesthetic Consult"). Also accepts treatment_num as numeric ID.Auto-creates patient if not found (searches by email + name match first).
POST
/api/webhook/jane/cancel
Cancel appointment
{
"calendar": {
"appointmentId": "{{appointment.id}}"
},
"notifyPatient": true // optional — sends cancellation notice
}
POST
/api/webhook/jane/reschedule
Reschedule appointment
{
"customData": {
"staff_member": PUT_STAFF_NUMBER // optional — keeps same if omitted
},
"calendar": {
"appointmentId": "{{appointment.id}}",
"startTime": "{{datetime_formatter.1.datetime}}",
"endTime": "{{datetime_formatter.2.datetime}}",
"selectedTimezone": "PUT_TIME_ZONE"
},
"notifyPatient": true // optional
}
Cancels old appointment, books new one with same patient & treatment at the new time.
POST
/api/webhook/jane/patients
Search or create patient
// Search { "action": "search", "query": "john@example.com" } // Create (checks for existing by email first) { "action": "create", "firstName": "John", "lastName": "Doe", "email": "john@example.com", // optional "phone": "416-555-1234" // optional }
POST
/api/webhook/jane/availability
Check open slots
{
"startDate": "2026-04-07",
"endDate": "2026-04-11",
"staffMemberIds": [42], // optional — filter by staff
"locationId": 1 // optional, default 1
}
GHL Webhooks — Ocean
Voice AI webhook endpoints for OceanMD clinics. URL pattern:
/api/webhook/ocean/{action}
POST
/api/webhook/ocean/book
Book appointment (Playwright RPA)
{
"firstName": "John",
"lastName": "Doe",
"healthCardNumber": "1234567890",
"dateOfBirth": "01/15/1990", // MM/DD/YYYY or YYYY-MM-DD
"date": "2026-04-07", // YYYY-MM-DD
"time": "10:00 AM", // h:mm AM/PM
"phone": "416-555-1234", // patient phone number
"email": "patient@example.com", // patient email
"appointmentType": "New Patient", // optional
"reasonForVisit": "Consultation", // optional, default "Consultation"
"provider": "Dr. Smith" // optional
}
Uses Playwright to automate the OceanMD booking page with real patient data. Requires a booking URL set on the clinic.
dateOfBirth accepts
dateOfBirth accepts
MM/DD/YYYY (slash) or YYYY-MM-DD (dash). Optional fields can also be sent inside a customData object as appointment_type, reason_for_visit, provider.
POST
/api/webhook/ocean/availability
Get cached available slots
{
"maxDays": 7 // optional, default 7
}
Returns cached slot data from the 15-second refresh loop. Also pushed to GHL via
{{ custom_values.availability }}.Direct API — Appointments
Lower-level endpoints. Use the webhook endpoints above for GHL integration.
GET
/appointments/:id
Get details by ID
GET /appointments/98765
POST
/appointments
Create & book (low-level)
// With existing patient ID: { "startAt": "2026-04-07T14:00:00-04:00", "endAt": "2026-04-07T14:30:00-04:00", "staffMemberId": 42, "treatmentId": 15, "patient": { "id": 12345 }, "locationId": 1 // optional, default 1 } // Auto-create patient (searches by email first): { "startAt": "2026-04-07T14:00:00-04:00", "endAt": "2026-04-07T14:30:00-04:00", "staffMemberId": 42, "treatmentId": 15, "patient": { "firstName": "John", "lastName": "Doe", "email": "john@example.com" } }
If
patient has no id, searches by email to avoid duplicates, then creates if not found.
POST
/appointments/:id/cancel
Cancel an appointment
POST /appointments/98765/cancel { "notifyPatient": true // optional — sends cancellation email }
Also available as
DELETE /appointments/:id (same behavior).
POST
/appointments/:id/reschedule
Reschedule an appointment
POST /appointments/98765/reschedule { "startAt": "2026-04-10T14:00:00-04:00", "endAt": "2026-04-10T14:30:00-04:00", "staffMemberId": 42, // optional — keeps same if omitted "notifyPatient": true // optional }
Cancels the old appointment and books a new one at the specified time. Returns the new appointment details.
Calendar / Schedule
GET
/calendar
View schedule for a date range
// Query params — no body needed GET /calendar?startDate=2026-04-07&endDate=2026-04-11 // Filter by staff GET /calendar?startDate=2026-04-07&endDate=2026-04-11&staffMemberIds=1,5,12
GET
/schedule
Ocean — cached slots (sub-100ms)
GET /schedule?maxDays=7
Returns cached availability from the 15-second Ocean refresh loop. Faster than
/availability.Patients
POST
/patients
Create a new patient
{
"firstName": "John", // required
"lastName": "Doe", // required
"email": "john@example.com", // optional
"phone": "416-555-1234", // optional
"city": "Toronto", // optional
"province": "ON", // optional
"country": "CA", // optional, default: CA
"timeZone": "America/Toronto" // optional, default: America/Toronto
}
GET
/patients
List all patients
// No body — just GET /patients
GET
/patients/search?q=...
Search by name, email, or phone
GET /patients/search?q=john@email.com
GET
/patients/:id
Get patient by ID
GET /patients/12345Staff
GET
/staff
List all practitioners
// No body — returns all staff with IDs you need for booking
GET
/staff/:id
Get staff member by ID
GET /staff/42Treatments
GET
/treatments
List all treatment types
// No body — returns all treatments with IDs you need for bookingHealth
GET
/health
No API key needed
// Response: { "status": "ready", "uptime": 3642.5, "clinics": [ { "slug": "elitemd", "browserConnected": true, "reconnectCount": 0 } ] }
GHL Custom Values
The schedule cache pushes Ocean slot data to GoHighLevel as a location custom value every 15 seconds (only when data changes). The voice AI reads this during live calls.
GHL
{{ custom_values.availability }}
Available time slots (auto-updated)
// Value stored in GHL (JSON string): { "lastUpdated": "2026-04-04T15:30:00Z", "days": [ { "date": "2026-04-07", "dayOfWeek": "Monday", "slots": ["10:00 AM", "11:15 AM", "1:30 PM"] }, { "date": "2026-04-08", "dayOfWeek": "Tuesday", "slots": ["9:00 AM", "10:30 AM", "3:00 PM"] } ] }
HOW
Setup
How it works
1. Schedule cache runs a Playwright browser against the OceanMD booking page every 15 seconds 2. Extracts available time slots via the booking page DOM 3. Compares with previous data — only pushes to GHL when slots actually change (saves API calls) 4. Pushes JSON to GHL location custom value via: PUT /locations/{locationId}/customValues/{fieldId} Host: services.leadconnectorhq.com Authorization: Bearer {GHL_API_KEY} Version: 2021-07-28 5. Voice AI reads it in conversation prompts as: {{ custom_values.availability }}
ENV
.env
Required environment variables
GHL_API_KEY=pit-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx GHL_LOCATION_ID=your-location-id GHL_SCHEDULE_FIELD_ID=custom-value-field-id
Get these from GHL → Settings → Custom Values. The field ID is the ID of the custom value named "availability".
Health Log
| Time | Clinic | Event | Detail |
|---|---|---|---|
| Loading... | |||