Skip to main content

GET /appointments/availability

Query bookable appointment slots for a given rooftop, route, and date. Returns the times a HopDrive driver could be at the consumer's pickup address — the times you can offer the consumer for their appointment.

Use this endpoint to populate a time-picker UI in your SLT (Service Lane Tool) or to surface availability via your scheduling integration before creating an appointment with POST /appointments.

Example Request

GET /v1/appointments/availability?customer_id=1234&pickup_address=210 S Mulberry St, Richmond, VA 23220&delivery_address=11161 Research Plaza Way, Richmond, VA 23236&date=2026-05-14
Authorization: Bearer <jwt>

Query Params

For each leg of the route (pickup and delivery), provide either an address string or a HopDrive location ID — whichever you have. Pass an address string and HopDrive resolves (and, when necessary, creates) the underlying location for you, the same way the move-creation endpoints accept addresses. Pass a location ID — e.g. one you got from GET /locations — and that leg is used as-is with no address lookup. The two legs are independent: you can supply a location ID for one and an address for the other.

FieldTypeRequired?Description
customer_idNumberRequiredHopDrive dealer account ID (the rooftop). Must match an authorized rooftop on your bearer token.
pickup_addressStringPer leg†The consumer's pickup address as a single string (e.g. 210 S Mulberry St, Richmond, VA 23220).
pickup_location_idNumberPer leg†HopDrive location ID for the pickup. Used as-is; takes precedence over pickup_address when both are sent.
delivery_addressStringPer leg†The dealership service address as a single string.
delivery_location_idNumberPer leg†HopDrive location ID for the dealership service address. Used as-is; takes precedence over delivery_address.
dateStringRequiredUTC calendar date in YYYY-MM-DD format. Slots are computed for the local timezone of the rooftop.
consumer_actionStringOptionalThe scenario variant: concierge-pu, concierge-pu-no-rideshare, concierge-ret, concierge-ret-no-rideshare, concierge-loaner-pu, or concierge-loaner-ret.

† For each leg you must supply one of its two inputs: pickup_address or pickup_location_id for the pickup, and delivery_address or delivery_location_id for the delivery. If both are sent for a leg the location ID is used and the address is ignored. If neither is sent for a leg, the request is rejected with REQUEST_INVALID.

The scheduling strategy and customer tier are not caller-configurable — both are derived from the rooftop's record on each request.

Example Response

200 /v1/appointments/availability
{
"rooftop_timezone": "America/Chicago",
"eligible": true,
"reason": null,
"slots": [
{
"pickup_start": "2026-05-14T13:00:00.000Z",
"appointment_time": "2026-05-14T13:20:00.000Z",
"return_end": "2026-05-14T13:45:00.000Z",
"available_drivers": 2
},
{
"pickup_start": "2026-05-14T13:30:00.000Z",
"appointment_time": "2026-05-14T13:50:00.000Z",
"return_end": "2026-05-14T14:15:00.000Z",
"available_drivers": 1
}
]
}

Response Fields

FieldTypeDescription
rooftop_timezoneStringIANA timezone of the rooftop. Use this to render slot times in the dealership's local clock without a second round-trip.
eligibleBooleantrue when bookable slots are returned. false for ineligible responses (see error responses below).
reasonStringnull on eligible responses. On ineligible responses, a stable machine-readable reason code (APPOINTMENT_MANAGEMENT_DISABLED, etc.).
slotsArrayBookable slot windows, sorted by appointment_time ascending. Empty array when eligible: false.
slots[].pickup_startStringISO 8601 UTC — when the driver leaves the dealer for the consumer's pickup address.
slots[].appointment_timeStringISO 8601 UTC — when the driver arrives at the consumer's pickup address. The customer-facing appointment anchor.
slots[].return_endStringISO 8601 UTC — when the vehicle is back at the dealer. Anchors when service can begin.
slots[].available_driversNumberCount of distinct drivers (or hypothetical gig-labor rows) that could fulfill this slot. Always ≥ 1. Filter on this to require redundancy.

Error Responses

HTTPCodeCause
400REQUEST_INVALIDA required query parameter is missing, malformed, or has the wrong type; a leg is missing both its _address and _location_id; or an _address could not be geocoded / matched more than one location.
401ACCESS_DENIEDMissing / invalid bearer token, OR the bearer token does not authorize the requested customer_id.
404REQUEST_INVALIDcustomer_id does not match an existing rooftop.
406ADDRESS_INVALIDRooftop is not bookable (appointment management disabled, rooftop closed for the date, no drivers configured). See reason in body.
500UNKNOWNUnexpected server error.
502UNKNOWNDrive-time computation against Google Routes failed or timed out. Retry shortly.

Ineligible Response Reasons

When HTTP 406 is returned, the response body's reason field carries a stable code:

ReasonMeaning
APPOINTMENT_MANAGEMENT_DISABLEDThe rooftop has not enabled appointment management. Contact HopDrive support to enable.
CLOSED:<closure-name>The rooftop is closed for date (e.g., a holiday, PTO, or recurring weekly closure). The closure name follows the colon.
NO_DRIVERS_CONFIGUREDThe rooftop's driver count is 0. Contact HopDrive support to provision driver capacity.

Worked Examples

Cold lane (first call for this consumer pickup ↔ dealer pair)

The very first availability query for a new (pickup, dealer) tuple triggers an address-resolution step (geocoding + location lookup/creation) plus a Google Routes call inline to compute drive times. Expect ~200–500 ms response time on cold calls.

GET /v1/appointments/availability?customer_id=1234&pickup_address=210 S Mulberry St, Richmond, VA 23220&delivery_address=11161 Research Plaza Way, Richmond, VA 23236&date=2026-05-14

Response is identical in shape to the warm case; the resolved locations and drive times are now cached for subsequent calls. Re-sending the same address strings resolves to the same locations without re-creating them.

Warm lane (subsequent calls for the same pair)

After the first call, drive times are cached in HopDrive's lane table. Subsequent availability queries against the same (pickup, dealer) pair are sub-100 ms — they read the cached drive time and run only the slot-packing algorithm.

Disabled rooftop

406 /v1/appointments/availability
{
"rooftop_timezone": "America/Chicago",
"eligible": false,
"reason": "APPOINTMENT_MANAGEMENT_DISABLED",
"slots": [],
"errors": [
{
"code": "ADDRESS_INVALID",
"message": "APPOINTMENT_MANAGEMENT_DISABLED"
}
]
}

The rooftop hasn't enabled appointment management. Coordinate with HopDrive support.

Rooftop closed for the date

406 /v1/appointments/availability
{
"rooftop_timezone": "America/Chicago",
"eligible": false,
"reason": "CLOSED:Memorial Day",
"slots": [],
"errors": [
{
"code": "ADDRESS_INVALID",
"message": "CLOSED:Memorial Day"
}
]
}

The rooftop is closed for date due to the named closure. Suggest a different date to the consumer.

Saturated day (all slots booked)

A fully-booked rooftop returns an empty slots array but eligible: true:

200 /v1/appointments/availability
{
"rooftop_timezone": "America/Chicago",
"eligible": true,
"reason": null,
"slots": []
}

This signals "the rooftop is bookable in principle, but no slots are available on this date." Different from eligible: false, which means the rooftop can't be booked at all.

Notes

  • All timestamps are UTC ISO 8601. Use rooftop_timezone to render in the dealership's local clock.
  • Slot rounding is fixed at 5-minute intervals.
  • available_drivers is informational. The endpoint does not reserve drivers — that happens at POST /appointments. A slot that's available now may not be at booking time.
  • Drive times are computed against Google Routes in traffic-unaware mode for consistency across calls.
  • Lane / route data is cached indefinitely once computed. Road-network changes are rare enough that this is acceptable; contact HopDrive support if you suspect stale routing.