---
title: "Clear (cancel) a contact's appointment"
description: "Finds the contact by `contact_id` or `phone_number` and cancels their\nappointment(s).\n\n**ALL** of the contact's live upcoming appointments are soft-deleted in\nthe `appointments` table, not just the soonest one — duplicate rows do\noccur (the AI setter, inbound webhooks, and the booking page each insert\nindependently), and clearing one left the duplicate on the calendar.\nPast appointments are left alone as reporting history; if the contact has\nnothing upcoming, the single most recent appointment is cleared instead.\n\nOnce no live appointment remains, the contact's `appointment_date`,\n`appointment_time`, and `appointment_date_display` columns are nulled so\nthe contact record and the calendar agree. If a live appointment still\nremains, those columns are left pointing at it.\n\n**A cancel that removed nothing returns `success: false` with reason\n`no_appointment_to_clear`** — still HTTP 200, because the request was\nprocessed fine and the outcome was simply \"nothing booked\". Check\n`success` (or `appointments_cleared`), not the status code: a cancel\narriving before the booking exists must not be read as a cancellation.\nIdempotent — calling it twice gives `success: false` the second time.\nNever creates a contact — an unknown identifier returns 404 with reason\n`contact_not_found`.\n\n**Required fields:** one of `contact_id` / `phone_number`. No date/time.\n"
---

> Documentation Index
> Fetch the complete documentation index at: https://developer.channelautomations.com/llms.txt
> Use this file to discover all available pages before exploring further.

Path: Channel Automation API › Appointments

`DELETE /api/appointments`

Finds the contact by `contact_id` or `phone_number` and cancels their
appointment(s).

**ALL** of the contact's live upcoming appointments are soft-deleted in
the `appointments` table, not just the soonest one — duplicate rows do
occur (the AI setter, inbound webhooks, and the booking page each insert
independently), and clearing one left the duplicate on the calendar.
Past appointments are left alone as reporting history; if the contact has
nothing upcoming, the single most recent appointment is cleared instead.

Once no live appointment remains, the contact's `appointment_date`,
`appointment_time`, and `appointment_date_display` columns are nulled so
the contact record and the calendar agree. If a live appointment still
remains, those columns are left pointing at it.

**A cancel that removed nothing returns `success: false` with reason
`no_appointment_to_clear`** — still HTTP 200, because the request was
processed fine and the outcome was simply "nothing booked". Check
`success` (or `appointments_cleared`), not the status code: a cancel
arriving before the booking exists must not be read as a cancellation.
Idempotent — calling it twice gives `success: false` the second time.
Never creates a contact — an unknown identifier returns 404 with reason
`contact_not_found`.

**Required fields:** one of `contact_id` / `phone_number`. No date/time.

## Authentication

Requires one of the following:

- `BearerAuth`, http, header `Authorization`, format `JWT`
- `ApiKeyAuth`, apiKey, in header, header `X-API-Key`, format `API_KEY`

## Request body

_Required._

- `contact_id` (`delete/api/appointments.contact_id`; string, optional) — Contact UUID. Either this or phone_number is required.
  - format `uuid`; example `"cc2b8604-151d-43ab-a63b-762ffa825f3e"`
- `phone_number` (`delete/api/appointments.phone_number`; string, optional) — Contact phone number (any common format). Used if contact_id is not provided.
  - example `"+17198592496"`

## Example request

```json
{
  "contact_id": "cc2b8604-151d-43ab-a63b-762ffa825f3e",
  "phone_number": "+17198592496"
}
```

## Code samples

### cURL

```bash
curl --request DELETE \
  --url https://api.channelautomation.com/api/appointments \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '
{
  "contact_id": "cc2b8604-151d-43ab-a63b-762ffa825f3e",
  "phone_number": "+17198592496"
}
'
```

### TypeScript

```typescript
const url = 'https://api.channelautomation.com/api/appointments';
const options = {
  method: 'DELETE',
  headers: {'Content-Type': 'application/json', Authorization: 'Bearer <token>'},
  body: JSON.stringify({
    contact_id: 'cc2b8604-151d-43ab-a63b-762ffa825f3e',
    phone_number: '+17198592496'
  })
};

fetch(url, options)
  .then(res => res.json())
  .then(json => console.log(json))
  .catch(err => console.error(err));
```

### Python

```python
import requests

url = "https://api.channelautomation.com/api/appointments"

payload = {
    "contact_id": "cc2b8604-151d-43ab-a63b-762ffa825f3e",
    "phone_number": "+17198592496"
}
headers = {
    "Content-Type": "application/json",
    "Authorization": "Bearer <token>"
}

response = requests.delete(url, json=payload, headers=headers)

print(response.text)
```

## Responses

### 200

Request processed. **`success` is the outcome, not the status code:**
`true` means at least one appointment was soft-deleted; `false` with
reason `no_appointment_to_clear` means the contact had nothing booked
and nothing was cancelled.

#### Example

```json
{
  "success": true,
  "reason": "no_appointment_to_clear",
  "contact_id": "f245e2a3-e141-4d41-912c-b1c1f5bd5598",
  "appointment_id": "21634f0d-2156-4d4b-a142-2e7c783a9212",
  "appointment_ids": [
    "497f6eca-6276-4993-bfeb-53cbbbba6f08"
  ],
  "appointment_cleared": true,
  "appointments_cleared": 1,
  "contact_columns_cleared": true,
  "remaining_upcoming_appointments": 0
}
```

- `success` (`delete/api/appointments.response.200.success`; boolean, optional) — True only if at least one appointment row was actually soft-deleted.
  - example `true`
- `reason` (`delete/api/appointments.response.200.reason`; string, optional) — Present only when success is false. `no_appointment_to_clear` = the contact had no appointment (or a concurrent caller cleared it first).
  - example `"no_appointment_to_clear"`
- `contact_id` (`delete/api/appointments.response.200.contact_id`; string, optional)
  - format `uuid`
- `appointment_id` (`delete/api/appointments.response.200.appointment_id`; string, optional) — The soonest appointment cleared. Kept for callers written against the single-appointment response; use appointment_ids for the full set.
  - format `uuid`
- `appointment_ids` (`delete/api/appointments.response.200.appointment_ids`; array<string>, optional) — Every appointment soft-deleted by this call.
  - format `uuid`
- `appointment_cleared` (`delete/api/appointments.response.200.appointment_cleared`; boolean, optional) — Mirrors `success` — true only if at least one row was cleared.
- `appointments_cleared` (`delete/api/appointments.response.200.appointments_cleared`; integer, optional) — How many appointment rows the database actually soft-deleted.
  - example `1`
- `contact_columns_cleared` (`delete/api/appointments.response.200.contact_columns_cleared`; boolean, optional) — True only if the contact's appointment columns were actually nulled (confirmed by row count). False when a live appointment still remains, since the columns must keep pointing at it.
- `remaining_upcoming_appointments` (`delete/api/appointments.response.200.remaining_upcoming_appointments`; integer, optional, nullable) — Live (non-deleted) appointments the contact still has after this call — non-zero means past appointments or rows beyond the 50-per-call cap remain. Null if the check itself failed.
  - example `0`

### 400

Missing identifier (neither contact_id nor phone_number)

#### Example

```json
{
  "success": false,
  "error": "phone_number is required",
  "reason": "missing_phone"
}
```

- `success` (`delete/api/appointments.response.400.success`; boolean, optional)
  - example `false`
- `error` (`delete/api/appointments.response.400.error`; string, optional) — Human-readable error message.
  - example `"phone_number is required"`
- `reason` (`delete/api/appointments.response.400.reason`; string, optional) — Short error code for programmatic handling. Possible values: - `missing_phone` - `missing_adate` - `missing_atime` - `missing_workspace` - `missing_workspace_context` - `contact_upsert_failed` - `contact_upsert_exception` - `datetime_resolution_failed` - `appointment_insert_failed` - `appointment_insert_exception` - `unhandled_exception` Additional values returned by PUT /api/appointments: - `missing_identifier` (neither contact_id nor phone_number provided) - `invalid_contact_id` (contact_id is not a valid UUID) - `contact_not_found` - `contact_lookup_failed` - `appointment_lookup_failed` - `appointment_update_failed` - `feature_disabled` (APPOINTMENT_UPDATE_API flag is off for the workspace)
  - example `"missing_phone"`

### 401

Authentication required

#### Example

```json
{
  "error": "Authentication required",
  "success": false
}
```

- `error` (`delete/api/appointments.response.401.error`; string, optional)
  - example `"Authentication required"`
- `success` (`delete/api/appointments.response.401.success`; boolean, optional)
  - example `false`

### 403

API key lacks `contacts.write` permission

### 404

No contact found for the provided identifier in this workspace
(reason `contact_not_found`). A contact that exists but has no
appointment is NOT a 404 — that returns 200 with `success: false`
and reason `no_appointment_to_clear`.

#### Example

```json
{
  "success": false,
  "error": "phone_number is required",
  "reason": "missing_phone"
}
```

- `success` (`delete/api/appointments.response.404.success`; boolean, optional)
  - example `false`
- `error` (`delete/api/appointments.response.404.error`; string, optional) — Human-readable error message.
  - example `"phone_number is required"`
- `reason` (`delete/api/appointments.response.404.reason`; string, optional) — Short error code for programmatic handling. Possible values: - `missing_phone` - `missing_adate` - `missing_atime` - `missing_workspace` - `missing_workspace_context` - `contact_upsert_failed` - `contact_upsert_exception` - `datetime_resolution_failed` - `appointment_insert_failed` - `appointment_insert_exception` - `unhandled_exception` Additional values returned by PUT /api/appointments: - `missing_identifier` (neither contact_id nor phone_number provided) - `invalid_contact_id` (contact_id is not a valid UUID) - `contact_not_found` - `contact_lookup_failed` - `appointment_lookup_failed` - `appointment_update_failed` - `feature_disabled` (APPOINTMENT_UPDATE_API flag is off for the workspace)
  - example `"missing_phone"`

### 500

Clearing the appointment failed (DB error)

#### Example

```json
{
  "success": false,
  "error": "phone_number is required",
  "reason": "missing_phone"
}
```

- `success` (`delete/api/appointments.response.500.success`; boolean, optional)
  - example `false`
- `error` (`delete/api/appointments.response.500.error`; string, optional) — Human-readable error message.
  - example `"phone_number is required"`
- `reason` (`delete/api/appointments.response.500.reason`; string, optional) — Short error code for programmatic handling. Possible values: - `missing_phone` - `missing_adate` - `missing_atime` - `missing_workspace` - `missing_workspace_context` - `contact_upsert_failed` - `contact_upsert_exception` - `datetime_resolution_failed` - `appointment_insert_failed` - `appointment_insert_exception` - `unhandled_exception` Additional values returned by PUT /api/appointments: - `missing_identifier` (neither contact_id nor phone_number provided) - `invalid_contact_id` (contact_id is not a valid UUID) - `contact_not_found` - `contact_lookup_failed` - `appointment_lookup_failed` - `appointment_update_failed` - `feature_disabled` (APPOINTMENT_UPDATE_API flag is off for the workspace)
  - example `"missing_phone"`


Source: https://developer.channelautomations.com/api/delete-api-appointments/index.md
