What SMART Scheduling Links is
SMART Scheduling Links is an HL7 specification for publishing open appointment availability. A publisher exposes a$bulk-publish manifest that points at NDJSON files of FHIR
resources: Schedule, Slot, and the actors they reference (Location, Practitioner,
PractitionerRole, HealthcareService). Anyone can poll the manifest to see what’s
bookable and send patients to the publisher’s booking link.
The roles in SSL
The IG defines a small set of roles:- Slot Publisher: a healthcare provider’s API that advertises its available slots.
- Provider Booking Portal: the provider’s UI where a patient books a slot.
- Slot Aggregator: a third-party API that combines data from multiple Slot Publishers and offers unified search across them. It does not own the underlying data.
- Slot Discovery Client: the patient’s booking tool. It searches for slots and sends the patient to the right portal to finish booking.
$bulk-publish feed itself.
haau3 also operates a reference Slot Publisher, its own openly fetchable $bulk-publish
feed, so you can test a direct publisher connection while evaluating the aggregator. It is a
contribution to the open standard. It is not a separate product. See Slot Publisher.
What the Scheduling API does as the aggregator
The Scheduling API turns raw publisher feeds into one queryable model in three steps:- Ingest: each publisher’s latest
$bulk-publishsnapshot is downloaded and tracked. - Validate: every resource is checked for base FHIR R4 validity, then against the
SMART Scheduling Links 1.0.0 profiles (continuous build of October 6, 2026) with strict
referential integrity. A publisher’s live production feed must meet every element the
profiles require. A feed from a publisher still testing its implementation may use the May
2026 ballot’s shape where 1.0.0 tightened it: acceptance in
appointmentTypeinstead of thepatient-typeextension, andbooking-phoneas a string. Records that don’t conform are quarantined and kept out of the served data. - Normalize: conforming slots are flattened into a denormalized
Slotshape with its sourcepublisher, status, start and end times, service types (every coding, with its system), the schedule’s specialties, accepting-patients code, an expandedlocation(name, description, telecom, address, coordinates, physical type),provider(a practitioner, or a healthcare service when the schedule names no practitioner, with its coded specialties),isVirtual, andsource(the publisher’s own id for the slot, the publisher’s snapshot time, and when the API last read it). Fields a source omits stay absent; the API adds nothing.
Limits on a feed
Each cycle reads a publisher’s feed within these limits:Querying availability
Five endpoints serve the model. See the API Reference for full details.GET /v1/scheduling/slots: search availability with filters (status, publisher, service type, provider NPI, specialty, city, state, virtual, time window, accepting-patients) and pagination. Results start from now: with nostartAfter, a slot that started before the request is left out unless you passincludePast=true, and an explicitstartAfterapplies as given. Every slot carries its sourcepublisher, andpublisher=<slug>narrows results to one publisher (useful for a publisher inspecting just its own slots).serviceTypeCodematches a slot when any of itsserviceTypescarries the code.npimatches a slot whose provider carries that National Provider Identifier (ten digits), a clinician’s or an organization’s, across every publisher.specialtymatches a slot when its provider’sspecialtiesor its schedule’sspecialtiescarry the code: sendcodeto match it in any code system, orsystem|codefor one system. Specialty codes are each publisher’s own and are not translated between code systems, so a code finds the publishers that use it, and a specialty a publisher sends as text only cannot be matched.serviceTypeCode,npiandspecialtyeach take a comma-separated list of up to 100 values and match a slot carrying any of them. TheacceptingPatientsfilter (HL7 accepting-patients codesnewpt,existptonlyandexistptfam) is lenient: a slot that does not declare an acceptance code still matches any requested value (absence never hides a slot), while a slot that declares one must equal it. A slot a source marksnopt(not accepting patients) is never served: it is not real availability, so it never appears in results regardless of the filter.GET /v1/scheduling/provider-locations: the matching slots grouped by provider location. One entry per provider at a location, or per location when a slot names no provider, aggregated server side, with exact counts and soonest availability over the full match set. It takes the same filters as/slotsexceptproviderLocationKey, includingincludePast. Paginated by provider location; each one’skeyreturns its slots through/slots?providerLocationKey=<key>.GET /v1/scheduling/publishers: list the registered publishers, each with anenabledflag (theslugvalues accepted by thepublisherfilter). A publisher can be listed and not currently ingesting, so checkenabledrather than assuming every slug returns slots.GET /v1/scheduling/service-types: list the distinct service types observed across served slots, each with itssystemand acountof the slots carrying it: a discovery aid for theserviceTypeCodefilter (the values it accepts), self-healing as publishers change. Like/slots, it counts slots from now unless you passincludePast=true, and only from enabled publishers.GET /v1/scheduling/specialties: list the distinct specialties observed across served slots, from the provider’sspecialtiesand the schedule’s, each with itssystemand acountof the slots carrying it: the values thespecialtyfilter accepts, in each publisher’s own code system. It follows the same rules as/service-types.
Retry-After. They never fall back to
sample or placeholder data, so a 200 is always a real answer. Treat a 503 as retryable; a
malformed request still returns 400 with issues naming each parameter at fault, so the two are
always distinguishable. A parameter an endpoint does not take, or one sent twice, is refused the
same way: see parameters.
Standards status
SMART Scheduling Links went to HL7 ballot in May 2026 as1.0.0-ballot. Its
continuous build, dated October 6,
2026, carries the ballot’s resolutions as version 1.0.0 (STU 1). It is not yet an authorized
publication.
The Scheduling API validates publisher feeds against the 1.0.0 build’s profiles. That build
changed three things a feed carries: every Slot has a patient-type extension saying whether it
is open to new or existing patients, booking-phone is a ContactPoint, and a Location no
longer needs an identifier or an address. A feed from a publisher still testing its
implementation may keep the ballot’s form of the first two, as described under Validate above.
SMART Scheduling Links builds on FHIR Bulk Publish, the mechanism for distributing static,
pre-generated FHIR datasets (provider directories, formularies, scheduling, and similar) through a
$bulk-publish manifest. Bulk Publish is part of version 4 of the HL7
Bulk Data Access IG, balloted as 4.0.0-ballot
(see its publish operation).
The current published version of that IG, 3.0.0, does not include it.