{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-PublicAPI/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":[]},"type":"markdown"},"seo":{"title":"API Consumer Guide","llmstxt":{"hide":false,"sections":[{"title":"Table of contents","includeFiles":["**/*"],"excludeFiles":[]}],"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"api-consumer-guide","__idx":0},"children":["API Consumer Guide"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This guide provides everything you need to integrate with the Ascend Public API."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"sample-applications","__idx":1},"children":["Sample Applications"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Sample applications can be found ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://github.com/HSOPublicApi/public-api"},"children":["here"]},"."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"rate-limiting","__idx":2},"children":["Rate Limiting"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Rate-limited endpoints include a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["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 ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"versioning","__idx":3},"children":["Versioning"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The API is a separate code base from the core product. Versioned endpoints are treated as stable integration contracts whenever possible."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"security","__idx":4},"children":["Security"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The Ascend API uses the standard OAuth2 client credentials flow. We will give you a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["client_id"]}," and a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["client_secret"]},". ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Always make sure that your credentials are never compromised."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Never expose your ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["client_id"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["To use the API, fetch an access token with a request like this:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"POST https://test.hs1api.com/oauth/client_credential/accesstoken?grant_type=client_credentials\n\nBody should be of type \"x-www-form-urlencoded\" with key value pairs of:\n  client_id: your_client_id\n  client_secret: your_client_secret\n\nHeaders:\n  Content-Type: application/x-www-form-urlencoded\n"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You can also fetch an access token using the curl command like this:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"curl --data \"grant_type=client_credentials&client_id=xxx&client_secret=xxx\" https://test.hs1api.com/oauth/client_credential/accesstoken?grant_type=client_credentials\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If successful, you will receive a 200 status response code and a body that looks like this:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"access_token\": \"rldbHIMMk9NXGVvWXh0023klkdZP\",\n  \"expires_in\": \"3599\",\n  \"token_type\": \"Bearer\"\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You must always pass the access token to all API requests as a header that looks like this:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"Authorization: Bearer access_token\n"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A dental organization is a dental company. A dental organization can have 1 to many locations. A location is normally a physical dental office."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You always need to pass an ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Organization-ID"]}," header for each request made to the API. Ascend will verify that your ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["client_id"]}," has access to the requested ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Organization-ID"]}," with the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["access_token"]},"."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"http-api-requests","__idx":5},"children":["HTTP API Requests"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The Ascend API follows normal REST API conventions. You always need to pass a header named ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Organization-ID"]}," for each request."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Base URL:"]}," The Public API base URL includes ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/api"]},". For example: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["https://test.hs1api.com/ascend-gateway/api"]}," (Sandbox) and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["https://prod.hs1api.com/ascend-gateway/api"]}," (Production). All versioned paths are relative to this base (e.g. ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/v1/patients"]}," → ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["…/api/v1/patients"]},")."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You can quickly see which OrgMapper organizations your ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["client_id"]}," has access to by calling ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["OrgMapper’s LinkedOrgs"]}," endpoint with the same Bearer token you use for Public API requests:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"GET https://test.hs1api.com/orgmapper/LinkedOrgs\n"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["(Production: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["https://prod.hs1api.com/orgmapper/LinkedOrgs"]},".)"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["OrgMapper returns plain JSON (not the Ascend Public API ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["statusCode"]}," / ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["data"]}," envelope):"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"organizations\": [\n    \"5xxxxxxxxxxxxxxxxxxxxxxx0f\",\n    \"5xxxxxxxxxxxxxxxxxxxxxx66\"\n  ]\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["These strings are OrgMapper organization identifiers you can use together with your credentials (for example when choosing an ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Like a normal REST API, the Ascend API exposes single model GET endpoints and bulk GET endpoints that return multiple models."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A single GET request follows the pattern of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/v1/patients/123"]}," where ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["123"]}," is the id of the patient."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["To make a bulk GET request for multiple models, just omit the id at the end of the URL (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["123"]}," above)."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"get-requests","__idx":6},"children":["GET Requests"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["All GET requests can accept a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["responseFields"]}," parameter."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"responsefields","__idx":7},"children":["responseFields"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["All GET requests can take an optional ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["responseFields"]}," parameter in the URL. The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["responseFields"]}," is a comma-delimited string of field names that you want to fetch."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"GET /v1/patients/123?responseFields=firstName,lastName\n"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The response body will look like this:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"statusCode\": 200,\n  \"data\": {\n    \"type\": \"PatientV1\",\n    \"id\": \"123\",\n    \"firstName\": \"Ari\",\n    \"lastName\": \"Stone\"\n  }\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The API will always return the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["id"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["type"]}," fields plus all the other fields specified in the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["responseFields"]}," parameter."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"bulk-get-requests","__idx":8},"children":["Bulk GET Requests"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If you are fetching multiple objects in a GET request, the following URL parameters are normally available."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"filter","__idx":9},"children":["filter"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Most bulk GET endpoints can take a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["filter"]}," parameter to specify what data you want."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"GET /v1/patients?filter=firstName==Bobby\n"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"statusCode\": 200,\n  \"data\": [\n    {\n      \"type\": \"PatientV1\",\n      \"firstName\": \"Bobby\"\n    }\n  ],\n  \"meta\": {\n    \"pagination\": {\n      \"limit\": 100,\n      \"offset\": 0,\n      \"total\": 8\n    }\n  }\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["We will only return patients with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["firstName == Bobby"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The meta result includes a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["total"]}," of 8 - meaning there were 8 patients found with this filter of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["firstName==Bobby"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"filter-operators","__idx":10},"children":["Filter Operators"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Operator"},"children":["Operator"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Description"},"children":["Description"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["=="]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["equals"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["!="]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["not equals"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":[">"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["greater than"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":[">="]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["greater than or equals"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["<"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["less than"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["<="]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["less than or equals"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["->"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["contains"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["~="]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["partial match (LIKE / contains substring)"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["<>="]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["not partial match (NOT LIKE)"]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Example of contains:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"GET /v1/patients?filter=id->[123, 456]\n"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This will return the patients with the ids of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["123"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["456"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You can add multiple filters to a single bulk GET request:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"GET /v1/patients?filter=firstName~=Ba,lastName~=Wo\n"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"pagesize-and-lastid","__idx":11},"children":["pageSize and lastId"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["By default, the API will return the first 100 models. Bulk GET requests include a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["meta"]}," response that includes the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["limit"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["offset"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["total"]}," fields. The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["total"]}," field is how many possible models that exist in the API at the time you made the request. If no ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["lastModified"]}," date is supplied, the API will add a default one. It is highly recommended that you supply your own for predictable behavior."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You can page through sets of models by passing in a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["lastId"]}," parameter and a filter of some sort will be required going forward."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Pretend that you fetched 100 patients and the last patient's id in the set was ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["12345"]},"."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"GET /v1/patients?filter=lastModified>2000-01-01\n"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You could fetch the next set of 100 patients with this request:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"GET /v1/patients?lastId=12345&filter=lastModified>2000-01-01\n"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You can page through huge sets of data using the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["lastId"]}," parameter. ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Please avoid doing this very often."]}," If you need a data sync, use the Stream API described below."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You can combine the parameters ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["responseField"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["filter"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["pageSize"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["lastId"]}," together for requests:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"GET /v1/patients?lastId=123,filter=lastName~=Wo,lastModified>2000-01-01,responseFields=firstName,lastName\n"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Future re-syncs would utilize the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["lastModified"]}," date of the last record."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"post-put-requests","__idx":12},"children":["POST, PUT Requests"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["POST and PUT requests follow normal REST API conventions for the most part."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["POST and PUT response bodies contain fields that have been updated."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For example, if you update a patient's ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["firstName"]}," to \"Bob\" and the value was already \"Bob\" you will not see the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["firstName"]}," in the response payload because the value did not change."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If you want to manually clear out a field, you must pass in the value of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["null"]},". Example of a PUT body to set a title field to null:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{ \"title\": null }\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Setting fields to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["null"]}," only works if the field is nullable."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"data-sync--stream-api","__idx":13},"children":["Data Sync / Stream API"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Many customers only need a data sync between Dentrix Ascend and their own application. In all of these cases, ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["do NOT use the bulk GET requests in the API to keep your application in sync with Ascend."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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. ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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. ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Please be careful in how many durable queues you create and make sure you auto-heal broken listeners."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You can connect to the Stream API using 2 different methods: ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["STOMP over secure WebSockets"]}," or the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["AMQPS protocol"]},". The AMQPS protocol should be preferred but if you have trouble over networks, STOMP will probably work for you."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["The best place to start learning about how to use the Stream API is here:"]}," ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://github.com/HSOPublicApi/public-api/tree/main/python/streaming"},"children":["https://github.com/HSOPublicApi/public-api/tree/main/python/streaming"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Please follow the examples carefully with your own ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["client_id"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["client_secret"]}," in the sandbox environment."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"stream-api-availability","__idx":14},"children":["Stream API Availability"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"dentrix-ascend-api-security-best-practices","__idx":15},"children":["Dentrix Ascend API Security Best Practices"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["API keys, shared secrets or other forms of API authentication such as Username and Password authentication should be protected by following these guidelines:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["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.)"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["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."]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["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."]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["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.)"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["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."]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Back up key encryption keys"]}," to safe locations following the patterns of least privilege and encryption at rest and resiliency."]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["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."]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Never provide your API credential or key to another party"]}," or over the phone, or by email etc., regardless of who is asking."]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["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."]}]}]}]},"headings":[{"value":"API Consumer Guide","id":"api-consumer-guide","depth":1},{"value":"Sample Applications","id":"sample-applications","depth":2},{"value":"Rate Limiting","id":"rate-limiting","depth":2},{"value":"Versioning","id":"versioning","depth":2},{"value":"Security","id":"security","depth":2},{"value":"HTTP API Requests","id":"http-api-requests","depth":2},{"value":"GET Requests","id":"get-requests","depth":2},{"value":"responseFields","id":"responsefields","depth":3},{"value":"Bulk GET Requests","id":"bulk-get-requests","depth":2},{"value":"filter","id":"filter","depth":3},{"value":"Filter Operators","id":"filter-operators","depth":3},{"value":"pageSize and lastId","id":"pagesize-and-lastid","depth":3},{"value":"POST, PUT Requests","id":"post-put-requests","depth":2},{"value":"Data Sync / Stream API","id":"data-sync--stream-api","depth":2},{"value":"Stream API Availability","id":"stream-api-availability","depth":2},{"value":"Dentrix Ascend API Security Best Practices","id":"dentrix-ascend-api-security-best-practices","depth":2}],"frontmatter":{"title":"API Consumer Guide","toc":true,"category":"getting-started","description":"Complete guide for integrating with the Ascend Public API including authentication, HTTP requests, bulk operations, and real-time data sync.","seo":{"title":"API Consumer Guide"}},"lastModified":"2026-05-13T15:05:21.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/publicapi/api-consumer-guide","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}