SMART Scheduling Links, published by HL7 International / Patient Administration. This guide is not an authorized publication; it is the continuous build for version 1.0.0-ballot built by the FHIR (HL7® FHIR® Standard) CI Build. This version is based on the current content of https://github.com/HL7/smart-scheduling-links/ and changes regularly. See the Directory of published versions
| Page standards status: Informative |
This guide explains how a Slot Publisher makes an appointment slots available across multiple EHRS or bookings portals to a Slot Discovery Client. For background and role definitions, see README.md.

Key Actors
Slot Publisher – EHRs or booking portals provide a list of the available providers and slots that are available via provider organizations. This will include all the available FHIR resources including practitioner, practitioner role, health service, location, and organizations. Slot publishers will make vias avaiable via bulk publish speficiation.
Directories – Could be a directory of slots or can include complete provider directories. This actor will serve as both a client and servier will consume the slots via the bulk publish API. Publishers can expose available slots with nothing more than static web hosting (e.g., from a cloud storage bucket or off-the-shelf web server). This low effort approach does not require a full FHIR server.
Client Scheduling Applications –Apps can then connect to the directory and find available appointments that best suite their needs eliminating the back and forth need to call providers to book appointments. Patients or the consumer can more easily find the best slot availale to suite their need rather a docters appointment, booking a vaccine or a vaerity of other healthcare services based on provider, location, time, find the appointment slot that best fits their need without having to pick-up the phone.
sequenceDiagram
participant EHR
participant Directory as Directory of Slots
participant Client as Client Scheduling App
EHR->>Directory: Publish Slots
Client->>Directory: Find Open Slots
activate Directory
Directory-->>Client: Return Open Slot
deactivate Directory
Client->>EHR: Book Open Slot via SMART Scheduling Link
The EHR or booking portal will provide the data via the bulk publish to allow for NDJSON files for consumption for the directory of slots. The directory of slots will then consume the NDJSON to make available to the client application. The directory will presnt slots to client discovery app to allow for the selection of available slots from multiple booking portals. The slots will then available for presenation to client scheduling applications. The patient will leverage the deep link to book directly back into the EHR or booking portal. This light and easily approach allows for simple rendering of availabilty of many appointment types.
A Slot Publisher hosts not only appointment slots, but also Locations, PractitionerRoles, and Schedules associated with these slots:

Concretely, a Slot Publisher hosts six kinds of files:
Practitioner, Practitioner Role, Healthcare Service, Location, Schedule, Slot
$bulk-publish (a convention used when publishing data sets using FHIR; this convention applies any time a potentially large data set needs to be statically published).
Example file showing ten locations for the fictional "SMART Urgent Care". Each line provides details about a single physical location in the MA area.
Schedule Files. Each line contains a minified JSON object representing the calendar for a healthcare service offered at a specific location or by a specific practitioner role.
A client queries the manifest on a regular basis, e.g. once every 1-5 minutes. The client iterates through the links in the manifest file to retrieve any PractitionerRole, Location, Schedule, or Slot files it is interested in. (Clients SHOULD ignore any output items with types other than PractitionerRole, Location, Schedule, or Slot.)
Wherever “timestamps” are used in this specification, they SHALL be in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz (e.g. 2015-02-07T13:28:17.239+02:00 or 2017-01-01T00:00:00Z). The time SHALL specified at least to the second and SHALL include a time zone offset (for UTC, the offset MAY be Z). These timestamps match FHIR’s “instant” format, and are also valid ISO 8601 timestamps.
Accept HeadersFor Bulk Publication Manifest requests, servers SHALL support at least the following Accept headers from a client, returning the same FHIR JSON payload in all cases:
Accept header presentAccept: application/jsonFor Bulk Output File requests, servers SHALL support at least the following Accept headers from a client, returning the same FHIR NDJSON payload in all cases:
Accept header presentAccept: application/fhir+ndjsonCache-Control: max-age=<seconds> header as a hint to clients about how long (in seconds) to wait before polling next. For example, Cache-Control: max-age=300 indicates a preferred polling interval of five minutes.If-None-Match or If-Modified-Since with each query to prevent retrieving data when nothing has changed since the last query.?_since={} query parameter with a timestamp when retrieving a manifest file to request only changes since a particular point in time. Servers are free to ignore this parameter, meaning that clients should be prepared to retrieve a full data set.$bulk-publish content at open, publicly accessible endpoints when sharing general healthcare appointment availability (no required access keys or client credentials). This pattern ensures that data can be used widely and without pre-coordination to meet public health and consumer access use cases.
$bulk-publish pattern, Slot Publishers host static files, which scale well to open publicationThe manifest file is the entry point for a client to retrieve scheduling data. The manifest JSON file includes:
| field name | type | description |
|---|---|---|
transactionTime |
timestamp as string | the time when this data set was published. See "timestamps" for correct formatting. |
request |
url as string | the full URL of the manifest |
output |
array of JSON objects | each object contains a type, url, and extension field |
→ type |
string | whether this output item represents a "PractitionerRole","Location", "Schedule", or "Slot" file |
→ url |
url as string | the full URL of an NDJSON file for the specified type of data |
→ extension |
JSON object | contains tags to help a client decide which output files to download |
→ → state |
JSON array of strings | state or jurisdiction abbreviations (e.g., ["MA"] for a file with data pertaining solely to Massachusetts) |
(For more information about this manifest file, see the FHIR bulk data specification.)
{
"transactionTime": "2021-01-01T00:00:00Z",
"request": "https://example.com/covid-vaccines/$bulk-publish",
"output": [
{
"type": "Schedule",
"url": "https://example.com/data/schedule_file_1.ndjson"
},
{
"type": "PractitionerRole",
"url": "https://example.com/data/practitionerrole_file_1.ndjson"
},
{
"type": "Location",
"url": "https://example.com/data/location_file_1.ndjson"
},
{
"type": "Slot",
"url": "https://example.com/data/slot_file_MA.ndjson",
"extension": {
"state": ["MA"]
}
},
{
"type": "Slot",
"url": "https://example.com/data/slot_file_NEARBY.ndjson",
"extension": {
"state": ["CT", "RI", "NH"]
}
}
],
"error": []
}
The Booking Portal is responsible for handling incoming deep links.
Each Slot exposed by the Slot Publisher can include an extension indicating the Booking Deep Link, a URL that the Slot Discovery Client can redirect a user to. The Slot Discovery Client can attach the following URL parameters to a Booking Deep Link:
source: a correlation handle indicating the identity of the Slot Discovery Client, for use by the Provider Booking Portal in tracking the source of incoming referrals.booking-referral: a correlation handle for this specific booking referral. This parameter can optionally be retained by the Provider Booking Portal throughout the booking process, which can subsequently help the Slot Discovery Client to identify booked slots. (Details for this lookup are out of scope for this specification.)For the Slot example above, a client can construct the following URL to provide a deep link for a user to book a slot:
sourcebooking-referralIn this case, if the source value is source-abc and the booking-referral is 34d1a803-cd6c-4420-9cf5-c5edcc533538, then the fully constructed deep link URL would be:
https://ehr-portal.example.org/bookings?slot=opaque-slot-handle-89172489&source=source-abc&booking-referral=34d1a803-cd6c-4420-9cf5-c5edcc533538
(Note: this construction is not as simple as just appending &source=... to the booking-deep-link, because the booking-deep-link may or may not already include URL parameters. The Slot Discovery Client must take care to parse the booking-deep-link and append parameters, e.g., including a ? prefix if not already present.)
This approach maintains clean separation of concerns:
The Provider Booking Portal is responsible for initiating holds within the scheduling system's source of truth, with the Slot Publisher reflecting these state changes in subsequent publications at its discretion based on publication frequency and caching strategies.
Slot data discovered by a Slot Discovery Client may no longer be valid by the time a user reaches the Provider Booking Portal. There are many potential sources of slot invalidity for a given user, including but not limited to:
This specification intentionally does not prescribe how each source of invalidity should be handled, because the appropriate response depends on the implementation context. Instead, it establishes a clear division of responsibility:
"status": "free", Publishers should ensure the Slot is available for booking given current business rules.This model parallels the airline booking analogy: a travel search tool may surface a flight that is no longer available by the time the user clicks through to the airline's site, but the airline's booking system handles this gracefully by presenting close alternatives. The same user experience expectation applies here.
Systems that re-publish data from other Slot Publishers are referred to as Slot Aggregators. If you are a Slot Aggregator, you may wish to use some additional extensions and FHIR features to supply useful provenance information or describe ways that your aggregated data may not exactly match the definitions in the SMART Scheduling Links specification. The practices and approaches in this section are recommendations; none are required for a valid implementation.
Slot Aggregators usually need to assign new id values to resources in order to make sure they are unique across the aggregated dataset. The id of the original resource SHOULD be preserved in the identifier list, alongside the identifiers from the original resource. The specific system and value used for the identifier is left up to the implementer.
For example, given this Location resource from an underlying source:
{
"resourceType": "Location",
"id": "123",
"identifier": [
{
"system": "https://healthsystem.example.com/facility-directory",
"value": "FAC-PITT-001"
}
],
"name": "Berkshire Family Medicine - Pittsfield",
// additional Location fields here...
}
```js
A _Slot Aggregator_ might publish a Location like:
```js
{
"resourceType": "Location",
"id": "456",
"identifier": [
{
"system": "https://healthsystem.example.com/facility-directory",
"value": "FAC-PITT-001"
},
{
"system": "https://berkshirefamilymedicine.example.org/",
"value": "123"
}
],
"name": "Berkshire Family Medicine - Pittsfield",
// additional Location fields here...
}
In this example, https://berkshirefamilymedicine.example.org/ is an arbitrary string that defines “Flynn’s Pharmacy” as the identifier system. Slot Aggregators should only use a URL that is not under their control in cases where the URL is predictable and might reasonably be chosen by other Slot Aggregators. When this is not the case, Slot Aggregators SHOULD choose a URL under their control. For example, an aggregator at usdr.example.org might choose a URL like https://usdr.example.org/fhir/identifiers/flynns.
When the source system is a SMART Scheduling Links implementation, a Slot Aggregator SHOULD use FHIR’s Resource.meta.source field to describe it. The value is a URI that SHOULD include the URL of the source system, and MAY add the resource id.
For example, given the above example resource at https://api.flynnspharmacy.example.org/fhir/smart-scheduling/$bulk-publish, a Slot Aggregator might publish a location like:
{
"resourceType": "Location",
"id": "456",
"meta": {
"source": "https://api.flynnspharmacy.example.org/fhir/smart-scheduling/Location/123"
},
"identifier": [
{
"system": "https://cdc.gov/vaccines/programs/vtrcks",
"value": "CV1654321"
},
{
"system": "https://flynnspharmacy.example.org/",
"value": "123"
}
],
"name": "Flynn's Pharmacy in Pittsfield, MA",
// additional Location fields here...
}
### Indicate Data “Freshness”
Aggregators may request or receive information from publishers at different times, and understanding how recently data about a Location, Schedule, or Slot was retrieved can help end users gauge the accuracy of items in an aggregated dataset. _Slot Aggregators_ SHOULD use the `lastSourceSync` extension on the `meta` field of any resource to indicate the last time at which the data was known to be accurate:
```js
{
"resourceType": "Schedule",
"id": "123",
"meta": {
"extension": [{
"url": "http://hl7.org/fhir/StructureDefinition/lastSourceSync",
"valueDateTime": "2021-04-19T20:35:05.000Z"
}]
}
// additional Schedule fields here...
}
Because source systems may experience errors or may not conform to the SMART Scheduling Links specification, Slot Aggregators need additional tools to describe unusual situations that are not relevant to first-party Slot Publishers. Specifically, Slot Aggregators may describe Schedules where:
Use the optional "Has Availability" extension to convey that a Schedule has non-zero or unknown future availability, without conveying details about when or how much. When used on a Schedule, that Schedule MAY have no associated Slots. Slot Publishers SHALL NOT use this capacity in place of publishing granular Slots; it is defined to support Slot Aggregators (i.e. systems that re-publish Slot data from other APIs).
| field name | type | description |
|---|---|---|
url |
string | Fixed value of "http://fhir-registry.smarthealthit.org/StructureDefinition/has-availability" |
valueCode |
string | One of: <ul><li>"some": The Schedule has non-zero future availability.</li><li>"none": The Schedule has no future availability.</li><li>"unknown": The schedule has unknown future availability (e.g. because there is no source of data for this schedule or because the source system had errors or was unparseable).</li></ul> |
Example usage on a Schedule:
{
"resourceType": "Schedule",
"id": "456",
"serviceType": [
{
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/service-type",
"code": "124",
"display": "General Practice"
}
]
}
],
"extension": [
{
"url": "http://fhir-registry.smarthealthit.org/StructureDefinition/has-availability",
"valueCode": "some"
}
],
"actor": [
{
"reference": "Location/123"
}
]
}