Appointments API Reference

Create and cancel appointments from external calendar tools, and query calendar syncs, booked appointments, hosts, setters, meeting URLs, and outcome log states.

Date & Timezone Handling: All timestamps in responses are UTC, formatted as ISO 8601 (2026-01-15T10:30:00.000Z). Date-range filters accept either date-only YYYY-MM-DD (which expands to UTC day boundaries) or full ISO 8601 timestamps. Note that scheduled_from and scheduled_to query filters match against schedule_time, not created_at.

Create Appointment

Add an appointment to Hermon from an external calendar tool (for example, Cal.ai via Make.com). The lead is resolved by email (created if new), the event type is resolved by name (created as a SALES_CALL if new), and all standard booking side effects run — default statuses, the Call Booked notification, and outbound webhooks — exactly as an in-app booking.

POST/api/external/appointments
No duplicate appointments. Every request must include an external_reference — the unique booking ID from your source system. Sending the same reference again returns the existing appointment (is_duplicate: true) instead of creating a second one. A new booking must use a new reference. If the same reference is sent with a different schedule_time, the request is rejected with 409 Conflict rather than silently changing the time.
Provide the lead as lead_email (find or create) or an existing lead_id; provide the event type as event_type_name (find or create) or an existing event_type_id. host_email, setter_email, and triager_email must belong to active members of your organization.

Request Body

FieldTypeRequiredDescription
external_referencestringRequiredUnique ID of the booking from your source system (e.g. the Cal.ai booking ID). Used to prevent duplicates: sending the same value again returns the existing appointment instead of creating a new one.
schedule_timestring (ISO 8601)RequiredAppointment start time in UTC, e.g. 2026-08-20T15:00:00Z.
lead_emailstringRequiredAttendee email. Finds an existing lead in your org by email, or creates a new lead if none matches. Required unless lead_id is provided.
event_type_namestringRequiredName of the call/event type (e.g. "Sales Call"). Matched case-insensitively; if it does not exist yet, a new event type is created with call category SALES_CALL. Required unless event_type_id is provided.
lead_idstring (UUID)OptionalTarget an existing lead directly by UUID. Use instead of lead_email.
event_type_idstring (UUID)OptionalTarget an existing event type directly by UUID. Use instead of event_type_name.
lead_first_namestringOptionalFirst name used when a new lead is created. Falls back to the email prefix if omitted.
lead_last_namestringOptionalLast name used when a new lead is created.
lead_phonestringOptionalAttendee phone number. E.164 format preferred (e.g. +14155551234); numbers that cannot be parsed are ignored.
host_emailstringOptionalEmail of the host (closer) for the call. Must be an active member of your organization.
setter_emailstringOptionalEmail of the setter. Must be an active member of your organization.
triager_emailstringOptionalEmail of the triager. Must be an active member of your organization.
meeting_urlstringOptionalURL of the video/meeting room for the appointment.
notesstringOptionalFree-text note attached to the appointment.

Code Examples

curl -X POST "https://api.hermon.io/api/external/appointments" \
  -H "Content-Type: application/json" \
  -H "x-api-key: sk_live_your_key_here" \
  -d '{
    "external_reference": "cal-ai-booking-123",
    "schedule_time": "2026-08-20T15:00:00Z",
    "lead_email": "jane@example.com",
    "event_type_name": "Sales Call",
    "lead_first_name": "Jane",
    "lead_phone": "+14155551234",
    "host_email": "closer@yourorg.com",
    "meeting_url": "https://cal.ai/j/abc",
    "notes": "Booked via Cal.ai"
  }'

Response Example

A new booking returns 201 Created.

json
{
  "status": "success",
  "message": "Appointment created successfully",
  "data": {
    "id": "c76340d1-72a8-403e-b920-d6a2b526926c",
    "is_duplicate": false
  }
}

A repeated external_reference returns 200 OK with the existing appointment.

json
{
  "status": "success",
  "message": "Appointment already exists for this reference",
  "data": {
    "id": "c76340d1-72a8-403e-b920-d6a2b526926c",
    "is_duplicate": true
  }
}

Cancel Appointment

Cancel an appointment that was added through this API. Send the same external_reference used when it was created; Hermon marks the appointment Canceled — updating the lead status, clearing the meeting URL, and firing the cancellation notification and outbound webhook, exactly like canceling in the app.

POST/api/external/appointments/cancel
Only appointments created via this API (source MANUAL) can be canceled here — bookings synced from Calendly, iClosed, or GoHighLevel are managed by those integrations. Cancelling is idempotent: sending it again for an already-canceled appointment simply re-applies the Canceled outcome.

Request Body

FieldTypeRequiredDescription
external_referencestringRequiredThe booking ID you sent when the appointment was created. Identifies which appointment to cancel.
notesstringOptionalFree-text note explaining the cancellation (e.g. "Canceled by attendee").

Code Examples

curl -X POST "https://api.hermon.io/api/external/appointments/cancel" \
  -H "Content-Type: application/json" \
  -H "x-api-key: sk_live_your_key_here" \
  -d '{
    "external_reference": "cal-ai-booking-123",
    "notes": "Canceled by attendee"
  }'

Response Example

json
{
  "status": "success",
  "message": "Appointment cancelled successfully",
  "data": {
    "id": "c76340d1-72a8-403e-b920-d6a2b526926c"
  }
}

List Appointments

Retrieve a paginated list of appointments. Results can be filtered by call category, outcome status role, lead, assigned setter, triager, or closer (host).

GET/api/external/appointments

Query Parameters

ParameterTypeRequiredDescription
pageintegerOptionalPage number for pagination. Default: 1
limitintegerOptionalNumber of items per page. Default: 10, Max: 100
lead_idstring (UUID)OptionalUUID of the lead to filter appointments by. Must be a valid UUID that exists in your org.
call_categorystringOptionalComma-separated list of call categories (e.g. SALES_CALL, COACHING_CALL, TRIAGE_CALL).
outcome_rolestringOptionalComma-separated list of outcome status roles (e.g. NEW_LEAD, APPOINTMENT_BOOKED, NO_SHOW, RESCHEDULED, CANCELED, PARTIAL_PAYMENT, WON, UNQUALIFIED, FOLLOW_UP, LOST).
setterstringOptionalEmail address of the setter to filter by.
triagerstringOptionalEmail address of the triager to filter by.
closerstringOptionalEmail address of the closer (host) to filter by.
scheduled_fromstringOptionalLower bound for schedule_time. Accepts YYYY-MM-DD or ISO 8601.
scheduled_tostringOptionalUpper bound for schedule_time. Accepts YYYY-MM-DD or ISO 8601.
created_fromstringOptionalLower bound for created_at. Accepts YYYY-MM-DD or ISO 8601.
created_tostringOptionalUpper bound for created_at. Accepts YYYY-MM-DD or ISO 8601.
updated_fromstringOptionalLower bound for updated_at. Accepts YYYY-MM-DD or ISO 8601.
updated_tostringOptionalUpper bound for updated_at. Accepts YYYY-MM-DD or ISO 8601.

Code Examples

curl -X GET "https://api.hermon.io/api/external/appointments?page=1&limit=10&call_category=COACHING_CALL&outcome_role=APPOINTMENT_BOOKED&setter=sales-setter@mailinator.com&closer=hermon@mailinator.com&triager=triager@mailinator.com&lead_id=e799c30f-9da7-4fd7-9552-83b96a9bfac2" \
  -H "Content-Type: application/json" \
  -H "x-api-key: sk_live_your_key_here"

Response Example

json
{
  "success": true,
  "message": "Appointments fetched successfully",
  "data": [
    {
      "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
      "lead": {
        "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "first_name": "John",
        "last_name": "Doe",
        "email": "john.doe@example.com",
        "phone_e164": "+14155551234",
        "phone_country": "US",
        "next_touch_point_at": "2026-06-15T14:00:00.000Z",
        "next_touch_point_type": "PHONE_CALL"
      },
      "schedule_time": "2026-02-10T15:00:00.000Z",
      "host": {
        "first_name": "Jane",
        "last_name": "Smith",
        "email": "jane@example.com",
        "profile_image_url": "https://img.clerk.com/eyJ0eXBlIjoicHJveHkiLCJzcmMiOiJodHRwczovL2ltYWdlcy5jbGVyay5kZXYvb2F1dGhfZ29vZ2xlL2ltZ18zOWJ3NkxJaTFFSTdlM3Z4RHFhTFViUTZVeFMifQ",
        "id": "user_456def",
        "role": "org:admin"
      },
      "setter": {
        "first_name": "Alice",
        "last_name": "Jones",
        "email": "alice@example.com",
        "profile_image_url": null,
        "id": "user_123abc",
        "role": "org:member"
      },
      "triager": null,
      "meeting_url": "https://calendly.com/jane-smith/discovery",
      "appointment_type": "Discovery Call",
      "call_category": "SALES_CALL",
      "appointment_event_type": {
        "id": "evt_abc123",
        "provider": "CALENDLY",
        "owner_name": "Jane Smith"
      },
      "outcome": {
        "id": "status_won123",
        "name": "Closed Won",
        "role": "WON",
        "description": "Deal successfully closed",
        "text_color": "#14532D",
        "bg_color": "#DCFCE7",
        "created_at": "2025-12-01T09:00:00.000Z"
      },
      "notes": {
        "note": "Strong buying intent, ready to start immediately",
        "mention_users": []
      },
      "recording_url": "https://example.com/recordings/call-abc123",
      "no_show": false,
      "source": "CALENDLY",
      "external_reference": "CALY-abc123",
      "utm_source": "facebook",
      "utm_medium": "paid_social",
      "utm_campaign": "q1-2026-coaching",
      "utm_content": "video-ad-v2",
      "utm_term": null,
      "fathom": {
        "id": "fathom_xyz789",
        "fathom_call_id": "fathom-call-abc123",
        "ai_generated_title": "Discovery Call — Strong Interest in Premium Package",
        "ai_rationale": "Lead showed high engagement and asked specific questions about onboarding",
        "ai_suggested_outcome": "WON",
        "ai_confidence_score": 0.87,
        "appointment_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
        "created_at": "2026-02-10T16:30:00.000Z"
      },
      "created_at": "2026-02-08T10:00:00.000Z",
      "updated_at": "2026-02-10T16:35:00.000Z",
      "custom_fields": []
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 1,
    "totalPages": 1,
    "hasNext": false,
    "hasPrev": false
  }
}

Get Appointment Details

Fetch detailed information for a single appointment by its unique UUID.

GET/api/external/appointments/:id

Path Parameters

:idRequired. The unique appointment UUID.

Appointment Object Fields

FieldTypeDescription
idstring (UUID)Unique identifier for the appointment.
leadobjectBasic lead info (id, first_name, last_name, email, phone_e164, phone_country, next_touch_point_at, next_touch_point_type).
schedule_timestring (ISO 8601)UTC timestamp of the scheduled appointment.
hostobject | nullThe closer/host conducting the call (first_name, last_name, email, profile_image_url, id, role).
setterobject | nullSetter who booked the appointment (first_name, last_name, email, profile_image_url, id, role).
triagerobject | nullTriager assigned to the appointment (first_name, last_name, email, profile_image_url, id, role).
meeting_urlstring | nullVideo/phone meeting URL.
appointment_typestring | nullName of the event type (e.g. "Discovery Call").
call_categorystring (enum)Category of the call (SALES_CALL, COACHING_CALL, TRIAGE_CALL).
appointment_event_typeobjectEvent type metadata (id, provider, owner_name).
outcomeobject | nullOutcome status (id, name, role, description, text_color, bg_color, created_at).
notesobject | nullLatest note on the appointment (note, mention_users) [List API Only].
recording_urlstring | nullCall recording URL.
no_showbooleanWhether the lead was a no-show.
sourcestring (enum)Booking source (CALENDLY, ICLOSED, GOHIGHLEVEL, MANUAL).
external_referencestring | nullReference ID from the booking provider.
utm_sourcestring | nullUTM source parameter [List API Only].
utm_mediumstring | nullUTM medium parameter [List API Only].
utm_campaignstring | nullUTM campaign parameter [List API Only].
utm_contentstring | nullUTM content parameter [List API Only].
utm_termstring | nullUTM term parameter [List API Only].
fathomobject | nullFathom AI call analysis (id, fathom_call_id, ai_generated_title, ai_rationale, ai_suggested_outcome, ai_confidence_score, appointment_id, created_at).
created_atstring (ISO 8601)UTC timestamp when created.
updated_atstring (ISO 8601)UTC timestamp when last updated.
custom_fieldsarrayCustom field values.
first_sourceobjectLead's first attribution source [Details API Only].
last_sourceobjectLead's last attribution source [Details API Only].
opt_insarrayUp to 3 most recent opt-in submissions from the lead [Details API Only].
The detail endpoint does not include notes or UTM fields (utm_source, utm_medium, etc.) as those are list-only fields. The details response includes first_source, last_source, opt_ins (up to 3 most recent), and custom_fields instead.

Code Examples

curl -X GET "https://api.hermon.io/api/external/appointments/c76340d1-72a8-403e-b920-d6a2b526926c" \
  -H "Content-Type: application/json" \
  -H "x-api-key: sk_live_your_key_here"

Response Example

json
{
  "success": true,
  "message": "Appointment fetched successfully",
  "data": {
    "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
    "lead": {
      "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "first_name": "John",
      "last_name": "Doe",
      "email": "john.doe@example.com",
      "phone_e164": "+14155551234",
      "phone_country": "US",
      "next_touch_point_at": "2026-06-15T14:00:00.000Z",
      "next_touch_point_type": "PHONE_CALL"
    },
    "schedule_time": "2026-02-10T15:00:00.000Z",
    "host": {
      "first_name": "Jane",
      "last_name": "Smith",
      "email": "jane@example.com",
      "profile_image_url": "https://img.clerk.com/eyJ0eXBlIjoicHJveHkiLCJzcmMiOiJodHRwczovL2ltYWdlcy5jbGVyay5kZXYvb2F1dGhfZ29vZ2xlL2ltZ18zOWJ3NkxJaTFFSTdlM3Z4RHFhTFViUTZVeFMifQ",
      "id": "user_456def",
      "role": "org:admin"
    },
    "setter": null,
    "triager": null,
    "meeting_url": "https://calendly.com/jane-smith/discovery",
    "appointment_type": "Discovery Call",
    "call_category": "SALES_CALL",
    "appointment_event_type": {
      "id": "evt_abc123",
      "provider": "CALENDLY",
      "owner_name": "Jane Smith"
    },
    "outcome": {
      "id": "status_won123",
      "name": "Closed Won",
      "role": "WON",
      "description": "Deal successfully closed",
      "text_color": "#14532D",
      "bg_color": "#DCFCE7",
      "created_at": "2025-12-01T09:00:00.000Z"
    },
    "recording_url": "https://example.com/recordings/call-abc123",
    "no_show": false,
    "source": "CALENDLY",
    "external_reference": "CALY-abc123",
    "fathom": null,
    "first_source": {
      "id": "src_abc123",
      "name": "Facebook Ad Campaign"
    },
    "last_source": {
      "id": "src_def456",
      "name": "Referral"
    },
    "custom_fields": [],
    "opt_ins": [
      {
        "id": "optin_xyz789",
        "lead_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "source": "TYPEFORM",
        "provider_form_id": "form_abc123",
        "provider_form_name": "Coaching Application",
        "external_reference": "TYPEFORM-abc123",
        "created_at": "2026-01-15T09:00:00.000Z"
      }
    ],
    "created_at": "2026-02-08T10:00:00.000Z",
    "updated_at": "2026-02-10T16:35:00.000Z"
  }
}

Values Reference

Call Category Values

ValueDescription
SALES_CALLSales discovery or closing call
COACHING_CALLCoaching session
TRIAGE_CALLInitial qualification/triage call

Status Role Values

ValueDescription
NEW_LEADNew/unqualified lead
APPOINTMENT_BOOKEDAppointment scheduled
NO_SHOWLead did not attend
RESCHEDULEDAppointment rescheduled
CANCELEDAppointment canceled
PARTIAL_PAYMENTPartial payment received
WONDeal won/closed
UNQUALIFIEDLead disqualified
FOLLOW_UPRequires follow-up
LOSTDeal lost

Appointment Source Values

ValueDescription
CALENDLYBooked via Calendly
ICLOSEDBooked via iClosed
GOHIGHLEVELBooked via GoHighLevel
MANUALManually created

Nested Objects Reference

Appointment Lead Object

FieldTypeDescription
idstring (UUID)Lead ID
first_namestringFirst name
last_namestringLast name
emailstringEmail address
phone_e164stringPhone in E.164 format
phone_countrystringISO 3166-1 alpha-2 country code
next_touch_point_atstring (ISO 8601)Next touchpoint timestamp
next_touch_point_typestring (enum)Type of next touchpoint

User Object

FieldTypeDescription
first_namestringFirst name
last_namestringLast name
emailstringEmail address
profile_image_urlstringProfile image URL (null if none)
idstringClerk user ID
rolestringOrganization role

Event Type Object

FieldTypeDescription
idstringEvent type ID (null when no event type is set)
providerstringBooking provider (e.g. CALENDLY, ICLOSED, GOHIGHLEVEL)
owner_namestringOwner name in the booking provider

Outcome Object

FieldTypeDescription
idstringStatus ID
namestringStatus display name
rolestring (enum)Status role
descriptionstringStatus description
text_colorstringHex color code for text
bg_colorstringHex color code for background
created_atstring (ISO 8601)When this status was created

Note Object

FieldTypeDescription
notestringNote text
mention_usersarrayUsers @mentioned in the note (Clerk user objects)

Fathom Object

FieldTypeDescription
idstringRecord ID
fathom_call_idstringFathom platform call ID
ai_generated_titlestringAI-generated call title
ai_rationalestringAI reasoning for the suggested outcome
ai_suggested_outcomestringSuggested outcome status name
ai_confidence_scorenumberConfidence score (0–1)
appointment_idstringParent appointment ID
created_atstring (ISO 8601)When the record was created

Error Responses

400Missing or invalid field (Create)
json
{
  "status": "error",
  "message": "external_reference is required"
}
401Missing or invalid API key
json
{
  "status": "error",
  "message": "Invalid or revoked API key"
}
404Resource not found
json
{
  "status": "error",
  "message": "Resource not found"
}
404No appointment for external_reference (Cancel)
json
{
  "status": "error",
  "message": "No appointment found for external_reference \"cal-ai-booking-123\""
}
409external_reference reused with a different time (Create)
json
{
  "status": "error",
  "message": "external_reference \"cal-ai-booking-123\" already maps to an appointment at a different time. Send a new external_reference for a new booking."
}
429Rate limit exceeded
json
{
  "status": "error",
  "message": "Too many requests"
}