Server-side APIEndpointsAppointments

Available slots

The bookable slots for one doctor, at one location, on one date.

GET /v1/appointments/available-slots

The starting point of every scheduled booking. It returns the grid a picker renders: the slots that can be booked, the ones that cannot (so you can grey them out instead of leaving unexplained gaps), and the plans the visit can be paid for with.

No OTP is needed to read slots — only to book one.

Query parameters

NameTypeRequiredDescription
addressIdnumberYesThe clinic location. From addresses[].id on workspace detail.
doctorIdnumberYesThe practitioner. From addresses[].doctors[].id.
appointmentDatestringYesYYYY-MM-DD, in the clinic's own timezone.
modestringNoOFFLINE, ONLINE, HOME_VISIT, HOME_COLLECTION or WALK_IN. Omit to get every mode the doctor offers.
durationTierIdnumberNoGenerate the grid for one consultation-length tier. See availableDurations in the response.
durationMinsnumberNoThe same choice expressed as minutes, when you do not hold the tier id.
patientIdnumberNoNarrows plans to what this patient can actually use — their active packs and follow-up entitlement.
patientCountryCodestringNoDial code, ISO2 or country name. Decides domestic vs international pricing on plans.
patientPhoneNumberstringNoSame purpose as patientId when you only hold a phone.
followUpbooleanNoPrice the visit as a follow-up of the patient's last consultation. To book it as one, send followUp: true on the booking too.
patientTimezonestringNoIANA zone (Asia/Kolkata) used for the patient* fields below.

workspaceId is taken from your API key. Sending one changes nothing.

Try it

GET/v1/appointments/available-slots

No OTP is needed to read slots — only to book one.

Query

Request
GET /v1/appointments/available-slots?addressId=1&doctorId=4&appointmentDate=2026-10-12&mode=OFFLINE
to https://api.medos.one
Use a dedicated test key, and put your browser's address on its allowlist

A Developer API key authenticates on its own, so it only works from the addresses registered against it — and this page calls from your browser, not your servers. Unless your own public address is on the list you get a 403 naming it, which is the allowlist doing its job. Your browser may also reach us over IPv6 even when your server does not, so the address in the error is often not the one you expected. The key here is kept in memory only and never written to storage, but create a test key for it and deactivate that key when you are done.

Request

curl -sG "https://api.medos.one/v1/appointments/available-slots" \
  -H "x-api-key: $MEDOS_API_KEY" \
  --data-urlencode "addressId=1" \
  --data-urlencode "doctorId=4" \
  --data-urlencode "appointmentDate=2026-09-20" \
  --data-urlencode "mode=OFFLINE"

Response

{
  "slots": [
    {
      "appointmentDate": "2026-09-20",
      "fromDateTimeTs": "09:00",
      "toDateTimeTs": "09:30",
      "fromDateTimeIso": "2026-09-20T09:00:00+05:30",
      "toDateTimeIso": "2026-09-20T09:30:00+05:30",
      "appointmentTimezone": "Asia/Kolkata",
      "patientFromDateTimeIso": "2026-09-20T05:30:00+02:00",
      "patientToDateTimeIso": "2026-09-20T06:00:00+02:00",
      "patientTimezone": "Europe/Berlin"
    }
  ],
  "unavailableSlots": [
    {
      "appointmentDate": "2026-09-20",
      "fromDateTimeIso": "2026-09-20T10:00:00+05:30",
      "toDateTimeIso": "2026-09-20T10:30:00+05:30",
      "status": "BOOKED"
    }
  ],
  "plans": [],
  "activeSessionPackResponses": [],
  "allSessionPackResponses": [],
  "nextAvailableDate": "2026-09-21",
  "selectedDurationTierId": null,
  "availableDurations": [15, 30],
  "patientTimezone": "Europe/Berlin",
  "patientTimezoneSource": "IP"
}
FieldMeaning
slots[]Bookable. Send fromDateTimeIso / toDateTimeIso back verbatim when you book.
unavailableSlots[]Same grid, unbookable. status is BOOKED (a Medos appointment holds it) or BLOCKED (external busy time from the practitioner's calendar). Deliberately coarse — it never names whose.
plans[]How this visit can be paid for: GENERAL_CONSULTATION, SESSION_PACK (buyable) or ACTIVE_PACKAGE (already owned). Each carries pricePerSession, currency, and for packs sessionsRemaining and expiresOn.
nextAvailableDateThe next date with any capacity — use it to skip an empty day rather than probing forward.
availableDurations[]Distinct consultation lengths this doctor offers. Drive a duration picker from this, not from plans.
selectedDurationTierIdWhich tier the returned grid was generated for.

Two renderings of the same instant

Every slot comes back twice. fromDateTimeIso is the clinic's own time; the patient* fields are the same moment rendered in the viewer's zone, resolved in this order:

SourcepatientTimezoneSource
?patientTimezone=PARAM
A CloudFront viewer headerHEADER
GeoIP on your server's addressIP
The clinic's own zoneCLINIC

CLINIC is not an error — it just makes both renderings identical, keeping the response shape stable. An unparseable patientTimezone falls through instead of failing the request, and a slot with no usable timestamp is passed through without the patient* fields at all.

Book with the clinic time, never the patient time

patientFromDateTimeIso is for display. Sending it back on a booking double-converts the slot and books the wrong hour. Booking takes fromDateTimeIso.

Server integrations usually want PARAM

Left alone, the zone is guessed from your server's IP — which is where your backend runs, not where the patient is. If you know the patient's zone, send patientTimezone; if you do not, ignore the patient* fields entirely.

Common failures

StatusCause
400appointmentDate is not YYYY-MM-DD, or mode is not one of the five values.
403Your source address is not on the key's allowlist — see IP allowlist.
404doctorId is not mapped to addressId. A doctor belongs to specific locations.

An empty slots array is a successful response, not an error: the doctor has no capacity that day. Use nextAvailableDate to move on.

Next

On this page