Create an appointment
create an appointment.
POST
https://api.centerfy.ai/webhooks/inbound/appointments Headers
| Header | Required | Value | Description |
|---|---|---|---|
x-api-key | Yes | your sub-account API key (cfy_…) | Authenticates the request; or Authorization: Bearer <key>. |
Content-Type | Yes | application/json | JSON request body. |
Body parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
title | string | Yes | — | Appointment title; required and must be non-empty. |
appointment_date | string | Yes | — | Start date/time as ISO 8601. Must include a UTC offset/Z, or pass timezone to interpret a bare local value; otherwise 400. |
calendar_id | string | Yes | — | UUID of an existing calendar. 400 if missing, invalid, or not found. |
contact_id | string | No | — | UUID of an existing contact. The contact is resolved by contact_id first; if absent it is resolved by email/phone (created if missing). Must be a valid UUID and exist, or 404. |
email | string | No | — | Used to find or create the contact when contact_id is not given. At least one of contact_id, email, or phone must resolve or create a contact. |
phone | string | No | — | Used to find or create the contact when contact_id is not given (normalized). At least one of contact_id, email, or phone must resolve or create a contact. |
name | string | No | — | Contact display name used if a new contact must be created; falls back to first_name + last_name. |
first_name | string | No | — | Used to build the contact name if name is not provided. |
last_name | string | No | — | Used to build the contact name if name is not provided. |
description | string | No | — | Optional appointment description. |
timezone | string | No | — | IANA timezone used to interpret a bare (offset-less) appointment_date, e.g. “America/New_York”. |
duration_minutes | integer | No | — | Positive integer; defaults to 60. Non-integer or <= 0 → 400. |
location | string | No | — | Optional location text. |
notes | string | No | — | Optional notes text. |
status | string | No | — | One of: scheduled, confirmed, completed, cancelled, no_show (case-insensitive). Defaults to ‘scheduled’. Other values → 400. |
check_availability | boolean | No | — | When true, the booking is refused if it overlaps a non-cancelled appointment on the same calendar (existing appointments without a duration count as 60 minutes). Defaults to false (no check). |
Request body
{
"title": "Discovery Call",
"appointment_date": "2026-06-20T15:00:00Z",
"calendar_id": "<calendar_id>",
"contact_id": "<contact_id>",
"duration_minutes": 30,
"location": "Zoom",
"notes": "Intro call to discuss requirements",
"status": "scheduled",
"timezone": "America/New_York"
}Example
curl -X POST "https://api.centerfy.ai/webhooks/inbound/appointments" \
-H "x-api-key: $CENTERFY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Discovery Call",
"appointment_date": "2026-06-20T15:00:00Z",
"calendar_id": "<calendar_id>",
"contact_id": "<contact_id>",
"duration_minutes": 30,
"location": "Zoom",
"notes": "Intro call to discuss requirements",
"status": "scheduled",
"timezone": "America/New_York"
}' import { CenterfyClient } from "@centerfy/sdk";
const centerfy = new CenterfyClient({ apiKey: process.env.CENTERFY_API_KEY! });
const result = await centerfy.appointments.create({
"title": "Discovery Call",
"appointment_date": "2026-06-20T15:00:00Z",
"calendar_id": "<calendar_id>",
"contact_id": "<contact_id>",
"duration_minutes": 30,
"location": "Zoom",
"notes": "Intro call to discuss requirements",
"status": "scheduled",
"timezone": "America/New_York"
});
console.log(result); centerfy appointments create '{"title":"Discovery Call","appointment_date":"2026-06-20T15:00:00Z","calendar_id":"<calendar_id>","contact_id":"<contact_id>","duration_minutes":30,"location":"Zoom","notes":"Intro call to discuss requirements","status":"scheduled","timezone":"America/New_York"}' MCP tool: centerfy_appointments_create (see MCP server)
Response
{
"status": "success",
"created": true,
"appointment_id": "c3d4e5f6-a7b8-4c9d-0e1f-2a3b4c5d6e7f",
"contact_id": "f1e2d3c4-b5a6-4978-8c9d-0e1f2a3b4c5d"
}Errors
400— for a missing title or appointment_date, a bad date, an invalid duration or status, or an invalid/missing calendar_id.404— if a given contact_id does not exist.500— on failure.409— when check_availability is true and the requested time overlaps an existing non-cancelled appointment on the calendar; the response includes a conflicting array (id, title, appointment_date, duration_minutes), and nothing is created, not even a new contact.
Notes
The contact is resolved by contact_id, otherwise by email/phone (a minimal contact is created if none matches, since an appointment requires a contact).