This guide provides everything you need to integrate with the Ascend Public API.
Sample applications can be found here.
Rate-limited endpoints include a Rate-Limiting-Remaining response header indicating how many requests remain before a 429 Too Many Requests error is triggered. If the limit is exceeded, the response will also include a Retry-After header specifying when it is safe to make another call. It is the responsibility of the calling application to monitor these headers and implement self-throttling logic accordingly. This approach helps avoid unnecessary rate-limited errors while still enabling near real-time processing. Please note that rate limits will change dynamically based on server volatility; therefore, your code must adapt in real time based on the response headers to remain functional.
Beta endpoints are not versioned. They are typically not safe to use in any production environment. The data formats are subject to change at any time.
Once an endpoint has been versioned, the Dentrix Ascend team will try to keep that version working even if we make a breaking change. This will not always be possible in the beginning of the project.
The API is a separate code base from the core product. Versioned endpoints are treated as stable integration contracts whenever possible.
Endpoints will only be versioned if there are clear breaking changes for clients. Changing how a domain model's data is organized is probably a breaking change.
Adding new fields to a domain model is typically not a breaking change. Your client code that uses the Ascend API should follow the tolerant reader pattern. As new fields are added to API domain models, your client code should be able to handle and ignore fields that your code doesn't know about.
The Ascend API uses the standard OAuth2 client credentials flow. We will give you a client_id and a client_secret. Always make sure that your credentials are never compromised.
Never expose your client_id and client_secret in a browser, iOS, or Android application. If you need to access the Ascend API directly from a browser, iOS, or Android application, use a secure backend server application to fetch an OAuth token and pass the OAuth access token to your browser, iOS, or Android application. If or when your access token expires after an hour, use your secure backend application to renegotiate a new access token.
To use the API, fetch an access token with a request like this:
POST https://test.hs1api.com/oauth/client_credential/accesstoken?grant_type=client_credentials
Body should be of type "x-www-form-urlencoded" with key value pairs of:
client_id: your_client_id
client_secret: your_client_secret
Headers:
Content-Type: application/x-www-form-urlencodedYou can also fetch an access token using the curl command like this:
curl --data "grant_type=client_credentials&client_id=xxx&client_secret=xxx" https://test.hs1api.com/oauth/client_credential/accesstoken?grant_type=client_credentialsIf successful, you will receive a 200 status response code and a body that looks like this:
{
"access_token": "rldbHIMMk9NXGVvWXh0023klkdZP",
"expires_in": "3599",
"token_type": "Bearer"
}The access_token you receive will be good for 1 hour. It can be used to access all Ascend dental organizations that you have been granted access to.
You must always pass the access token to all API requests as a header that looks like this:
Authorization: Bearer access_tokenA dental organization is a dental company. A dental organization can have 1 to many locations. A location is normally a physical dental office.
You always need to pass an Organization-ID header for each request made to the API. Ascend will verify that your client_id has access to the requested Organization-ID with the access_token.
The Ascend API follows normal REST API conventions. You always need to pass a header named Organization-ID for each request.
Base URL: The Public API base URL includes /api. For example: https://test.hs1api.com/ascend-gateway/api (Sandbox) and https://prod.hs1api.com/ascend-gateway/api (Production). All versioned paths are relative to this base (e.g. /v1/patients → …/api/v1/patients).
You can quickly see which OrgMapper organizations your client_id has access to by calling OrgMapper’s LinkedOrgs endpoint with the same Bearer token you use for Public API requests:
GET https://test.hs1api.com/orgmapper/LinkedOrgs(Production: https://prod.hs1api.com/orgmapper/LinkedOrgs.)
OrgMapper returns plain JSON (not the Ascend Public API statusCode / data envelope):
{
"organizations": [
"5xxxxxxxxxxxxxxxxxxxxxxx0f",
"5xxxxxxxxxxxxxxxxxxxxxx66"
]
}These strings are OrgMapper organization identifiers you can use together with your credentials (for example when choosing an Organization-ID context where applicable). This operation is documented alongside the Public API in Redocly for convenience; it is served by OrgMapper, not by the Public API application process.
Like a normal REST API, the Ascend API exposes single model GET endpoints and bulk GET endpoints that return multiple models.
A single GET request follows the pattern of /v1/patients/123 where 123 is the id of the patient.
To make a bulk GET request for multiple models, just omit the id at the end of the URL (123 above).
All GET requests can accept a responseFields parameter.
All GET requests can take an optional responseFields parameter in the URL. The responseFields is a comma-delimited string of field names that you want to fetch.
GET /v1/patients/123?responseFields=firstName,lastNameThe response body will look like this:
{
"statusCode": 200,
"data": {
"type": "PatientV1",
"id": "123",
"firstName": "Ari",
"lastName": "Stone"
}
}The API will always return the id and type fields plus all the other fields specified in the responseFields parameter.
If you are fetching multiple objects in a GET request, the following URL parameters are normally available.
Most bulk GET endpoints can take a filter parameter to specify what data you want.
GET /v1/patients?filter=firstName==Bobby{
"statusCode": 200,
"data": [
{
"type": "PatientV1",
"firstName": "Bobby"
}
],
"meta": {
"pagination": {
"limit": 100,
"offset": 0,
"total": 8
}
}
}We will only return patients with firstName == Bobby.
The meta result includes a total of 8 - meaning there were 8 patients found with this filter of firstName==Bobby.
| Operator | Description |
|---|---|
== | equals |
!= | not equals |
> | greater than |
>= | greater than or equals |
< | less than |
<= | less than or equals |
-> | contains |
~= | partial match (LIKE / contains substring) |
<>= | not partial match (NOT LIKE) |
Example of contains:
GET /v1/patients?filter=id->[123, 456]This will return the patients with the ids of 123 and 456.
You can add multiple filters to a single bulk GET request:
GET /v1/patients?filter=firstName~=Ba,lastName~=WoBy default, the API will return the first 100 models. Bulk GET requests include a meta response that includes the limit, offset and total fields. The total field is how many possible models that exist in the API at the time you made the request. If no lastModified date is supplied, the API will add a default one. It is highly recommended that you supply your own for predictable behavior.
You can page through sets of models by passing in a lastId parameter and a filter of some sort will be required going forward.
Pretend that you fetched 100 patients and the last patient's id in the set was 12345.
GET /v1/patients?filter=lastModified>2000-01-01You could fetch the next set of 100 patients with this request:
GET /v1/patients?lastId=12345&filter=lastModified>2000-01-01You can page through huge sets of data using the lastId parameter. Please avoid doing this very often. If you need a data sync, use the Stream API described below.
You can combine the parameters responseField, filter, pageSize and lastId together for requests:
GET /v1/patients?lastId=123,filter=lastName~=Wo,lastModified>2000-01-01,responseFields=firstName,lastNameFuture re-syncs would utilize the lastModified date of the last record.
POST and PUT requests follow normal REST API conventions for the most part.
POST and PUT response bodies contain fields that have been updated.
For example, if you update a patient's firstName to "Bob" and the value was already "Bob" you will not see the firstName in the response payload because the value did not change.
If you want to manually clear out a field, you must pass in the value of null. Example of a PUT body to set a title field to null:
{ "title": null }Setting fields to null only works if the field is nullable.
Many customers only need a data sync between Dentrix Ascend and their own application. In all of these cases, do NOT use the bulk GET requests in the API to keep your application in sync with Ascend.
In some situations, customers need to know what data has been deleted in Ascend. Ascend typically does not retain deleted data. In situations like this, customers would need to fetch the entire set of data in Ascend and compare their own local copies of the data to see what has been deleted. This is very harmful to the Ascend product and should not be done. If customers need to synchronize data changes, they should use the Stream API. The Stream API will let you know if something has been deleted in around 1 second.
The Stream API allows customers to listen for whenever a create, update or delete has happened in the API and get a notification near real time. Often messages are sent in less than a second.
Customers can receive updates for a subset of domain models they care about. Customers can receive data in a guaranteed fashion. If a customer's listener goes down, the messages they care about are queued up in the Ascend API system until customers come back online.
Whenever a customer starts listening to the Stream API, they create a queue that lives in the Ascend system (or multiple queues). If a customer's code is running, data will typically flow to the customer's code in less than a second.
If a customer creates a durable queue and stops listening to that queue, data will be backed up on our system's hard disk. If a customer creates queues and stops listening to them, we will eventually delete the queue(s). It's possible that a queue bound to hundreds of Organizations and Domain models could overflow our system disk in a few hours. We will try to warn customers and give them a chance to re-attach their queue listeners but in some cases we will need to delete queues to avoid a system shut down. Please be careful in how many durable queues you create and make sure you auto-heal broken listeners.
If you want to see the queues associated with your account, contact your integration contact or refer to your API contract for the available endpoints.
When you create a queue, you will have an opportunity to give it a name. Please name your queues carefully. If we need to purge queues, it's important that we know what the queue is being used for.
To use the Stream API, you will first obtain an access token like any of the other REST API endpoints. Once you have an access token, you can then access one of two endpoints to connect to the Stream API.
You can connect to the Stream API using 2 different methods: STOMP over secure WebSockets or the AMQPS protocol. The AMQPS protocol should be preferred but if you have trouble over networks, STOMP will probably work for you.
The best place to start learning about how to use the Stream API is here: https://github.com/HSOPublicApi/public-api/tree/main/python/streaming
Please follow the examples carefully with your own client_id and client_secret in the sandbox environment.
There are 2 paths where data can be mutated (create, update, delete) in Ascend. The core application (what you see in a browser) and this API.
Many mutations in the core application (what you see in a browser) spawn Stream API messages but not all. If you find a critical mutation in the core application that does not spawn a Stream API message let us know and we can probably hook it up.
API keys, shared secrets or other forms of API authentication such as Username and Password authentication should be protected by following these guidelines:
Only trusted and authorized users may configure or set up the connection with authentication secrets and keys. Make sure they do not inappropriately or insecurely store that credential anywhere but in an unencrypted location. (Ex: hand written notes, photos, or online notes, DropBox etc.)
Maintain least privilege access to the machine that acts as the API consumer and the device or machine that holds a copy of the secrets or credentials or certificates or keys. We will refer to these types of secrets and credentials generally as "keys" for the purpose of this document.
Any personal computer or user level machine with API access to Ascend must follow the principles of least privilege and least number of users, and it is strongly recommended to limit the number of machines and users with access to API capability and API keys.
Machines accessing the API should do so from a protected corporate network where possible. It is not recommended to make API connections from machines on home or public networks. Configure API machines in such a way as to be able to audit the users who have access and be able to record and show who did what with the API and API config files and when. (App logs, Machine & User logs etc.)
Encrypt all API keys, secrets and passwords while not in use. (To achieve maximum protection, it is recommended by NIST to xOR-split and then encrypt the two pieces of a key. See Wikipedia for key splitting.) Control access to the key that encrypts the API keys and secrets and limit access to it. If that is the password to a password manager database then limit access and knowledge of the key to unlock any such key storage or encrypted configuration.
Back up key encryption keys to safe locations following the patterns of least privilege and encryption at rest and resiliency.
Keep API keys and credentials logically and physically separated from other user access on any given machine in which they reside. For example do not store API keys in a part of a drive where users without specific privileges assigned can access them. Ideally, only the service or process that initializes the API activity should have sufficient privileges to read the configuration and, at least temporarily, the admin user who installed the configuration containing the key or credential.
Never provide your API credential or key to another party or over the phone, or by email etc., regardless of who is asking.
All keys or Patient and/or sensitive Data should be encrypted in such a way as to meet HIPAA standards which typically means the latest safe and validated implementation of properly configured and coded Advanced Encryption Standard.