Skip to main content
POST
/
appointments
/
add
curl -X POST "https://app.medisync.me/api/appointments/add" \
  -H "Authorization: Bearer <JWT>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Allgemeine Beratung",
    "date": "2026-07-15",
    "time": "14:30",
    "status": "planned",
    "notes": "Erstberatung",
    "language": "de",
    "appointment_type": "in_person"
  }'
{
  "success": true,
  "data": {
    "_id": "65f8a1b2c3d4e5f6789012ef",
    "name": "Allgemeine Beratung",
    "date": "2026-07-15T00:00:00.000Z",
    "time": "14:30",
    "status": "planned",
    "error_reason": null,
    "doctor_id": "65f0a1b2c3d4e5f678901234",
    "notes": "Erstberatung",
    "language": "de",
    "notes_type": null,
    "appointment_type": "in_person",
    "room": null,
    "duration_minutes": null,
    "deletionDate": "2026-07-29T00:00:00.000Z",
    "booking_status": "none",
    "booking": null,
    "createdAt": "2026-07-02T09:12:00.000Z",
    "updatedAt": "2026-07-02T09:12:00.000Z",
    "__v": 0
  }
}

Overview

Creates a new appointment owned by the authenticated doctor. The doctor_id is taken from the JWT; all other details come from the request body. name, date and time are required by the data model. Optionally, supplying notify_patient: true together with a patient object turns the same appointment into a confirmed booking and sends the patient a confirmation email with a calendar (ICS) invite — no separate record is created.
This endpoint sits behind a subscription gate. If your trial or subscription has expired the request is rejected with 403 before it runs (see Error Responses). Free-trial and paid accounts are also subject to appointment / encounter caps that return structured error codes your integration should handle.

Authentication

Authorization
string
required
Bearer token for authenticated access. The doctor id is taken from the token.
Authorization: Bearer <JWT>

Body Parameters

name
string
required
Appointment title.
date
string
required
YYYY-MM-DD (or ISO). When creating a notify_patient booking, this is combined with time in the practice timezone into a precise instant.
time
string
required
Wall-clock time, HH:mm.
status
string
Recording lifecycle status. Defaults to recording; pass planned to create a planned appointment.
recording_id
string
Associated recording id.
recording_url
string
Recording URL.
recording_key
string
Recording storage key.
notes
string
Private clinical notes.
language
string
ISO language code for note generation. Default de.
notes_type
string
Notes template key. Default null (the service selects a template).
appointment_type
string
Free-form appointment type. Default in_person.
duration_minutes
number
Duration in minutes, 5480. An out-of-range or non-numeric value falls back to the appointment type’s duration (when appointment_type_id is given) or null.
room
string
Room / location label. Whitespace-only or empty is stored as null. Remembered for the practice’s room autocomplete.
appointment_type_id
string
ObjectId of an appointment type you own. Used to default the duration and tag the booking source. An unknown or unowned id returns 404.
notify_patient
boolean
When true, the appointment becomes a confirmed booking and a confirmation email + ICS invite is sent. Requires a valid patient.email.
patient
object
Patient contact details for the confirmation email.
doctor_note
string
Patient-facing note stored on the booking (only when a booking is created).

Response

success
boolean
Whether the request succeeded.
data
object
The created appointment object.
Additional fields may appear on the appointment object. Treat any field not documented here as opaque.

Example Request

curl -X POST "https://app.medisync.me/api/appointments/add" \
  -H "Authorization: Bearer <JWT>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Allgemeine Beratung",
    "date": "2026-07-15",
    "time": "14:30",
    "status": "planned",
    "notes": "Erstberatung",
    "language": "de",
    "appointment_type": "in_person"
  }'

Example Response

{
  "success": true,
  "data": {
    "_id": "65f8a1b2c3d4e5f6789012ef",
    "name": "Allgemeine Beratung",
    "date": "2026-07-15T00:00:00.000Z",
    "time": "14:30",
    "status": "planned",
    "error_reason": null,
    "doctor_id": "65f0a1b2c3d4e5f678901234",
    "notes": "Erstberatung",
    "language": "de",
    "notes_type": null,
    "appointment_type": "in_person",
    "room": null,
    "duration_minutes": null,
    "deletionDate": "2026-07-29T00:00:00.000Z",
    "booking_status": "none",
    "booking": null,
    "createdAt": "2026-07-02T09:12:00.000Z",
    "updatedAt": "2026-07-02T09:12:00.000Z",
    "__v": 0
  }
}

Error Responses

{
  "success": false,
  "code": "TRIAL_APPOINTMENT_LIMIT_REACHED",
  "limit": 10,
  "used": 10,
  "message": "Free-trial appointment limit reached. Upgrade to create more appointments."
}
{
  "success": false,
  "code": "ENCOUNTER_LIMIT_REACHED",
  "limit": 200,
  "used": 200,
  "message": "Monthly encounter limit reached. Purchase an encounter pack to continue."
}
{
  "success": false,
  "code": "SLOT_TAKEN",
  "error": "A confirmed appointment already exists at this time"
}
{
  "success": false,
  "error": "Appointment type not found"
}
{
  "success": false,
  "code": "PATIENT_EMAIL_REQUIRED",
  "error": "A valid patient email is required to send a notification"
}
{
  "success": false,
  "message": "Ihr Probezeitraum ist abgelaufen. Bitte abonnieren Sie einen Plan, um MediSync weiterhin zu nutzen.",
  "trialExpired": true
}
{
  "success": false,
  "message": "Unauthorized"
}
Branch on the code field for the structured limit responses (TRIAL_APPOINTMENT_LIMIT_REACHED, ENCOUNTER_LIMIT_REACHED, SLOT_TAKEN, PATIENT_EMAIL_REQUIRED). The subscription gate returns a German message with a trialExpired (or subscriptionExpired) flag. Missing required fields or other save failures return 400/500 with the raw error in the error field. A missing or invalid Bearer token returns 401 with a message key.