Introduction
About
Healthie offers a web and mobile platform for healthcare tech organizations to launch and scale digital services. Developer teams can use our API to build client-facing, and provider-facing, apps, leveraging Healthie’s back-end, data architecture, and sample front-ends.
For Developers & Customers
Healthie makes available the same API that our own front-end developers use to build Healthie (Healthie Help Guide Linked Here), so the functionality covers our entire platform, including client engagement, coaching, provider management, telehealth, insurance billing, EHR, and more. As a result, product and engineering teams are able to focus on the implementation of specific care models to deliver ideal client-facing experiences.
Many organizations also leverage our API to pull detailed data for reporting & analytics that spans business operations, financial performance, client engagement, and client outcomes.
Our developer support team is here to help answer questions you may have about the API. We encourage you to contact us: hello@gethealthie.com and also share your feedback & requests as you explore and utilize our API.
For Marketplace Partners
At Healthie, it’s our vision to improve access to healthcare and enable better healthcare outcomes through technology. By building partnerships across the healthcare and technology industry, our Marketplace helps to break down barriers to workflows, processes, and care.
Our Marketplace makes it easy for businesses to use software solutions together to enhance efficiency, increase revenue, and deliver patient care. Healthie partners with a wide range of technology platforms, as well as service providers, that collectively enable our customers to achieve the solutions they need for better business or client care outcomes.
If you are an independent software provider serving a mutual customer base with Healthie, you can leverage our API and sandbox access to self-build an integration with our platform.
Learn more about becoming a Marketplace Partner here or email marketplace@gethealthie.com.
GraphQL Overview
Healthie offers a GraphQL API. If you have not worked with GraphQL before, we recommend that you read Introduction to GraphQL and Queries and Mutations over at Learn about GraphQL before you begin development.
Our goal is to adhere to the latest GraphQL specification. If you do not believe that we are adhering to the spec, please contact us and we will work to make your experience better.
Operations
The GraphQL spec provides three types of operations: Queries, Mutations, and Subscriptions.
Operation | Usage |
---|---|
Queries | A read-only fetch |
Mutations | A write followed by a fetch |
Subscriptions | A long-lived request that fetches data in response to source events |
Queries
Requests to the Healthie API are all HTTP requests, so any HTTP client (cURL, Axios, Net:HTTP, etc) can be used to make API requests. However, using tools with better GraphQL support can help a lot when using Healthie's API. Here are our recommendations.
The easiest way to learn about and run your first GraphQL query is by using our API Explorer. It is a fully-fledged GraphQL IDE based on GraphiQL, providing you documentation and an easy to use interface to run queries.
As you progress beyond that, you can make queries via API tools like Insomnia or Postman.
Finally, when it comes time to write the actual code, we highly recommend using a GraphQL client, versus trying to construct the direct HTTP requests. Two examples are Apollo Client (for JS) and GraphQL-Client (for Ruby).
Example Projects
We have an open-source widget that utilizes the API on Github. That repo is a great place is to get started if you are looking for an example project that uses our API.
Developer Support
Healthie prides ourselves on our hands-on developer support, available to all API customers.
- We’re happy to answer any questions as they come up
- We can help you break down workflows and talk through how you can use the API to accomplish a specific goal
- We’re also happy to be in a Slack channel with your developers, or schedule screenshares/calls
API Concepts
Environments
Healthie offers two API environments - Sandbox and Production. Sandbox and Production are fully seperate environments, and it is not possible for Healthie to copy data between them. Please note, before hard-coding IDs into your integration, that they will be different on Sandbox and Production.
The examples in this documentation use the Sandbox environment's API URL. The URLs are as follows:
Name | URL |
---|---|
Sandbox | https://staging-api.gethealthie.com/graphql |
Production | https://api.gethealthie.com/graphql |
Sandbox Limitations
The sandbox environment is intended for testing and development purposes only. It is not intended for production use. Due to the nature of the environment, some of the features of the API are not available in the sandbox environment. The table below shows which features are available in the sandbox environment.
Feature / integration | Available in sandbox |
---|---|
Change Healthcare E-Labs | No |
Faxing | No |
FitBit | Yes |
Stripe | Yes |
Withings | No |
Zoom | No |
ICD + CPT codes | Limited |
Stripe
You can test Stripe payments in the Healthie sandbox environment. You'll be prompted to add a bank account in the Healthie portal follow the instructions and use Stripe test data for the bank info. You can use any U.S. address.
If you receive the following error when using the createBillingItem
or completeCheckout
mutations, you likely need to complete your setup of the test bank info: "Your destination account needs to have at least one of the following capabilities enabled: transfers, crypto_transfers, legacy_payments"
Change Healthcare: E-Labs
Change Labs is not currently available in the sandbox environment. We recommend working instead with LabOrder
objects, but not with the actual Change connection. Unfortunately, this restriction comes from Change -- their developer environment does not have the ability to automatically trigger tests end-to-end, nor to provision new accounts.
You can generate LabOrder
objects, which the Change integration also generates, in the staging environment via the API to help simulate an end-to-end testing experience.
ICD + CPT codes
Healthie provides a limited subset of ICD and CPT codes in the sandbox environment. We recommend building any workflows that rely on these codes in sandbox using stand-in codes as needed.
Authentication
Healthie uses API keys to allow access to the API. Each Healthie user account can have its own API key, and one user account can have multiple API keys.
Scopes
API keys are scoped to a user account, and the API key takes on the permissions and behaviors of the user account it is connected to. If you want to set up an "admin" API key for server-side actions, make sure you've granted all the appropriate permissions to the Healthie user account associated with that key. Actions done via a user's API key are considered to be done by the user.
You can generate API keys programmatically for other users. If you want to use this functionality, contact Healthie at hello@gethealthie.com and we'll set it up for the user account you specify.
Request headers
Healthie expects the API key to be included in all authenticated API requests to the server, via headers that look like the following:
Authorization: Basic YOUR_API_KEY_HERE
AuthorizationSource: API
If your data is in a shard (you will know if this is the case), you must also include the following header:
AuthorizationShard: YOUR_SHARD_AUTHORIZATION_ID
Generating API keys
You can generate an API key with the createApiKey
mutation.
mutation createApiKey($name: String, $user_id: ID) {
createApiKey(
input: {
name: $name # A used-defined name for the API key. Optional
user_id: $user_id # The ID of the user to create an API key for. Required
}
) {
api_key
api_key_object {
created_at
displayable_key
id
}
messages {
field
message
}
}
}
Rate Limits
Healthie sets limits on how many requests can made against our API within a certain period of time. We put them in place to help ensure the stability and performance of our system. Our rate limit is currently 250 requests per second per API key and 100 sign in requests per minute per user. If you make too many requests over that period, our system will return an error message telling you that the rate limit has been exceeded.
Versioning
As per the GraphQL Best Practices, Healthie's API is not versioned. We avoid breaking changes, and old API clients will continue to work. This is possible since GraphQL only returns the data that's explicitly requested. New capabilities can be added to Healthie's API via new types and new fields on those types without creating a breaking change.
In short, one way to think of a breaking change is as such, a breaking change is one that, given no change to account data, changes the returned values of a query or mutation.
Examples of changes we would consider breaking (and so would not make):
- Removing a field
- Changing the return type of a field
- Changing an input argument default (e.g changing a query to be default unpaginated versus paginated)
Examples of changes we do not consider breaking (and so would make)
- Adding new queries and mutations
- Adding an optional new input field to an existing query or mutation
- Adding a new filter or sort_by option to an existing input field
- Adding new and updated data to Healthie-provided resource queries (e.g Insurance Plans or Icd Codes)
- Fixing an internal server error (e.g a 500)
Error Handling
As noted, our goal is to adhere to the latest GraphQL specification, including Section 7.1.2 on errors. Although the spec is transport-agnostic, our system runs over HTTP and, therefore, we strive to adhere to the latest HTTP specifications, including the semantics described in RFC 9110. As such, our API responds with the most appropriate HTTP response code where applicable.
Pagination
Many queries in Healthie's API are paginated. We use offset-based pagination.
When a query takes in an offset
argument, that specifies where the data returned should start.
For example, our entries
query has a page size of 10. if you have a list of 20 entries, and want to load all of them in, you would first do an entries
query with offset
of 0
and then an entries
query with an offset of 10. Many queries have a corresponding "count" query (e.g entries
has an entriesCount
), That "count" query lets you see the total amount of data that you will need to page through.
If you send in an offset
value that is too large (e.g 150 for a data set that only contains 100 objects), our API will just return an empty array.
Cursor Pagination
Healthie suggests using the new Cursor pagination instead of the previously available offset-based pagination. It is more stable, performant, and easier to use. Each query supporting Cursor pagination has an after
input argument and returned objects provide a cursor
field. To fetch the next page of results, use the cursor
value from the last item returned in the current query.
{
announcements(page_size: 2, should_paginate: true) {
id
cursor
}
announcementsCount
}
Consider the following query:
{
"data": {
"announcements": [
{
"id": "3",
"cursor": "eyJrIjpbIjMiXX0="
},
{
"id": "2",
"cursor": "eyJrIjpbIjIiXX0="
}
],
"announcementsCount": 3
}
}
Which should give a result like this:
{
announcements(page_size: 1, should_paginate: true, after: "eyJrIjpbIjIiXX0=") {
id
cursor
}
announcementsCount
}
Now, let's take the cursor
value of the last item on the list and use it in the next query:
{
"data": {
"announcements": [
{
"id": "1",
"cursor": "eyJrIjpbIjEiXX0="
}
],
"announcementsCount": 3
}
}
It should return the next set of result after the specified cursor.
Queries that currently support Cursor pagination:
-
announcements
-
api_keys
-
appointment_types
-
appointments
-
billing_items
-
comments
-
conversation_memberships
-
course_memberships
-
courses
-
cpt_codes
-
customEmails
-
document_viewings
-
food_search
-
user_package_selections
Client Mutation ID
In the schema, you will come across the field clientMutationId
on all of our mutations. This is a GraphQL auto-generated field that is not used in our API. You should not send in a clientMutationId
and can ignore the field.
Terminology
Due to the API's evolution over time, and the wide range of use-cases we support, there are a few terms that are used interchangeably throughout the API docs. There are also some terms that may have API objects with non-obvious names. Here is a quick glossary to help you better understand the documentation (especially the Schema mentioned below).
- Patient/Client are used interchangeably.
- Provider/Dietitian are used interchangeably.
- Support Member/Staff Member mean a member of an organization who is not a provider, and are used interchangeably.
- A "Chat Message" is the
Note
object in our API. A "Chart Note" is represented as aFormAnswerGroup
- An "Invoice" is a
RequestedPayment
- A "Payment" is a
BillingItem
Webhooks
Healthie's API offers webhooks for common events.
When the event happens, Healthie will send a HTTP POST request with the following body to a URL you provide us.
You can see an example HTTP POST body in the right-hand column.
{
"resource_id": resource_id, # The ID of the resource that was affected
"resource_id_type": resource_id_type, # The type of resource (can be 'Appointment', 'FormAnswerGroup', 'Entry', or 'Note')
"event_type": event_type # The event that occurred
}
We support different URLs for each event, as well as multiple URLs for the same event.
Retry Logic
You can choose to opt into having us retry a failed webhook if your service returns an error when we send a webhook to you. We will retry sending the webhook for up 3 days based on an exponential backoff. After retrying for up to 3 days, we will disable the webhook.
Email on Failure
If your service continues to return an error, we will send an email notification to let you know after approximately 24 hours.
Email on disable
If your service continues to return an error, we will disable the webhook after approximately 3 days.
Better URL validation
We have better validations around the URL of the service that you specify. You will be notified sooner if your URL does not validate.
Signed payloads
Overview
When you receive webhooks from our service, it is important to verify that they were indeed sent by us. This can be achieved by validating the signature in the webhook's headers. Below, we provide the steps and a code sample for verifying the signature using JavaScript.
Signature Verification Process
Each webhook request includes several headers that are used to ensure the authenticity and integrity of the request:
Content-Type
: Specifies the media type of the resource (should beapplication/json
).Content-Digest
: Contains a SHA-256 hash of the request payload.Signature-Input
: Specifies the components used to construct the signature.Signature
: Contains the actual cryptographic signature of the message that you need to verify.
The signature is computed using HMAC (Hash-Based Message Authentication Code) with SHA-256 hashing. The data string used to generate the signature is constructed using specific parts of the request, including the HTTP method, path, query, and body content.
Steps to Verify the Signature
Construct the Data String: Replicate the construction of the data string that was signed on the server side. This involves concatenating the HTTP method, path, query string, content digest, content type, and content length of the request.
Compute the HMAC: Using the same key that the server used, compute the HMAC with SHA-256 of the data string.
Compare the Computed HMAC with the Signature in the Request: The signature provided in the webhook's
Signature
header should match the HMAC you computed. If they match, the request is verified.
JavaScript Code Sample
async function getSigningKey(secretKey) {
const encoder = new TextEncoder();
const keyData = encoder.encode(secretKey);
return await crypto.subtle.importKey(
'raw',
keyData,
{ name: 'HMAC', hash: { name: 'SHA-256' } },
false,
['sign']
);
}
function constructDataToSign({ method, path, query, headers, body }) {
const contentDigest = headers['content-digest'].split('=')[1];
const contentType = 'application/json';
const contentLength = new Blob([JSON.stringify(body)]).size;
return `${method.toLowerCase()} ${path} ${query} ${contentDigest} ${contentType} ${contentLength}`;
}
async function generateSignature(key, data) {
const encoder = new TextEncoder();
const signature = await crypto.subtle.sign('HMAC', key, encoder.encode(data));
return Array.from(new Uint8Array(signature))
.map(byte => byte.toString(16).padStart(2, '0'))
.join('');
}
async function verifySignature(requestData, secretKey) {
const key = await getSigningKey(secretKey);
const dataToSign = constructDataToSign(requestData);
const computedSignature = await generateSignature(key, dataToSign);
const actualSignature = requestData.headers['signature'].split('=')[1];
return computedSignature === actualSignature;
}
// Example usage
const requestData = {
method: 'post',
path: '/uri/path/here',
query: '', // any query params. leave blank if none
body: {
resource_id: 123123123,
resource_id_type: 'Appointment',
event_type: 'appointment.updated',
changed_fields: ["pm_status_last_changed_by_id", "last_updated_by_id"]
},
headers: {
'content-type': 'application/json',
'content-digest': 'SHA-256=content-digest-from-header',
'signature': 'sig1=signature-from-header'
}
};
const secretKey = 'whsec_my-secret-key'; // The secret shared with you
verifySignature(requestData, secretKey)
.then(isValid => console.log('Is the signature valid?', isValid))
.catch(err => console.error('Error verifying signature:', err));
Thin payloads
When webhooks are sent for updates, we will include a changed_fields
property to the payload we send. This property will contain a list of fields that changed during the update.
Please note: For webhooks related to a created
event or deleted
event, we do not send data about the event other than the resource ID and event category. We expect you to make a GraphQL API call after receiving the event, using the resource ID, to get the data you need.
Security
In addition to signing our webhooks, you can whitelist Healthie's current IP addresses as an additional security measure:
Staging / sandbox environment:
18.206.70.225
44.195.8.253
Production environment:
52.4.158.130
3.216.152.234
Please note that Healthie's IP addresses are subject to change. However, a change would be unusual and Healthie would notify your team ahead of time about the change.
Below are the webhook event types available currently:
Type |
---|
applied_tag.created |
applied_tag.deleted |
appointment.created* |
appointment.deleted* |
appointment.updated* |
availability.created |
availability.deleted |
availability.updated |
billing_item.created |
billing_item.updated |
care_plan.created |
care_plan.deleted |
care_plan.updated |
cms1500.created |
cms1500.deleted |
cms1500.updated |
comment.created |
comment.deleted |
comment.updated |
conversation_membership.viewed |
document.created |
document.deleted |
document.updated |
dosespot_notification.created |
entry.created |
entry.deleted |
entry.updated |
form_answer_group.created |
form_answer_group.deleted |
form_answer_group.locked |
form_answer_group.signed |
goal.created |
goal.deleted |
goal.updated |
goal_history.created |
insurance_authorization.created |
insurance_authorization.deleted |
insurance_authorization.updated |
message.created |
message.deleted |
metric_entry.created |
metric_entry.deleted |
metric_entry.updated |
patient.created |
patient.updated |
policy.created |
policy.deleted |
policy.updated |
received_fax.created |
requested_form_completion.created |
requested_form_completion.deleted |
requested_form_completion.updated |
requested_payment.created |
requested_payment.updated |
task.created |
task.deleted |
task.updated |
*The appointment-related webhook events are only triggered for one-time appointments by default. If you also want to receive appointment-related events for recurring appointments, please reach out to the Healthie support team at hello@gethealthie.com and we'll turn them on for you.
Don't see a webhook for an event you want to listen to? Please reach out to us.
Examples
Say you would like to add a special external video link when patients under a particular provider book an appointment. You could:
- listen to the
appointment.created
webhook event - fetch the appointment when the event is received
- if the appointment's provider is a match, use the
updateAppointment
mutation to update the appointment'sexternal_videochat_url
You can find more examples how to leverage Healthie API Webhooks in the Automation Examples section.
When specific webhook events are triggered
Event type | Conditions when triggered |
---|---|
patient.created | When the signUp, completeCheckout, or createClient mutations are called successfully |
patient.updated | When the updateUser or updateClient mutations are called successfully |
message.created | When a message (a Note object) is sent in a conversation |
conversation_membership.created | When a user is added to a conversation |
form_answer_group.created | When a Patient completes a Form (intake, program or charting) |
requested_form_completion.created | When a Provider requests a Patient to complete a Form |
Webhook behavior in the context of sub-organizations
If your company is using sub-organizations in Healthie, you may want certain webhook events to fire only when they are triggered by an action in a given sub-org. Here is how webhook events behave in the context of parent organizations and sub-organizations:
- If the parent org has a webhook set up, all relevant events from all sub-orgs will be sent to that endpoint
- If a sub-org has a webhook set up, only relevant events from that sub-org will be sent to that endpoint
File Uploads
Some Healthie API mutations provide support for uploading files. While it's not natively supported by the Apollo Client, it can be achieved by using third-party libraries like apollo-upload-client.
Alternatively, here's a curl command to upload the image as a base64 string:
curl --request POST \
--url https://staging-api.gethealthie.com/graphql \
--header 'Authorization: Basic KEYHERE' \
--header 'AuthorizationSource: API' \
--header 'Content-Type: application/json' \
--data '{"query":"mutation createDocument($file: String) {\n\tcreateDocument(input: {rel_user_id: \"10\", share_with_rel: true, file_string: $file, display_name: \"example.pdf\"}) {\n\t\tdocument {\n\t\t\tid\n\t\t}\n\t}\n}\n","variables":{"file":"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"},"operationName":"createDocument"}'
Healthie API uses the Upload
type for fields that support file uploads.
For implementation details, please refer to the documentation of the GraphQL upload library of your choice.
API Deprecations
Introduction
Our API evolves over time to add new features, improve functionality, and maintain security. When certain features or practices become outdated, they will be deprecated, meaning they are scheduled to be removed or replaced by a newer alternative. Remember, deprecation does not mean immediate removal but indicates that the feature should be avoided and planned for replacement in the future.
This page is designed to provide you with the latest information about deprecated features within our API. We understand that changes to the API can impact your development process and we are committed to ensuring a smooth transition for all users.
Our API leverages the GraphQL @deprecated
directive to flag deprecated fields and enum values. This feature enables you to see warnings within your development environment or GraphQL Explorer when you are using a deprecated feature.
Deprecation Policy
Our deprecation policy is designed to give developers a clear and predictable path for transitioning from old features and functionality to new ones. When a feature is deprecated:
- We will provide a minimum of 6 months notice before any deprecated feature is removed.
- We will provide migration instructions for deprecated features.
List of Deprecated Features
Below is a list of features that are currently deprecated within our GraphQL API.
String-based sort_by
query arguments
- Deprecation date: November 6, 2023
- Expected removal date: August 6, 2024
- Recommendation: All API clients should use the enum-based
order_by
arguments.
Migration Support
If you have any questions or concerns about migrating from deprecated features, please reach out to our support team at hello@gethealthie.com.
We appreciate your understanding and cooperation as we continue to improve our GraphQL API to serve you better.
User Accounts
There are two primary types of accounts in Healthie: Patients and providers. If a user in Healthie is a patient, that means the user is not a provider and vice versa (the types are mutually exclusive). On the User
type, the field is_patient
signifies whether a user is a patient.
Organizations
When using the Healthie API, your company will have an organization (Organization
type) and potentially sub-organizations associated with it. Every provider in Healthie is associated with one organization, but patients are not associated with an organization directly in the data model. Instead, patients are associated with one or more providers.
If a provider works for multiple organizations or sub-organizations, the provider will have multiple user accounts (one per organization).
Account uniqueness
Account uniqueness in Healthie is based on several factors:
- Every user account is associated with a
namespace
in Healthie. Anamespace
is essentially your organization's authentication space within Healthie. There can only be one patient account with the same combination ofnamespace
,email
,first_name
andlast_name
values. This means two user accounts can have the samenamespace
andemail
iffirst_name
and/orlast_name
differ between them. - Every user account is also associated with a specific
human
in Healthie. Ahuman
is meant to signify an actual person using Healthie. Eachhuman
can have one or more Healthie accounts associated with them. Within a singlenamespace
in Healthie, if multiple accounts exist with the sameemail
, those accounts will typically be associated with the samehuman
. The person using those accounts will be able to switch between them in the Healthie portal UI.
To avoid creating duplicate accounts in Healthie by accident, we typically recommend querying the Healthie API first to check for any users with the email specified by a person booking an appointment or signing up for your service.
Developer and owner permissions
There are no separate "developer" or "owner" account types in Healthie. Instead, these are both permission types controlled by settings on a provider account.
Developer permissions can be changed with the updateOrganizationMembership
mutation using the is_developer
parameter. (Every provider account in an organization has an associated OrganizationMembership
.) The is_owner
field on the User
type shows if a given provider account is the organization owner.
API Explorer
All possible queries, mutations, and subscriptions are covered in our API Explorer. GraphQL is incredibly flexible. Using the API Explorer is a great way to see the different types of queries and mutations you can do, and the data you can get back from them.
Here is the API Explorer in action:
You will need a Sandbox API key to make queries, but you can view the live documentation without one.
While less recommended, you can also view the schema as a website here
React SDKs
In addition to our GraphQL API, we also provide React SDKs to make it easier for developers to work with our API within their existing frontend applications and accelerate the development process.
Our SDKs provide a set of pre-built components and functions that can be integrated into your application, allowing you to access the functionality of Healthie's API with minimal setup and configuration. However, it is important to note that while the SDKs provide a simplified way to interact with our API, the developer is still responsible for providing the scaffolding and overall structure of the application.
Please note that these SDKs are designed to work with our GraphQL API only, and it is recommended to have a basic understanding of our API before using them.
Currently Healthie provides SDKs for the following capabilities:
- Chat
- Forms
- Booking & Buying (Calendar and Packages)
npm install @healthie/sdk
# or
yarn add @healthie/sdk
To get started, you can install the SDK using the following command:
For more information, please visit @healthie/sdk on npm.
Patient (Client) Management
The Patient Object
# Some Example Fields
"user": {
"id": "9900",
"first_name": "Jonas",
"last_name": "Salk",
"dob": "2010-10-10",
"gender": "Male",
"email": "jonas.salk@example.com",
"phone_number": "555-444-2244",
"next_appt_date": null
}
Patients are User
objects.
This is the same object type that is used for providers/staff members.
Due to this, The User
object has an immense amount of fields, many of which are not relevant for patients.
With GraphQL, you select the exact fields you want returned, so you don't need to deal with any non-relevant data.
You can view the full list of available fields here.
If you have questions about how legal_name
and gender
/gender_identity
/sex
interact with each other, We recommend reading our general help articles here and here.
Creating a Patient
Patient accounts are created in three main ways.
Method | Corresponding Mutation |
---|---|
Added by a provider/staff member | createClient |
Self-Sign Up | signUp |
Booking and/or buying | completeCheckout |
createClient
Mutation
mutation createClient($first_name: String,
$last_name: String,
$email: String,
$skipped_email: Boolean,
$phone_number: String,
$dietitian_id: String,
$user_group_id: String,
$dont_send_welcome: Boolean ) {
createClient(input: { first_name: $first_name,
last_name: $last_name, email: $email,
skipped_email: $skipped_email,
phone_number: $phone_number,
dietitian_id: $dietitian_id,
user_group_id: $user_group_id,
dont_send_welcome: $dont_send_welcome }) {
user {
id
}
messages {
field
message
}
}
}
The createClient
mutation is called from an authenticated provider/staff account.
You can view a full list of potential inputs here. We will go over some of them below.
Input | Info |
---|---|
first_name |
Required |
last_name |
Required |
email |
Required if skipped_email is false |
skipped_email |
Optional |
phone_number |
Optional |
dietitian_id |
Required, ID of the provider. Defaults to the authenticated user's ID |
user_group_id |
Optional |
dont_send_welcome |
When true, an invite email will not be sent to the new client. |
Sending a welcome email with signup_token
After creating a client with the createClient
mutation, you may want to send your own welcome email to the client with the signup_token
to allow them to set a password and activate their account. You can obtain the signup_token
from the set_password_link
field on the User object, by querying the client account with the user
query.
Patient signUp
Mutation
mutation signUp($timezone: String, $first_name: String, $last_name: String, $email: String, $password: String, $phone_number: String, $invite_code: String, $provider_type: String, $role: String, $dietitian_id: String) {
signUp(
input: {
first_name: $first_name,
last_name: $last_name,
email: $email,
password: $password,
phone_number: $phone_number,
invite_code: $invite_code,
role: $role,
dietitian_id: $dietitian_id,
timezone: $timezone,
}
) {
user {
id
}
messages {
field
message
}
}
}
The signUp
mutation is called unauthenticated.
You can view a full list of potential inputs here. We will go over the inputs relevant for new patients below.
Input | Info |
---|---|
first_name |
Required |
last_name |
Required |
email |
Required |
password |
Required. Needs to be a minimum of 8 chars, and meet a calculated strength requirement. Healthie uses StrongPassword Ruby gem to calculate password strength |
role |
Required. Needs to be "patient" |
phone_number |
Optional |
dietitian_id |
ID of the provider. Either this or invite_code need to be provided. This can be provided along with a user group's invite code |
invite_code |
Invite code for a specific provider or user group. |
timezone |
Optional. Sets the timezone of the new patient's account |
completeCheckout
Mutation
The completeCheckout
mutation is used for a patient to book an appointment, or make a payment.
It creates a new patient if the following conditions are present
- The mutation is called unauthenticated
- The given email does not exist on another patient in the provider's account
We will go over this mutation in more depth in the Scheduling and Payments sections.
Retrieving a Patient
query getUser($id: ID) {
user(id: $id) {
id
first_name
last_name
dob
gender
email
phone_number
next_appt_date
}
}
Retrieving a specific patient's information is done via the user
query.
Input | Info |
---|---|
id |
Either this needs to be provided, or or_current_user needs to be true. |
clear_notifs |
When true, existing notifications from this patient will be marked as seen. |
or_current_user |
When true, the authenticated user will be returned if id is invalid or not present. |
Updating a Patient
Patient account information is updated in two main ways.
Method | Corresponding Mutation |
---|---|
Updated by a provider/staff member | updateClient |
Updated in bulk by a provider/staff member | bulkUpdateClients |
Updated by the patient themselves | updateUser |
updateClient
Mutation
mutation updateClient(
$id: ID,
$first_name: String,
$last_name: String,
$legal_name: String,
$email: String,
) {
updateClient(input:
{
id: $id,
first_name: $first_name,
last_name: $last_name,
legal_name: $legal_name,
email: $email,
}) {
user {
id
first_name
last_name
legal_name
email
}
messages {
field
message
}
}
}
The updateClient
mutation is called from an authenticated provider/staff account.
You can view a full list of potential inputs here.
We will go over some of the required and less clear ones below.
Input | Info |
---|---|
id |
Required |
resend_welcome |
When true, a new invite email will be sent to the patient. |
send_form_request_reminder |
If this is ture, is_intake_item or requested_form_completion_id must be sent in as well |
is_intake_item |
When sent with send_form_request_reminder , will send the patient an email reminding them to complete their intake forms. |
requested_form_completion_id |
When sent with send_form_request_reminder , will send the patient an email reminding them to complete a specific form request. |
for_changing_groups |
When true, the patient will be removed from their group. It is normally better to just send in a user_group_id of "" |
password |
When passed in, this will change the password the patient uses to sign in. This is helpful if you want to set passwords for your patients. If provided, needs to be a minimum of 8 chars, and meet a calculated strength requirement. Healthie uses StrongPassword Ruby gem to calculate password strength |
The inputs that trigger emails/reminders (resend_welcome
through requested_form_completion_id
) take precendece over other demographic updates you are trying to make to the patient record. For example, if you send in a new first_name
and also resend_welcome
, the invite email will be sent, but the patient's name will not be updated.
bulkUpdateClients
Mutation
mutation bulkUpdateClients($ids: [ID!], $active_status: Boolean) {
bulkUpdateClients(input: { ids: $ids, active_status: $active_status } ) {
users {
id
}
messages {
field
message
}
}
}
The bulkUpdateClients
mutation is called from an authenticated provider/staff account.
It allows you to update multiple patients at the same time.
You can view a full list of potential inputs here.
We will go over some of the common ones below.
Input | Info |
---|---|
ids |
Required |
user_group_id |
When present, all specified patients will be moved to the specified user group. |
active_status |
If user_group_id is not present, this field is required. All specified patients will have their status update. |
This mutation can only bulk update one field at a time. If user_group_id
and active_status
are both passed in, active_status
will be ignored.
Patient updateUser
Mutation
mutation updateUser(
$first_name: String
$legal_name: String
$last_name: String
$email: String
$id: ID
) {
updateUser(
input: {
first_name: $first_name
legal_name: $legal_name
last_name: $last_name
email: $email
id: $id
}
) {
user {
id
}
messages {
field
message
}
}
}
The updateUser
mutation is called from an authenticated patient account or (more rarely) from an authenticated provider/staff account with access to the patient.
You can view a full list of potential inputs here.
We will go over some of the required and less clear ones below.
Input | Info |
---|---|
current_email |
Email of the Patient to update. Normally better to pass id in instead |
id |
ID of the patient to update. If this is blank, the mutation will default to updating the authenticated user. |
Deleting a Patient
Patients (and User
objects in general) cannot be deleted via the Healthie API. Instead, you should archive the patient using the updateClient
mutation mentioned above.
List All Patients
query users(
$offset: Int,
$keywords: String,
$sort_by: String,
$active_status: String,
$group_id: String,
$show_all_by_default: Boolean,
$should_paginate: Boolean,
$provider_id: String,
$conversation_id: ID,
$limited_to_provider: Boolean,
) {
usersCount(
keywords: $keywords,
active_status:$active_status,
group_id: $group_id,
conversation_id: $conversation_id,
provider_id: $provider_id,
limited_to_provider: $limited_to_provider
)
users(
offset: $offset,
keywords: $keywords,
sort_by: $sort_by,
active_status: $active_status,
group_id: $group_id,
conversation_id: $conversation_id,
show_all_by_default: $show_all_by_default,
should_paginate: $should_paginate,
provider_id: $provider_id,
limited_to_provider: $limited_to_provider
) {
id
}
}
A list of patients can be retrieved via the users
query. This query is called from an authenticated provider/staff account.
You can view a full list of potential arguments here.
We will go over some of them below.
Input | Info |
---|---|
should_paginate |
Defaults to true. When false, the returned patient list is not paginated. |
keywords |
Optional. A term to search patients by. Find more about searching here. |
sort_by |
Defaults to "last_name_desc". View the query here to see all potential options. |
active_status |
Optional. Options are ["active", "archived"]. When passed in, only patients of the specified active status will be returned. |
group_id |
Optional. When passed in, only patients in the specified User Group will be returned |
show_all_by_default |
Defaults to true. When false, no patients will be returned unless keywords are also provided to the query. |
provider_id |
Defaults to the ID of the authenticated user. When passed in, only patients the specified user has access to will be returned. |
limited_to_provider |
Defaults to false. When true, only patients with the specified user as a provider will be returned. |
conversation_id |
Optional. When passed in, returns patients in the specified conversation. |
Patient Search
query users(
$keywords: String
) {
users(
keywords: $keywords
) {
id
}
}
Patients can be searched using the keywords
argument. The keywords
argument can be used to search by id
, first_name
, legal_name
, last_name
, record_identifier
, email
, dob
, and npi
.
Example variables:
{
"keywords": "John Doe 2002-01-01"
}
You can combine multiple search terms by separating them with a space. The search will return patients who match all the terms. In the provided example, the search will only return patients with the first and last names "John Doe" and a date of birth 2002-01-01.
Create Location
mutation createLocation($user_id: String, $name: String, $line1: String, $zip: String, $city: String, $state: String, $npi: String) {
createLocation(input: {user_id: $user_id, name: $name, line1: $line1, zip: $zip, city: $city, state: $state, npi: $npi }) {
location {
id
name
line1
zip
}
messages {
field
message
}
}
}
You can use the creatLocation
or updateLocation
mutations to create update address information for patients.
User Groups
The User Group Object
# Some Example Fields
"userGroup": {
"id": "9900",
"name": "Discovery Patients",
"users_count": "7",
"invite_code": "fbeb7f",
"onboarding_flow_id": 11
}
Patients can be organized into groups. Groups are a powerful feature in Healthie's API. You can trigger automations, automatically share content, restrict features, and more based on groups. You can read a full overview of the feature here.
Groups are UserGroup
objects.
You can view the full list of available fields here.
Creating a User Group
mutation createGroup($name: String ) {
createGroup(input: {name: $name }) {
user_group {
id
name
}
messages {
field
message
}
}
}
User Groups are created via the createGroup
mutation. The createGroup
mutation is called from an authenticated provider/staff account.
You can view a full list of potential inputs here.
We will go over them below.
Input | Info |
---|---|
name |
Required. The name of the group. |
Retrieving a User Group
query getUserGroup($id: ID) {
userGroup(id: $id) {
id
name
users_count
invite_code
}
}
Retrieving information on a a specific group is done via the userGroup
query.
Input | Info |
---|---|
id |
Required. |
Updating a User Group
mutation updateUserGroup($id: ID,
$name: String,
$onboarding_flow_id: ID) {
updateUserGroup(input: { id: $id,
name: $name,
onboarding_flow_id: $onboarding_flow_id}) {
user_group {
id
name
}
messages {
field
message
}
}
}
User Groups are updated via the updateUserGroup
mutation. The updateUserGroup
mutation is called from an authenticated provider/staff account.
You can view a full list of potential inputs here.
We will go over them below.
Input | Info |
---|---|
id |
Required. The ID of the group that will be updated |
name |
Optional. |
onboarding_flow_id |
Optional. The ID of the Onboarding Flow that patients in the group will be asked to fill out. |
Deleting a User Group
mutation deleteUserGroup($id: ID!,
$group_to_receive_clients: ID) {
deleteUserGroup(input: { id: $id,
group_to_receive_clients: $group_to_receive_clients }) {
user_group {
id
}
messages {
field
message
}
}
}
User Groups are deleted via the deleteUserGroup
mutation. The deleteUserGroup
mutation is called from an authenticated provider/staff account.
You can view a full list of potential inputs here.
We will go over them below.
Input | Info |
---|---|
id |
Required. The ID of the group to delete. |
group_to_receive_clients |
Optional. The ID of a group to transfer all patients to that are currently assigned to the deleted group. When left blank, patients are not placed into any group. |
List All User Groups
query userGroups($offset: Int, $keywords: String, $sort_by: String) {
userGroupsCount(keywords: $keywords)
userGroups(offset: $offset, keywords: $keywords, sort_by: $sort_by, should_paginate: true) {
id
name
users_count
}
}
A list of groups can be retrieved via the userGroups
query. This query is called from an authenticated provider/staff account.
You can view a full list of potential arguments here.
We will go over some of them below.
Input | Info |
---|---|
should_paginate |
Defaults to false. When true, the returned list of groups is paginated. |
keywords |
Optional. A term to search groups by. Groups can be searched by name . |
sort_by |
Defaults to "name". Options are ["name", "invite_code", "created_at_asc", "created_at_desc", "group_name_asc", "group_name_desc", "users_count_asc", "users_count_desc"] |
Tags
The User Tag Object
{
"id": 1,
"name": "Tag A",
"tagged_users": [
{
"id": 2,
"first_name": "John"
}
],
"user": {
"id": 1,
"email": "provider@example.com"
}
}
Client Tags offer an additional way (beyond Client Groups) to assign particular attributes to clients and automate specific actions based on Tags that have been established. While clients can only be assigned to one group, clients can have an unlimited number of tags.
Tags are Tag
objects.
Please note that tags are unique per provider/staff account.
Creating a Tag
mutation createTag($name: String, $taggable_user_id: ID) {
createTag(input: {
name: $name,
taggable_user_id: $taggable_user_id
}) {
tag {
id
name
tagged_users {
id
}
}
messages {
field
message
}
}
}
The createTag
mutation is called from an authenticated provider/staff account.
Creating a new Tag with with the same name as another one will fail. To apply an existing Tag to other Users please check out Applying Tags to Users.
You can view a full list of potential inputs here.
Input | Info |
---|---|
name |
Required. Tag name. |
taggable_user_id |
Optional. The ID of the User to apply a Tag on. |
Returns createTagPayload
.
Updating a Tag
mutation updateTag($id: ID, $name: String) {
updateTag(input: {
id: $id,
name: $name
}) {
tag {
id
name
}
messages {
field
message
}
}
}
Allows to change the Tag name.
You can view a full list of potential inputs here.
Input | Info |
---|---|
id |
Required. Tag ID. |
name |
Required. Tag name. |
Returns updateTagPayload
.
Deleting a Tag
mutation deleteTag($id: ID) {
deleteTag(input: {
id: $id
}) {
tag {
id
name
}
messages {
field
message
}
}
}
Use the deleteTag
mutation to permanently delete a Tag. It does not delete associated Users.
You can view a full list of potential inputs here.
Input | Info |
---|---|
id |
Required. The Tag ID. |
Returns deleteAppliedTagPayload
.
List all Tags
query tags {
tags {
id
name
}
}
Display a list of all Tags for the current User's Organization.
Returns an array of Tag
objects.
Applying Tags to Users
mutation applyTags($ids: [ID], $taggable_user_id: ID) {
bulkApply(input: {
ids: $ids,
taggable_user_id: $taggable_user_id
}) {
tags {
id
name
}
messages {
field
message
}
}
}
You can apply multiple existing Tags at once to a single User by using bulkApply
mutation.
The bulkApply
mutation is called from an authenticated provider/staff account.
You can view a full list of potential inputs here.
Input | Info |
---|---|
ids |
Required. List of Tag IDs to apply. |
taggable_user_id |
Required. The ID of the User to apply a Tag on. |
Returns bulkApplyPayload
.
Removing Applied Tags
mutation removeAppliedTag($id: ID, $taggable_user_id: ID) {
removeAppliedTag(input: {
id: $id,
taggable_user_id: $taggable_user_id
}) {
tag {
id
name
}
messages {
field
message
}
}
}
You can remove applied Tag from User by using removeAppliedTag
mutation.
You can view a full list of potential inputs here.
Input | Info |
---|---|
id |
Required. The Tag ID. |
taggable_user_id |
Required. The ID of the User to remove the Tag from. |
Returns removeAppliedTagPayload
.
Organization Management
The Organization Object
# Some Example Fields
"organization": {
"id": "9900",
"name": "Healthie",
"phone_number": "555-444-2244",
"tax_id": "123456789",
"tax_id_type": "EIN",
"npi": "1234567890",
"location": {
"city": "New York",
"country": "US"
}
}
Organizations are Organization
objects.
You can view the full list of available fields here.
Retrieving an Organization
query getOrganization($id: ID) {
organization(id: $id) {
id
name
location {
city
country
}
}
}
Input | Info |
---|---|
id |
Organization ID, if none provided, will retutrn the current user's Organization |
provider_id |
Find Organization by Provider ID |
provider_ids |
Find Organization by multiple Provider IDs |
email |
Email address of the User associated with the Organization |
for_client |
Use true to find the Organization for the currently logged-in end User (Patient) |
Adding an Organization Member
In order to create a new Organization Member, you should use the createOrganizationMembership
mutation.
mutation createOrganizationMembership(
$first_name: String,
$last_name: String,
$email: String,
$password: String,
$title: String,
$org_role: String,
$send_invite_email: Boolean,
$phone_number: String,
$dont_send_welcome: Boolean,
$organization_id: ID,
$timezone: String
) {
createOrganizationMembership(input: {
first_name: $first_name,
last_name: $last_name,
email: $email,
password: $password,
title: $title,
org_role: $org_role,
send_invite_email: $send_invite_email,
phone_number: $phone_number,
dont_send_welcome: $dont_send_welcome,
organization_id: $organization_id,
timezone: $timezone
}) {
organizationMembership {
user {
id
}
org_role
can_see_calendar
can_edit_forms
}
messages {
field
message
}
}
}
The createOrganizationMembership
mutation is called from an authenticated Super Admin account or by an Organization Member with can_add_members
permission.
You can view a full list of potential inputs here.
Input | Info |
---|---|
organization_id |
Required. Specifies to which organization to invite the user. |
first_name |
Required. |
last_name |
Required. |
email |
Required. |
title |
Optional. Used for additional metadata about the membership. Not used in Healthie UI. |
org_role |
Optional. Either "Standard" (default) or "Support" . |
password |
Optional. Sets the default password for the invited user. If provided, needs to be a minimum of 8 chars, and meet a calculated strength requirement. Healthie uses StrongPassword Ruby gem to calculate password strength |
send_invite_email |
Optional. Whether to send an invite email. |
The createOrganizationMembership
mutation returns an OrganizationMembership
object, which describes a list of the new member's permissions. You can view a full list of potential return properties here.
Listing Providers / Organization Members
You can retrieve a list of your organization's providers and support members using the organizationMembers
query.
You can view a full list of potential inputs in our API Explorer.
query getOrganizationMembers(
$offset: Int,
$page_size: Int,
$keywords: String,
$sort_by: String,
$licensed_in_state: String,
$conversation_id: ID
) {
organizationMembers(
offset: $offset,
page_size: $page_size,
keywords: $keywords,
sort_by: $sort_by,
licensed_in_state: $licensed_in_state,
conversation_id: $conversation_id
) {
id
first_name
last_name
email
has_api_access
}
}
Input | Info |
---|---|
sort_by |
Default value is last_name . Other options are email , first_name_asc , first_name_desc , last_name_asc , last_name_desc , created_at_desc , created_at_asc , updated_at_desc , updated_at_asc , group_name_asc , group_name_desc , provider_name_asc , provider_name_desc , next_appt_asc , and next_appt_desc . |
licensed_in_state |
Two letter state abbreviation (e.g. "CA", "NY") |
conversation_id |
Can be used to filter org members by a Conversation ID |
offset |
Optional. Offset for pagination |
page_size |
Defaults to 10. The number of records to return |
Updating an Organization Membership
mutation updateOrganizationMembership(
$first_name: String,
$last_name: String,
$email: String,
$password: String,
$title: String,
$org_role: String,
$send_invite_email: Boolean,
$phone_number: String,
$dont_send_welcome: Boolean,
$organization_id: ID,
$timezone: String
) {
updateOrganizationMembership(input: {
first_name: $first_name,
last_name: $last_name,
email: $email,
password: $password,
title: $title,
org_role: $org_role,
send_invite_email: $send_invite_email,
phone_number: $phone_number,
dont_send_welcome: $dont_send_welcome,
organization_id: $organization_id,
timezone: $timezone
}) {
organizationMembership {
user {
id
}
org_role
can_see_calendar
can_edit_forms
}
messages {
field
message
}
}
}
The updateOrganizationMembership
mutation shares many common inputs with createOrganizationMembership
and those inputs (e.g org_role
or title
work the same in both places).
However, the updateOrganizationMembership
provides more granular control over the membership object.
The updateOrganizationMembership
mutation can be called by Super Admins or Organization Members with can_edit_members
permission.
You can view a full list of potential inputs here.
Input | Info |
---|---|
id |
Required. ID of the Organization Membership to update |
active |
Optional. Boolean indicating whether the Organization Member should be active |
timezone |
Optional. Allows to update the timezone of the Organization Member |
qualifications |
Optional. Updates the qualifications of the Organization Member |
Additionally, the updateOrganizationMembership
mutation accepts a set of Boolean input parameters that determine permissions of the Member.
Returns an updateOrganizationMembershipPayload
object.
Removing an Organization Member
mutation deleteOrganizationMembership($id: ID) {
deleteOrganizationMembership(input: { id: $id }) {
organizationMembership {
id
}
messages {
field
message
}
}
}
Organization Memberships can be deleted via the deleteOrganizationMembership
mutation.
You can view a full list of potential inputs here.
The deleteOrganizationMembership
mutation can be called by Super Admins only.
Input | Info |
---|---|
id |
Required. ID of the Membership to delete. |
Returns a deleteOrganizationMembershipPayload
object.
Forms
Healthie supports two types of forms:
For more information on the feature in general, view our help articles here.
Form Templates
Form Templates are considered public to view. All mutations can be performed only by an authenticated provider/staff account.
The Form Template Object
{
"id": "1",
"name": "Health History",
"has_matrix_field": true,
"prefill": false,
"is_video": false,
"use_for_charting": false,
"use_for_program": false,
"has_non_readonly_modules": true,
"custom_modules": [
// An array of CustomModule objects
]
}
Forms are CustomModuleForm
objects.
You can view the full list of available fields here.
Listing all Forms
query formTemplates(
$include_default_templates: Boolean
$active_status: Boolean
$should_paginate: Boolean
$category: String
$keywords: String
$offset: Int
$sortBy: String
) {
customModuleForms(
include_default_templates: $include_default_templates
active_status: $active_status
should_paginate: $should_paginate
category: $category
keywords: $keywords
offset: $offset
sort_by: $sortBy
) {
id
is_video
name
prefill
uploaded_by_healthie_team
}
}
A list of Form Templates can be retrieved via the customModuleForms
query. This query is considered public.
You can view a full list of potential arguments here.
We will go over some of them below.
Input | Info |
---|---|
category |
Optional. Valid options are:
|
active_status |
Optional. Whether to fetch archived forms, false by default. |
keywords |
Optional. A term to search Forms by. Can be searched by form name. |
sort_by |
Optional. Valid options are:
|
Returns an array of CustomModuleForm
objects.
Retrieving a Form
query form($id: ID) {
customModuleForm(id: $id) {
id
has_matrix_field
has_non_readonly_modules
is_video
name
prefill
use_for_charting
use_for_program
custom_modules {
id
hipaa_name
mod_type
options
# ...
}
}
}
Retrieving a specific Form Template is done via the customModuleForm
query. This query is considered public.
The customModuleForm
query accepts the following arguments:
Input | Info |
---|---|
id |
Required. The ID of the Form Template |
Returns a CustomModuleForm
object.
Creating a Form
mutation createCustomModuleForm(
$name: String,
$use_for_charting: Boolean,
$use_for_program: Boolean
) {
createCustomModuleForm(input: {
name: $name,
use_for_charting: $use_for_charting,
use_for_program: $use_for_program,
}) {
customModuleForm {
id
}
messages {
field
message
}
}
}
The createCustomModuleForm
mutation is called from an authenticated provider/staff account.
You can view a full list of potential inputs here.
Input | Info |
---|---|
name |
Required. Form name. |
use_for_charting |
Optional. Set to true to use the Form for charting |
use_for_program |
Optional. Set to true to use the Form for a program |
Returns createCustomModuleFormPayload
.
Updating a Form
mutation updateCustomModuleForm(
$id: ID,
$name: String,
$is_archived: Boolean
) {
updateCustomModuleForm(input: {
id: $id,
name: $name,
is_archived: $is_archived
}) {
customModuleForm {
id
}
messages {
field
message
}
}
}
The updateCustomModuleForm
mutation is called from an authenticated provider/staff account.
You can view a full list of potential inputs here.
Input | Info |
---|---|
id |
Required. The ID of the Form to update. |
name |
Optional. New name for the Form. |
is_archived |
Optional. Set to true to archive the Form. |
use_for_charting |
Optional. Set to true to use the Form for charting |
use_for_program |
Optional. Set to true to use the Form for a program |
Returns updateCustomModuleFormPayload
.
Deleting a Form
mutation deleteCustomModuleForm(
$id: ID
) {
deleteCustomModuleForm(input: {
id: $id
}) {
customModuleForm {
id
}
messages {
field
message
}
}
}
The deleteCustomModuleForm
mutation is called from an authenticated provider/staff account.
You can view a full list of potential inputs here.
Input | Info |
---|---|
id |
Required. The ID of the Form to delete. |
Returns updateCustomModuleFormPayload
.
Copying a Form
mutation copyCustomModuleForm(
$id: ID,
$id_list: String,
$user_id: String
) {
copyCustomModuleForm(input: {
id: $id,
id_list: $id_list,
user_id: $user_id
}) {
customModuleForm {
id
}
messages {
field
message
}
}
}
You can clone a Form Template by using the copyCustomModuleForm
mutation.
The full input definition can be found here.
Input | Info |
---|---|
id |
The ID of the Form to copy. Either this or id_list is required. |
id_list |
A comma-separated string with IDs of the Forms to copy. Required if id is not present.Example: "1,2,3" . |
user_id |
The ID of the owner of newly created Forms. Available only to super admins, otherwise will copy to the current user's forms. |
Returns copyCustomModuleFormPayload
.
Sharing a Form
mutation shareCustomModuleForm(
$id: ID,
$form_share_recipient: String
) {
shareCustomModuleForm(input: {
id: $id,
form_share_recipient: $form_share_recipient
}) {
customModuleForm {
id
}
messages {
field
message
}
}
}
You can share a Form with another Healthie member. The recipient needs to be specified with an email address. They will receive their own independent copy of the form.
This mutation will trigger a notification email to the recipient.
You can clone a Form Template by using the shareCustomModuleForm
mutation.
The full input definition can be found here.
Input | Info |
---|---|
id |
Required. The ID of the Form to share. |
form_share_recipient |
Required. Email address of the recipient. Must be a valid email address of an existing Healthie member. |
Returns shareCustomModuleFormPayload
.
Onboarding Flows
The Onboarding Flow Object
{
"id": "1",
"name": "Intake flow",
"has_forms_after_welcome": true,
"is_multiple_providers": true,
"onboarding_items": [
// an array of OnboardingItem documents
]
}
Onboarding Flows are OnboardingFlow
objects. Onboarding Flows can be associated with zero or more User Groups.
Onboarding Flows consist of OnboardingItem
documents.
You can view the full list of available fields here.
Listing Onboarding Flows
query onboardingFlows(
$offset: Int,
$keywords: String,
$sort_by: String,
$should_paginate: Boolean
) {
onboardingFlows(
offset: $offset,
keywords: $keywords,
sort_by: $sort_by,
should_paginate: $should_paginate
) {
id
name
is_multiple_providers
has_forms_after_welcome
}
}
A list of Onboarding Flows can be retrieved via the onboardingFlows
query. This query is available to authenticated provider/staff accounts.
You can view a full list of potential arguments here.
We will go over some of them below.
Input | Info |
---|---|
keywords |
Optional. A term to search by. Can be searched by the flow name. |
sort_by |
Optional. Valid options are:
|
Returns an array of OnboardingFlow
objects.
Retrieving a Flow
query onboardingFlow(
$id: ID,
$user_id: ID
) {
onboardingFlow(
id: $id,
user_id: $user_id
) {
id
name
is_multiple_providers
has_forms_after_welcome
}
}
Retrieving a specific Onboarding Flow is done via the onboardingFlow
query. This can be done either via the ID of the Flow or for a specific Patient.
You can view a full list of all potential arguments here.
Input | Info |
---|---|
id |
ID of the Flow. Either this or user_id needs to be provided. |
user_id |
Patient's ID. This argument is ignored if querying as a non-staff/provider account. |
Returns an OnboardingFlow
object.
Creating a Flow
mutation createOnboardingFlow(
$name: String,
$groups_to_use_onboarding_flow: String
) {
createOnboardingFlow(input: {
name: $name,
groups_to_use_onboarding_flow: $groups_to_use_onboarding_flow
}) {
onboardingFlow {
id
}
messages {
field
message
}
}
}
The createOnboardingFlow
mutation is called from an authenticated provider/staff account.
You can view a full list of potential inputs here.
Input | Info |
---|---|
name |
Required. Onboarding Flow name. |
groups_to_use_onboarding_flow |
Optional. A comma-separated list of User Groups to attach the Onboarding Flow to. Example: "1,2,3" . |
Returns createOnboardingFlowPayload
.
Updating a Flow
mutation updateOnboardingFlow(
$id: ID,
$name: String,
$groups_to_use_onboarding_flow: String
) {
updateOnboardingFlow(input: {
id: $id,
name: $name,
groups_to_use_onboarding_flow: $groups_to_use_onboarding_flow
}) {
onboardingFlow {
id
}
messages {
field
message
}
}
}
Onboarding Flows are deleted via the updateOnboardingFlow
mutation. The updateOnboardingFlow
mutation is called from an authenticated provider/staff account.
You can view a full list of potential inputs here.
Input | Info |
---|---|
id |
Required. The ID of the Onboarding Flow to update. |
name |
Optional. A new name for the Onboarding Flow. |
groups_to_use_onboarding_flow |
Optional. A comma-separated list of User Groups to attach the Onboarding Flow to. Example: "1,2,3" . |
Returns updateOnboardingFlowPayload
.
Deleting a Flow
mutation deleteOnboardingFlow(
$id: ID,
$flow_to_receive_groups: ID
) {
deleteOnboardingFlow(input: {
id: $id,
name: $name,
flow_to_receive_groups: $flow_to_receive_groups
}) {
onboardingFlow {
id
}
messages {
field
message
}
}
}
Onboarding Flows are deleted via the deleteOnboardingFlow
mutation. The deleteOnboardingFlow
mutation is called from an authenticated provider/staff account.
When deleting an Onboarding Flow, you can automatically reassign the User Groups to another Flow by using the flow_to_receive_groups
argument.
You can view a full list of potential inputs here.
Input | Info |
---|---|
id |
Required. The ID of the Onboarding Flow to delete. |
flow_to_receive_groups |
Optional. Provide the ID of a Flow that should inherit the User Groups. |
Returns deleteOnboardingFlowPayload
.
Onboarding Items
Onboarding Items are used to associate Forms with Intake Flows.
The Onboarding Item Object
{
"id": "1980",
"item_type": "Insurance Form",
"display_name": "Insurance Form",
"is_skippable": true,
"onboarding_flow": {
"id": "170"
}
}
Onboarding Items are OnboardingItem
objects.
You can view the full list of available fields here.
Retrieving an Onboarding Item
query getOnboardingItem($id: ID) {
onboardingItem(id: $id) {
id
item_type
display_name
is_skippable
onboarding_flow {
id
}
}
}
Retrieving a specific Onboarding Item is done via the onboardingItem
query. This query is considered public.
Input | Info |
---|---|
id |
ID of the Onboarding Item to retrieve. |
Returns an OnboardingItem
object.
Creating an Onboarding Item
mutation createOnboardingItem(
$onboarding_flow_id: String,
$item_type: String,
$is_skippable: Boolean
) {
createOnboardingItem(input: {
onboarding_flow_id: $onboarding_flow_id,
item_type: $item_type,
is_skippable: $is_skippable
}) {
onboardingItem {
id
}
messages {
field
message
}
}
}
Onboarding Items are created via the createOnboardingItem
mutation. This mutation is called from an authenticated account.
You can view a full list of potential inputs here.
Input | Info |
---|---|
onboarding_flow_id |
Required. ID of the Onboarding Flow to attach the Item to |
item_type |
Required. A stringified JSON array containing object(s) of the following structure.
Example: {"type":"Module Form","id":"1"} |
is_skippable |
Required. Set to false if this Onboarding Item is mandatory |
Returns a createOnboardingItemPayload
object.
Updating an Onboarding Item
mutation updateOnboardingItem(
$id: ID,
$onboarding_flow_id: String,
$item_type: String,
$item_id: String,
$is_skippable: Boolean,
$row_order: Int
) {
updateOnboardingItem(input: {
id: $id,
onboarding_flow_id: $onboarding_flow_id,
item_type: $item_type,
item_id: $item_id,
is_skippable: $is_skippable,
row_order: $row_order
}) {
onboardingItem {
id
}
messages {
field
message
}
}
}
Onboarding Items are updated via the updateOnboardingItem
mutation. This mutation is called from an authenticated account.
You can view a full list of potential inputs here.
Input | Info |
---|---|
id |
Required. The ID of the Onboarding Item to update |
item_type |
Optional. Type of the Item. Please check Listing Available Item Types to get possible values |
item_id |
Optional. ID of the related Item |
row_order |
Optional. Use this to reorder the Item inside of the Onboarding Flow |
Returns an updateOnboardingItemPayload
object.
Deleting an Onboarding Item
Onboarding Items can be deleted via the deleteOnboardingItem
mutation.
You can view a full list of potential inputs here.
mutation deleteOnboardingItem($id: ID) {
deleteOnboardingItem(input: { id: $id }) {
onboardingItem {
id
}
messages {
field
message
}
}
}
The deleteOnboardingItem
mutation is called from an authenticated provider/staff account.
Input | Info |
---|---|
id |
Required. ID of the Onboarding Item to delete. |
Returns a deleteOnboardingItemPayload
object.
Listing Available Item Types
query getAvailableItemTypes(
$onboarding_flow_id: String
) {
availableItemTypes(
onboarding_flow_id: $onboarding_flow_id
)
}
Input | Info |
---|---|
onboarding_flow_id |
Optional. ID of the Onboarding Flow to return available types for |
This query returns a serialized JSON string containing a list of available types. Each type object has an id
and name
field, for example: {"id":"1","name":"Financial Agreement"}
.
Listing Unassociated Completed Onboarding Items
query getUnassociatedCompletedOnboardingItems(
$user_id: String
) {
unassociatedCompletedOnboardingItems(
user_id: $user_id
) {
id
item_type
item_id
onboarding_item_id
skipped
user {
id
}
}
}
The unassociatedCompletedOnboardingItems
query returns a list of user completed Onboarding Items, that are not part of the User's current flow.
Input | Info |
---|---|
user_id |
Optional. ID of the User to get the Items for |
Returns a CompletedOnboardingItem
object.
Creating a Completed Onboarding Item
mutation createCompletedOnboardingItem(
$onboarding_item_id: ID,
$user_id: ID,
$skipped: Boolean
) {
createCompletedOnboardingItem(input: {
onboarding_item_id: $onboarding_item_id,
user_id: $user_id,
skipped: $skipped
}) {
completed_onboarding_item {
id
}
}
}
To mark an Onboarding Item completed by a User, use the createCompletedOnboardingItem
mutation.
You can view a full list of potential inputs here.
Input | Info |
---|---|
onboarding_item_id |
Required. ID of the Onboarding Item to mark as complete |
skipped |
Required. If set to true , will mark this Onboarding Item as skipped |
user_id |
Optional. If not provided, will apply to the current User |
Returns a createCompletedOnboardingItemPayload
object.
Form Completion Requests
Form Completion Request Object
{
"id": 1,
"custom_module_form": {
"id": 1
},
"item_type": "cmf",
"recipient": {
"id": 200,
"email": "patient@example.com"
},
"form_answer_group": {
// FormAnswerGroup object
},
"sender": {
"id": 1
},
"status": "Open"
}
When you want to send Patient a Form to complete, you create a Form Completion Request.
Within the API, Form Completion Requests are known as RequestedFormCompletion
objects.
Listing all Form Completion Requests
query requestedFormCompletions(
$userId: ID,
$keywords: String,
$status: String
) {
requestedFormCompletions(
user_id: $userId,
keywords: $keywords,
status: $status
) {
id
item_type
}
}
A list of Form Completion Requests can be retrieved via the requestedFormCompletions
query. This query is available to authenticated provider/staff accounts.
You can view a full list of potential arguments here.
We will go over some of them below.
Input | Info |
---|---|
user_id |
Optional. ID of the Form recipient. |
status |
Optional. Can be either Open or Incomplete . |
keywords |
Optional. A term to search Requests by. Can be searched by Form Template name. |
Returns an array of RequestedFormCompletion
objects.
Creating a Form Completion Request
mutation createRequestedFormCompletion(
$recipient_ids: String,
$form: String,
$is_recurring: Boolean,
$frequency: String,
$period: String,
$minute: String,
$hour: String,
$weekday: String,
$monthday: String,
$recurrence_ends: Boolean,
$ends_on: String
) {
createRequestedFormCompletion(input: {
recipient_ids: $recipient_ids,
form: $form,
is_recurring: $is_recurring,
frequency: $frequency,
period: $period,
minute: $minute,
hour: $hour,
weekday: $weekday,
monthday: $monthday,
recurrence_ends: $recurrence_ends,
ends_on: $ends_on
}) {
requestedFormCompletion {
id
}
messages {
field
message
}
}
}
The createRequestedFormCompletion
mutation is called from an authenticated provider/staff account.
You can view a full list of potential inputs here.
Input | Info |
---|---|
recipient_ids |
Required. Array of User IDs that should receive the Form Completion Request. |
form |
Required. ID of the Form. |
is_recurring |
Optional. Set to true if the Form completion should be recurring. |
frequency |
Required if is_recurring is set to true . Valid options are:
|
period |
AM or PM . |
minute |
For instance, if you want to trigger the completion request at 1:05 PM, use "5" . |
hour |
For instance, if you want to trigger the completion request at 1:05 PM, use "1" . |
weekday |
Use the full weekday name, e.g. "Monday" . |
monthday |
Number of the day of month, e.g. "27th" . |
recurrence_ends |
Set to true if the recurrence should have an end date. |
ends_on |
Recurrence end date in the YYYY-MM-DD format. |
Returns createRequestedFormPayload
.
Deleting a Form Completion Request
mutation deleteRequestedFormCompletion(
$id: ID,
$recurringFormId: ID
) {
deleteRequestedFormCompletion(input: {
id: $id,
recurringFormId: $recurringFormId
}) {
requestedFormCompletion {
id
}
messages {
field
message
}
}
}
The deleteRequestedFormCompletion
mutation is called from an authenticated provider/staff account.
You can view a full list of potential inputs here.
Input | Info |
---|---|
id |
Required. The ID of the Form Completion Request to delete. |
recurringFormId |
Optional. Provide if you also want to delete the recurring form (if exists). |
Returns deleteRequestedFormPayload
.
Creating a Filled Out Form (e.g a chart note, or intake form)
mutation createFormAnswerGroup(
# Should almost always true
$finished: Boolean,
# ID of the custom_module_form (e.g "100")
$custom_module_form_id: String,
# ID of the patient (e.g "61")
$user_id: String,
# e.g [{custom_module_id: "1", answer: "foo", user_id: "61"}, {custom_module_id: "2", answer: "bar", user_id: "61"}]
$form_answers: [FormAnswerInput!]!
) {
createFormAnswerGroup(
input: {
finished: $finished,
custom_module_form_id: $custom_module_form_id,
user_id: $user_id,
form_answers: $form_answers,
}
) {
form_answer_group {
id
}
messages {
field
message
}
}
}
In the Healthie API, a filled-out form is known as a FormAnswerGroup
.
The most important fields when creating a FormAnswerGroup
are user_id
, custom_module_form_id
, finished
, form_answers
. Let's go over each one.
user_id
is the ID of the patient who the form is on. When you create a FormAnswerGroup
for a patient, that will be visible on the patient's chart.
custom_module_form_id
is the ID of the template (known as a CustomModuleForm
) that the FormAnswerGroup
completes. All filled-out forms need to be backed by a CustomModuleForm
. These are the templates that you can create, view, and edit in the Form Builder in Healthie.
finished
is a Boolean value. This needs to be passed in as true for the form to be visible in the patient's chart and when you query for formAnswerGroups
. If you do not pass in finished
(or pass it in as false), you will need to query the formAnswerGroup
by its ID.
form_answers
is an array containing all the individual answers. Each submitted form answer needs to have a custom_module_id
, user_id
, and an answer
. The user_id
should match the user_id
of the FormAnswerGroup
. The custom_module_id
should be the ID of the question (known as a CustomModule
) that is being answered. The answer
(as the name suggests) is the answer to the question.
Those fields are used in a createFormAnswerGroup
mutation to create the filled-out form. Please see the right sidebar for an example.
This mutation triggers a form_answer_group.created
webhook event.
Querying Filled Out Forms
query formAnswerGroups(
$date: String, # e.g "2021-10-29"
$custom_module_form_id: ID, # e.g "11"
) {
formAnswerGroups(
date: $date,
custom_module_form_id: $custom_module_form_id,
) {
id
name
created_at
form_answers {
label
displayed_answer
id
custom_module {
required
id
mod_type
label
}
}
}
}
To retrieve filled out forms, you can use the formAnswerGroups
query.
You can view all the argument options here but lets go over some of the most important ones below.
filler_id
is the ID of the user who filled out the form. For example, you can use this argument to see all forms filled out by a given provider.
user_id
is the ID of the patient whose chart the filled out form is in.
custom_module_form_id
is the ID of the form template that was filled out. This can be helpful to use to see all filled out answers for a specific template (e.g you want to see all filled out "Health History" forms)
date
refers to the date the form was filled out (or the date of service in the case of chart notes).
On the right, you can find an example of a simple query that returns filled out form data for a given form template and day.
Chat
For more information on the feature in general, view our help articles here.
Chat Objects
# Some example fields for a conversation.
{
"conversation": {
"id": "9900",
"conversation_memberships_count": "2",
"name": "Quick Question",
"includes_multiple_clients": false,
"owner": {
"id": "11",
"full_name": "Jeff Smith"
},
"notes": [
{
"user_id": "123451",
"content": "<p>Hey</p>"
},
{
"user_id": "123451",
"content": "<p>Example message</p>"
}
]
}
}
Within the API, a "Chat" is known as a Conversation
. A message in a conversation is a Note
object.
Conversation Memberships
# Example ConversationMembership
{
"conversationMembership": {
"convo": {
"id": "12345",
"viewed": false, # has unread messages
"notes": [
{
"user_id": "123451",
"content": "<p>Hey</p>"
},
{
"user_id": "123451",
"content": "<p>Example message</p>"
}
]
}
}
}
A conversation has multiple ConversationMemberships
objects which are connectors between a Conversation
and a User
.
To determine a given conversation has unread messages for the current user, check the viewed
field on the ConversationMembership
object.
Note content formatting
The content
field on a Note
object is a string of HTML. Healthie allows only subset of HTML elements and attributes. The rest will be stripped out. Basic formatting such as bold, italics, and underline, as well as images and video embeds are supported. New lines can be supported either by wrapping the content in separate <p>
tags, or by using \n
escape characters.
Creating a Conversation
A Conversation
is created in two main ways -- automatically and via createConversation
.
A Conversation
is automatically created when a patient is assigned to a provider (either as their primary provider or part of their care team) and the 'Has Chat conversation automatically created with client' permission is enabled. When this happens, our system automatically creates a two-person conversation between the patient and the provider (if one does not exist already). Note that 'Has Chat conversation automatically created with client' only applies to Care Team Providers and not the patient's Primary provider, a two-person conversation between the patient and their primary provider is always created.
A Conversation
can also be manually created with createConversation
.
Method | Corresponding Mutation |
---|---|
Automatically Created | N/A |
Manually Created | createConversation |
Users have no limits on the number of conversations they can be in, and the same patient and provider can have multiple conversations together. Healthie also supports internal conversations, which are conversations that just have providers and staff, with no patients involved.
createConversation
Mutation
mutation createConversation(
$simple_added_users: String # e.g "user-1,group-2,user-3"
$owner_id: ID # e.g "4"
$name: String # e.g "Questions for Next Appointment"
) {
createConversation(
input: {
simple_added_users: $simple_added_users
owner_id: $owner_id
name: $name
}
) {
conversation {
id
}
messages {
field
message
}
}
}
Conversations are manually created via the createConversation
mutation, which needs to be called from an authenticated staff/provider account.
You can view a full list of potential inputs here. We will go over some of the most common inputs below.
Input | Info |
---|---|
owner_id |
The staff member who is the "Owner" of the conversation. The owner is automatically added to the conversation, and has full ability to add and remove patients and staff from the conversation. If left blank, this defaults to the current user. |
simple_added_users |
A comma-seperated string of which patients, staff, and user groups should be added to the conversation. This comma seperated string uses the doc_share_id (which is a field on UserType and UserGroupType ). For example, it could look like user-1,group-2,user-3 . |
name |
The subject title for the conversation. Defaults to the names of the users and groups in the conversation. |
createNote mutation
A chat message in the Healthie API is referred to as a "note." To add a message to a chat conversation, use the createNote
mutation.
mutation createNote(
$user_id: String
$content: String
$conversation_id: String
$attached_image_string: String
$scheduled_at: String
$org_chat: Boolean
$hide_org_chat_confirmation: Boolean
) {
createNote(
input: {
user_id: $user_id
content: $content # Content of the note
conversation_id: $conversation_id
attached_image_string: $attached_image_string
scheduled_at: $scheduled_at # for scheduling notes, time note will be sent
org_chat: $org_chat # Pass `true` when creating a note by someone who is not the conversation owner (e.g., by another provider on the client's care team)
hide_org_chat_confirmation: $hide_org_chat_confirmation # When True, will hide org chat confirmation modal
}
) {
note {
id
content
user_id
}
messages {
field
message
}
}
}
Retrieving a Conversation
query getConversation($id: ID, $provider_id: ID) {
conversation(id: $id, provider_id: $provider_id) {
id
conversation_memberships_count
includes_multiple_clients
}
}
Retrieving a specific conversation is done via the conversation
query.
Input | Info |
---|---|
id |
Required. The ID of the conversation. |
Updating a Conversation
A Conversation
is updated via updateConversation
. This can be called from an authenticated provider or staff account.
Method | Corresponding Mutation |
---|---|
Updated by provider or staff | updateConversation |
Users have no limits on the number of conversations they can be in, and the same patient and provider can have multiple conversations together. Healthie also supports internal conversations, which are conversations that just have providers and staff, with no patients involved.
updateConversation
Mutation
mutation updateConversation(
$id: ID,
$simple_added_users: String,
$name: String,
$closed_by_id: ID,
) {
updateConversation(
input: {
id: $id,
simple_added_users: $simple_added_users,
name: $name,
closed_by_id: $closed_by_id,
}
) {
conversation {
id
}
messages {
field
message
}
}
}
Conversations are updated via the updateConversation
mutation, which needs to be called from an authenticated staff/provider account.
You can view a full list of potential inputs here. We will go over some of the most common inputs below.
Input | Info |
---|---|
id |
Required. The ID of the conversation to update |
simple_added_users |
A comma-seperated string of which patients, staff, and user groups should be in the conversation. See createConversation section for how to format. Any existing conversation members not included in this string will be removed from the conversation. |
name |
Optional. The subject title for the conversation. Defaults to the names of the users and groups in the conversation. |
closed_by_id |
When passed in, the conversation is closed. This prevents new messages from being sent. This ID should be the ID of the current user. |
Deleting a Conversation
Conversation
objects cannot be deleted via the Healthie API. Instead, you should close the conversation using the updateConversation
mutation mentioned above.
Listing Conversations
query conversationMemberships(
$offset: Int
$keywords: String
$active_status: String
$client_id: String
$read_status: String
$conversation_type: String
$provider_id: ID
$is_scheduled_tab: Boolean!
) {
conversationMembershipsCount(
keywords: $keywords
active_status: $active_status
client_id: $client_id
read_status: $read_status
conversation_type: $conversation_type
provider_id: $provider_id
)
conversationMemberships(
offset: $offset
keywords: $keywords
active_status: $active_status
client_id: $client_id
read_status: $read_status
conversation_type: $conversation_type
provider_id: $provider_id
) {
id
display_name
archived
viewed
convo {
id
conversation_memberships_count
}
}
}
To retrieve a list of conversations, it is best to use the conversationMemberships
query, not the conversations
query. The conversationMemberships
gives you additional argument options and context.
You can view a full list of potential arguments here.
We will go over some of them below.
Input | Info |
---|---|
client_id |
Optional. Retrieve conversationMemberships for a specific patient. To retrieve conversationMemberships that the authenticated user is not a part of, you must be authenticated as an organization admin. |
provider_id |
Optional. Defaults to the authenticated user if left blank. Retrieve conversationMemberships for a specific provider. You must be authenticated as an organization admin. |
keywords |
Optional. Allows for searching the conversation by the conversation name, or by the names of users in the conversation. |
read_status |
Defaults to all . Options are all , read , and unread . Returns conversationMemberships matching the read/unread status. |
active_status |
Defaults to active . Options are active , archived , closed . Retrieves conversationMemberships with the matching conversation status. |
conversation_type |
Defaults to all . Options are all , individual , community . Retrieves conversationMemberships with the matching conversation type. |
Chat SDK for React
The Healthie React SDK offers an easy integration of Healthie's Chat functionality into your application by providing pre-built React components and hooks. With minimal setup and configuration, these components can be rendered as is or restyled to match your application's design by overriding CSS classess.
The SDK should be sufficient in most use cases, providing the following capabilities:
- Conversation list
- Conversation view
- Individual (1:1) conversations
- Community Chat conversations
- Rendering all message types (text, image, audio, file attachments, video embeds)
It is however not possible to create conversations (outside of the Healthie web app or mobile app), and some features that exist in the Healthie web or mobile app may not be available via the SDK.
For more information and usage guide, refer to the SDK documentation here.
Video chat
Appointments created in Healthie can optionally include video chat. If you're building your own patient-facing app, you can embed Healthie video calls into your custom frontend.
Healthie video calls use either Zoom or OpenTok. The video calls that take place in the Healthie portal UI use the OpenTok API under the hood. When creating an appointment in Healthie that includes video chat, providers can choose either Zoom or Healthie video call.
Below is information about embedding a video chat experience into your own frontend app. Visit our help center for more general info about using video calls in Healthie.
Using Zoom
To embed video calls that use Zoom, you'll need to use a Zoom SDK. Zoom offers a web SDK, as well as a React Native SDK for mobile apps written in React Native.
The Appointment object from the Healthie API includes the participant zoom_join_url
as well as other relevant Zoom call info.
Using OpenTok
To embed a Healthie video call into your frontend application, you can use the opentok-react package. You'll need the generated_token
and session_id
that you can retrieve from an Appointment object via the Healthie API. You'll also need Healthie's public Vonage API key, which is 45624682
.
Group calls
Please note that group video calls can only be conducted using the Zoom integration, not with Healthie video calls / OpenTok.
Appointment Types
The Appointment Type Object
// Some Example Fields for an Appointment Type.
{
"id": "1",
"name": "Initial Consultation",
"is_group": false,
"bookable_by_groups": false,
"bookable_without_group": true,
"length": 60, # length in minutes
"available_contact_types": [
"Healthie Video Call",
"In Person",
"Phone Call"
],
"is_waitlist_enabled": false
}
Appointments Types are AppointmentType
objects.
AppointmentType
objects are used for 1:1 appointments, group appointments, and blocks to availability.
You can view the full list of available fields here.
If you have questions about the overall functionality of appointment's feature, you can find information here.
Retrieving an Appointment Type
query getAppointmentType(
$id: ID
) {
appointmentType(id: $id) {
id
name
available_contact_types
length
}
}
Retrieving a specific Appointment Type is done via the appointmentType
query.
Returns an AppointmentType
object.
Creating an Appointment Type
mutation createAppointmentType(
$name: String,
$length: Int,
$clients_can_book: Boolean,
$is_group: Boolean,
$bookable_without_group: Boolean,
$is_waitlist_enabled: Boolean,
$user_group_id: String,
$specific_groups: Boolean,
$bookable_group_ids: String,
$contact_type_overrides: [String],
$appointment_setting: AppointmentTypeAppointmentSettingInput
) {
createAppointmentType(
input: {
name: $name,
length: $length,
clients_can_book: $clients_can_book,
is_group: $is_group,
bookable_without_group: $bookable_without_group,
is_waitlist_enabled: $is_waitlist_enabled,
user_group_id: $user_group_id,
specific_groups: $specific_groups,
bookable_group_ids: $bookable_group_ids,
contact_type_overrides: $contact_type_overrides,
appointment_setting: $appointment_setting
}
) {
appointmentType {
id
}
messages {
field
message
}
}
}
Appointments are created using the createAppointmentType
mutation.
The createAppointmentType
mutation is called from an authenticated provider/staff account.
You can view a full list of potential inputs here. We will go over some of inputs used to create an appointment type.
Input | Info |
---|---|
name |
Required. The name of the appointment type. |
length |
Required. The length of the appointment type in minutes. |
is_group |
Optional. When true , indicates that this appointment type is used for group appointments. |
clients_can_book |
Optional. When false , clients will not have the ability to self-book this appointment. |
contact_type_overrides |
Optional. Available contact types for the appointment type. Provide one or more from ["video_chat", "in_person", "phone_call"] . |
specific_groups |
Optional. Set to true if only specific groups should be able to book this meeting. To be used with bookable_group_ids and bookable_without_group . |
bookable_group_ids |
Optional. A comma-separated list ("1,2" ) of Client Group IDs that are able to book this appointment type. Use "all" for all groups. |
bookable_without_group |
Optional. If set to true , only clients with no groups can book this appointment type. |
user_group_id |
Optional. ID of the Client Group that the Client will be placed in after booking. |
appointment_setting |
Optional. AppointmentTypeAppointmentSettingInputObjectType input object. Please refer to Appointment Settings for further reference. |
Returns a createAppointmentTypePayload
object.
Updating an Appointment Type
mutation updateAppointmentType(
$id: ID,
$name: String,
$length: Int,
$clients_can_book: Boolean,
$is_group: Boolean,
$bookable_without_group: Boolean,
$is_waitlist_enabled: Boolean,
$user_group_id: String,
$specific_groups: Boolean,
$bookable_group_ids: String,
$contact_type_overrides: [String]
$appointment_setting: AppointmentTypeAppointmentSettingInput
) {
updateAppointmentType(
input: {
id: $id,
name: $name,
length: $length,
clients_can_book: $clients_can_book,
is_group: $is_group,
bookable_without_group: $bookable_without_group,
is_waitlist_enabled: $is_waitlist_enabled,
user_group_id: $user_group_id,
specific_groups: $specific_groups,
bookable_group_ids: $bookable_group_ids,
contact_type_overrides: $contact_type_overrides,
appointment_setting: $appointment_setting
}
) {
appointmentType {
id
}
messages {
field
message
}
}
}
Appointment Types are updated via the updateAppointmentType
mutation.
The updateAppointmentType
mutation is called from an authenticated account.
We will go over some of them available to providers and staff below. The updateAppointmentType
mutation shares many common inputs with createAppointmentType
and those inputs (e.g length
or contact_type_overrides
work the same in both places).
You can view a full list of potential inputs here.
Input | Info |
---|---|
id |
Required. ID of the Appointment Type to update. |
Returns a updateAppointmentTypePayload
object.
Deleting an Appointment Type
Appointment Types can be (soft) deleted by authorized providers and staff members via the deleteAppointmentType
mutation.
mutation deleteAppointmentType($id: ID) {
deleteAppointmentType(input: { id: $id }) {
appointmentType {
id
}
messages {
field
message
}
}
}
The deleteAppointmentType
mutation is called from an authenticated provider/staff account.
Input | Info |
---|---|
id |
Required. ID of the Appointment Type to delete. |
Returns a deleteAppointmentTypePayload
object.
Listing Appointment Types
query getAppointmentTypes(
$offset: Int,
$should_paginate: Boolean,
$page_size: Int,
$keywords: String,
$show_group: Boolean,
$provider_id: String,
$clients_can_book: Boolean,
$appointment_type_ids: String,
$with_deleted_appt_types: Boolean
) {
appointmentTypes(
offset: $offset,
should_paginate: $should_paginate,
page_size: $page_size,
keywords: $keywords,
show_group: $show_group,
provider_id: $provider_id,
clients_can_book: $clients_can_book,
appointment_type_ids: $appointment_type_ids,
with_deleted_appt_types: $with_deleted_appt_types
) {
id
name
available_contact_types
length
is_group
is_waitlist_enabled
}
}
A list of Appointment Types can be retrieved via the appointmentTypes
query. This query is called from an authenticated account.
You can view a full list of potential arguments here.
We will go over some of them below.
Input | Info |
---|---|
should_paginate |
Defaults to true . When false , the returned patient list is not paginated. |
keywords |
Optional. A term to search Appointment Types by. Can be searched by the name field. |
appointment_type_ids |
A JSONified array of Appt Type IDs to fetch. Example: ["1", "2"] . |
Returns a list of AppointmentType
objects.
Using Appointment Type Credits
You may want to grant a number of credits to users for booking specific appointment types based on your subscription model. You can add credits to a user with the updateClient
mutation, using the appointment_type_credits_attributes
field. The credits can be queried on the User object using the appointment_type_credits
field. Visit our API Explorer for more details.
Appointment Settings
The Appointment Setting Object
// Some Example Fields for an Appointment Setting.
{
"id": "1",
"allow_clients_to_cancel_appt": true,
"allow_clients_to_reschedule_appt": true,
"allow_past_appointment_rescheduling": false,
"always_send_confirm_notification": false,
"appointment_locations": [
{
"id": "1",
"location": "Bob's Office"
}
],
"appt_type_confirmed_email": false,
"appt_type_reminder_email": false,
"appt_type_website_booking_email": false,
"ask_clients_to_confirm": false,
"ask_to_confirm_via_text": false,
"auto_invoicing": false,
"buffer": "0",
"calendar_color_schemes": [],
"calendar_interval": "30",
"cant_cancel_message": "Please contact provider to cancel",
"cant_reschedule_message": "Please contact provider to reschedule",
"client_should_call_provider": false,
"clients_have_billing": true,
"confirm_by_default": false,
}
Appointment Settings are AppointmentSetting
objects.
You can view the full list of available fields here.
Retrieving an Appointment Setting
query getAppointmentSetting(
$id: ID,
$provider_id: ID
) {
appointmentSetting(id: $id, provider_id: $provider_id) {
id
name
available_contact_types
length
}
}
Retrieving a specific Appointment Setting is done via the appointmentSetting
query.
Please refer to AppointmentSetting
object definition for a list of all available fields.
Input | Info |
---|---|
id |
Either this or provider_id is required. |
provider_id |
Optional. Get Appointment Setting for a specific Provider. |
Returns an AppointmentSetting
object.
Creating an Appointment Setting
mutation createAppointmentSetting(
$buffer: String,
$minimum_advance_cancel_time: Int,
$minimum_advance_reschedule_time: Int,
$minimum_advance_schedule_time: Int,
$end_reminder_one_hour_before: Boolean,
$ask_clients_to_confirm: Boolean
) {
createAppointmentSetting(
input: {
buffer: $buffer,
minimum_advance_cancel_time: $minimum_advance_cancel_time,
minimum_advance_reschedule_time: $minimum_advance_reschedule_time,
minimum_advance_schedule_time: $minimum_advance_schedule_time,
end_reminder_one_hour_before: $end_reminder_one_hour_before,
ask_clients_to_confirm: $ask_clients_to_confirm
}
) {
appointmentSetting {
buffer
minimum_advance_cancel_time
minimum_advance_reschedule_time
minimum_advance_schedule_time
end_reminder_one_hour_before
ask_clients_to_confirm
}
messages {
field
message
}
}
}
Appointment Settings are created using the createAppointmentSetting
mutation.
The createAppointmentSetting
mutation is called from an authenticated provider/staff account.
You can view a full list of potential inputs here. <!-- We will go over some of inputs used to create an appointment setting. -->
Returns a createAppointmentSettingPayload
object.
Updating an Appointment Setting
mutation updateAppointmentSetting(
$id: ID,
$buffer: String,
$minimum_advance_cancel_time: Int,
$minimum_advance_reschedule_time: Int,
$minimum_advance_schedule_time: Int,
$end_reminder_one_hour_before: Boolean,
$ask_clients_to_confirm: Boolean
) {
updateAppointmentSetting(
input: {
id: $id,
buffer: $buffer,
minimum_advance_cancel_time: $minimum_advance_cancel_time,
minimum_advance_reschedule_time: $minimum_advance_reschedule_time,
minimum_advance_schedule_time: $minimum_advance_schedule_time,
end_reminder_one_hour_before: $end_reminder_one_hour_before,
ask_clients_to_confirm: $ask_clients_to_confirm
}
) {
appointmentSetting {
buffer
minimum_advance_cancel_time
minimum_advance_reschedule_time
minimum_advance_schedule_time
end_reminder_one_hour_before
ask_clients_to_confirm
}
messages {
field
message
}
}
}
Appointment Settings are updated via the updateAppointmentSetting
mutation.
The updateAppointmentSetting
mutation is called from an authenticated provider/staff account.
You can view a full list of potential inputs here.
We will go over some of them below. The updateAppointmentSetting
mutation shares many common inputs with createAppointmentSetting
and those inputs (e.g ask_clients_to_confirm
or buffer
work the same in both places).
Input | Info |
---|---|
id |
Required. ID of the Appointment Setting to update. |
Returns an updateAppointmentSettingPayload
object.
Appointments
The Appointment Object
# Some Example Fields for a 1:1 appointment.
{
"appointment": {
"id": "9900",
"date": "2020-05-08 21:00:00 UTC",
"contact_type": "Secure Videochat",
"appointment_type_id": "1128",
"pm_status": "Occurred",
"provider": {
"id": "11",
"full_name": "Jonas Salk"
},
"form_answer_group": {
"id": "1234",
"finished": true,
"form_answers": [
{
"answer": "Example answer",
"label": "Test field",
"id": "123321",
"displayed_answer": "Example answer"
}
]
},
"filled_embed_form": {
"id": "5678",
"finished": true,
"form_answers": [
{
"answer": "Example answer",
"label": "Test field",
"id": "553321",
"displayed_answer": "Example answer"
}
]
},
"user": {
"id": "311",
"full_name": "Example Patient"
}
}
}
Appointments are Appointment
objects.
Appointment
objects are used for 1:1 appointments, group appointments, and blocks to availability.
You can view the full list of available fields here.
Appointments are considered active if they are 1) not deleted, 2) have a pm_status
that is not "Cancelled", "Re-Scheduled", or "No-Show".
If you have questions about the overall functionality of appointment's feature, you can find information here.
Appointments can have two FormAnswerGroup
objects attached: filled_embed_form
which is normally added at the time of booking, and form_answer_group
which is added during the appointment session. Each of these has a one-to-one relationship with the Appointment
object.
Creating an Appointment
Appointments are created in two main ways, createAppointment
and completeCheckout
.
completeCheckout
books the appointment from the patient's perspective (and can be done unathenticated).
createAppointment
books the appointment from a staff member's perspective, requires authentication, and allows for scheduling
appointments outside of available slots. Note that createAppointment
does not enforce availability, but completeCheckout
does enforce availability. This prevents patients from self-scheduling over existing appointments.
The code sample to the right is an example of a simple createAppointment
mutation.
Method | Corresponding Mutation |
---|---|
Added by a provider/staff member | createAppointment |
Added by a patient (client) | completeCheckout |
createAppointment
Mutation
mutation createAppointment(
$user_id: String, # ID of patient in Healthie
$appointment_type_id: String, # ID of appointment type in Healthie
$contact_type: String, # e.g "Phone Call"
$other_party_id: String, # ID of provider in Healthie. Defaults to authenticated user if left blank,
$datetime: String, # Timestamp in YYYY-MM-DD HH:MM:SS or ISO8601 format, supercedes date, time params.
) {
createAppointment(
input: {
user_id: $user_id,
appointment_type_id: $appointment_type_id,
contact_type: $contact_type,
other_party_id: $other_party_id,
datetime: $datetime,
}
) {
appointment {
id
}
messages {
field
message
}
}
The createAppointment
mutation must be called from an authenticated provider/staff account.
You can view a full list of potential inputs here. We will go over some of inputs used to create an appointment with a client below.
Input | Info |
---|---|
datetime |
Required. The datetime of the appointment in ISO8601 format. |
appointment_type_id |
Required. The ID of the appointment type |
contact_type |
Required. How the appointment will be conducted. You can see the list of available ones via the contactTypes query |
other_party_id |
Required. ID of the provider. Defaults to the authenticated user's ID |
user_id |
For 1:1 appointments, this is the ID of the client. For group appointments, leave this out |
attendee_ids |
A comma-seprated list of client IDs to add to the appointment. For 1:1 appointments, leave this out |
providers |
A comma-seprated list of provider IDs to add to the appointment as additional providers. For 1:1 appointments, leave this out. The ID of the primary provider other_party_id should not be included in this list. |
completeCheckout
Mutation
For information on completeCheckout
, take a look at "Step 4 - Book the appointment" in the patient self-scheduling example here.
Retrieving an Appointment
query getAppointment($id: ID) {
appointment(id: $id) {
id
date
contact_type
pm_status
provider {
id
full_name
}
user {
id
full_name
}
appointment_type {
id
name
}
}
}
Retrieving a specific appointment's information is done via the appointment
query.
Input | Info |
---|---|
id |
Required. The ID of the appointment. |
include_deleted |
When true, an appointment can be retrieved, even if it was previously deleted. |
Updating an Appointment
Regardless of user type, Appointments are updated via the updateAppointment
mutation.
However, the allowed and required inputs vary slightly based on whether a provider or a client is the one updating.
Method | Corresponding Mutation |
---|---|
Updated by a provider or client | updateAppointment |
updateAppointment
Mutation
mutation updateAppointment(
$pm_status: String,
$datetime: String,
$id: ID,
) {
updateAppointment(
input: {
$pm_status: String,
$datetime: String,
$id: ID,
}
) {
appointment {
id
date
}
messages {
field
message
}
}
}
The updateAppointment
mutation is called from an authenticated account.
You can view a full list of potential inputs here.
We will go over some of them available to providers and staff below. The updateAppointment
mutation shares many common inputs with createAppointment
and those inputs (e.g other_party_id
or date
work the same in both places).
Input | Info |
---|---|
id |
Required |
pm_status |
The "Post-Meeting" status of the appointment. Options are ["Occurred", "No-Show", "Re-Scheduled", "Cancelled"]. |
For more information on updating an appointment as a patient, please look at the example here
Deleting/Cancelling an appointment.
Appointments can be (soft) deleted by authorized providers and staff members via the deleteAppointment
mutation.
Appointments can be cancelled (without deleting them) by patients via deleteAppointment
or providers and staff via updateAppointment
.
Method | Corresponding Mutation |
---|---|
Deleted by a provider/staff member | deleteAppointment |
Canceled by a patient (but still visible) | deleteAppointment |
Canceled by a provider/staff member (but still visible) | updateAppointment |
deleteAppointment
mutation
mutation deleteAppointment($id: ID, $deleteRecurring: Boolean) {
deleteAppointment(input: { id: $id, deleteRecurring: $deleteRecurring }) {
appointment {
id
}
messages {
field
message
}
}
}
The deleteAppointment
mutation is called from an authenticated account.
If it is called from a provider/staff account, the appointment is deleted, and is no longer visible.
When it is called from a patient account, the appointment's pm_status
is set to Cancelled, and the appointment is still visible.
Input | Info |
---|---|
id |
Required |
deleteRecurring |
Boolean. For recurring appointments only. When true, all subsequent instances of the appointment will be canceled as well. |
Listing Appointments
query appointments(
$user_id: ID,
$filter: String,
$order_by: String,
$should_paginate: Boolean,
$offset: Int,
$is_active: Boolean,
$with_all_statuses: Boolean
) {
appointmentsCount(user_id: $user_id, filter: $filter, is_org: true, is_active: $is_active)
appointments(
is_active: $is_active,
user_id: $user_id,
filter: $filter,
is_org: true,
order_by: $order_by,
should_paginate: $should_paginate,
offset: $offset,
with_all_statuses: $with_all_statuses
) {
id
date
contact_type
length
location
provider {
id
full_name
}
appointment_type {
name
id
}
attendees {
id
full_name
first_name
avatar_url
phone_number
}
}
}
A list of appointments can be retrieved via the appointments
query. This query is called from an authenticated account.
You can view a full list of potential arguments here.
We will go over some of them below.
Input | Info |
---|---|
should_paginate |
Defaults to false. When false, the returned patient list is not paginated. |
filter |
Optional. Defaults to filter (so by default just future appointments will be returned). Options are ["didnt-occur", "future", "upcoming", 'past'. "ended", "all"] |
order_by |
View the enum here to see all potential options. |
user_id |
Optional. When passed in, appointments for that specific user will be returned. Defaults to current user. |
is_org |
Optional. When passed in from a provider or staff account, appointments from all providers in the organization can be returned. |
is_active |
Optional. When true, no non-active (canceled, no-showed, rescheduled, etc) appointments will be returned. |
with_all_statuses |
Optional. When true, all appointments, regardless of pm_status , will be returned. This takes precendece over the didnt-occur filter and is_active . |
Appointment Booking Warnings
The appointmentBookingWarnings
query can be used to return:
- a list of any possible conflicting events for a specific date and time (as well as for recurring event instances)
- a list of clients that do not have any credits available for booking (if using the Client Credit System)
Returns an array of AppointmentBookingWarning
objects. Conflicting events will have a category
of "Event Conflicts," and clients without the necessary credits will have a category of "Credit Deficits." It is possible to have both types of warnings returned in the same response, if there are events existing in the date/time requested, and the proposed client does not have the appropriate amount of credit.
query getAppointmentBookingWarnings(
$appointment_type_id: ID
$provider_id: ID
$date: String
$time: String
$timezone: String
) {
appointmentBookingWarnings(
appointment_type_id: $appointment_type_id
provider_id: $provider_id
date: $date
time: $time
timezone: $timezone
) {
category
subtitle
potential_issues
potential_issue_ids
}
}
# Sample parameters for a request
{
"appointment_type_id": "1",
"provider_id":"3",
"date":"July 11, 2024",
"time":"12 00",
"timezone":"America/New_York"
}
# Sample response returning a conflicting event
{
"data": {
"appointmentBookingWarnings": [
{
"category": "Event Conflicts",
"subtitle": "Existing event(s) for the provider conflict with this one. Would you like to book this event anyway?",
"potential_issues": [
"1:1 Session - Jul 11, 12:00 PM EDT",
"1:1 Session - Jul 11, 12:30 PM EDT"
],
"potential_issue_ids": [
"2111",
"2112"
]
}
]
}
}
Here are the available parameters available when checking for conflicting events and credit deficits.
Input | Info |
---|---|
provider_id |
The calendar of this provider will be searched for conflicting events. |
date |
What date to search for conflicts. Example: "May 23, 2024" |
time |
The time to search for a conflicting event, which is combined with the date field above. Format is "HH MM" in 24-hour time. |
timezone |
Optional. Timezone used for the date and time arguments. Defaults to the timezone of the provider_id used in query. Timezone value must be on this list of timezone identifiers. |
appointment_type_id |
Appointment type is used to determine what span of time to use when checking for conflicts (30 minutes, 60 minutes, etc.). Either this parameter or length_in_minutes are required. |
length_in_minutes |
Specific span of time starting from date and time to check for conflicting events. Either this parameter or appointment_type_id are required. |
attendee_ids |
Optional. This parameter (a comma-delimited string of client IDs) is used for checking whether the passed attendees have enough credits available for booking. The appointment_type_id parameter is required when using this parameter. |
additional_provider_ids |
Optional. Additional providers whose calendars should be searched for conflicting events. Should be a comma-delimited string of provider IDs. |
recurring_appt_id |
Optional. When rescheduling an existing recurring appointment, passing the recurring_appt_id will ensure any existing recurring events in series will not be returned as conflicts. |
current_appt_id |
Optional. When rescheduling an existing appointment, passing this parameter ensures that appointment will not show as a conflict. |
is_repeating |
Optional. Whether to repeat the search based on repeating options. Defaults to false . |
repeat_interval |
Optional. What interval to check again for conflicts. Options are "Weekly", "Biweekly", "Monthly", or "Every 4 Weeks". |
repeat_times |
Optional. How many times to check the repeat interval for conflicts. |
Availability
The Availability Object
{
"id": "1",
"day_of_week": 1,
"duration_string": "5h",
"is_repeating": false,
"range_start": "2022-08-15 07:30:00 +0200",
"range_end": "2022-08-15 12:30:00 +0200",
"resourceId": "1",
"timezone_abbr": "CEST",
"user": {
"email": "superadmin@example.com"
}
}
Calendar Availability is represented using Availability
objects.
Availability
objects are used to allow booking Appointments.
You can view the full list of available fields here.
If you have questions about the overall functionality of the calendar availability feature, you can find information here.
Listing all Availabilities
query(
$show_availability: Boolean,
$one_time: Boolean,
$is_repeating: Boolean,
$startDate: String,
$endDate: String
) {
availabilities(
show_availability: $show_availability,
one_time: $one_time,
is_repeating: $is_repeating,
startDate: $startDate,
endDate: $endDate
) {
id
day_of_week
duration_string
is_repeating
range_start
range_end
resourceId
timezone_abbr
user {
email
}
}
}
A list of availabilities can be retrieved via the availabilities
query. This query is called from an authenticated provider/staff account.
You can view a full list of potential arguments here.
We will go over some of them below.
Input | Info |
---|---|
show_availability |
Optional. If set to false , no results will be returned. |
day_of_week |
Optional. A 0-indexed day of the week the availability is on. |
one_time |
Optional. Query only one-time availabilities. Either this or is_repeating must be provided. |
is_repeating |
Optional. Query only repeating availabilities. Either this or one_time must be provided. |
includeRepeating |
Optional. Include repeating availabilities in the query. |
startDate |
Optional. Find only availabilities from a specific date (inclusive). |
endDate |
Optional. Find only availabilities until a specific date (exclusive). |
is_org |
Optional. If true, retrieves availabilities for providers in an organization. Must be true if passing provider_ids . |
Returns an array of Availability
objects.
Retrieving an Availability
query availability($id: ID) {
availability(id: $id) {
id
custom_modules {
id
day_of_week
duration_string
is_repeating
range_start
range_end
resourceId
timezone_abbr
user {
email
}
}
}
}
Retrieving a specific availability information is done via the availability
query.
Input | Info |
---|---|
id |
Required. The ID of the availability. |
Returns an Availability
object.
Creating an Availability
mutation createAvailability(
$day_of_week: Int
$is_repeating: Boolean
$range_start: String
$time: String
$end_time: String
$user_id: String
) {
createAvailability(input: {
day_of_week: $day_of_week,
is_repeating: $is_repeating,
range_start: $range_start,
time: $time,
end_time: $end_time,
user_id: $user_id,
}) {
availability {
id
}
messages {
field
message
}
}
}
Availabilities are created using the createAvailability
mutation.
To create a recurring (weekly) availability, make sure to set is_repeating
to true
and specify the day_of_week
. One availability is repeating once a week.
You need to create five separate availabilities to define a Monday-Friday availability. Check out how to create Availabilities in bulk.
The createAvailability
mutation is called from an authenticated provider/staff account.
You can view a full list of potential inputs here.
Input | Info |
---|---|
range_start |
Required. Date when the availability starts. For one-time availability, it's the date of the availability. Required date format: month_name DD, YYYY , e.g., August 16, 2022 . |
time |
Required. Time when the availability starts. Required time format: HH MM , e.g., 07 30 or 19 00 . |
end_time |
Required. Time when the availability ends. Required time format: HH MM , e.g., 07 30 or 19 00 . |
day_of_week |
Optional. A 0-indexed day of the week the availability is on (0 for Sunday, 1 for Monday, etc.). For repeating availabilities, it's the day of the availability every week. |
is_repeating |
Optional. If set to true , the availability will be repeating on the day specified in day_of_week . |
user_id |
Optional. ID of the user to create the availability for. |
Returns createAvailabilityPayload
.
Updating an Availability
mutation editAvailability(
$id: String,
$day_of_week: Int
$is_repeating: Boolean
$range_start: String
$time: String
$end_time: String
$user_id: String
) {
editAvailability(input: {
id: $id,
day_of_week: $day_of_week,
is_repeating: $is_repeating,
range_start: $range_start,
time: $time,
end_time: $end_time,
user_id: $user_id,
}) {
availability {
id
}
messages {
field
message
}
}
}
Updating Availabilities is done using the editAvailability
mutation.
The editAvailability
mutation shares many common inputs with createAvailability
and those inputs (e.g range_start
or time
` work the same in both places).
The editAvailability
mutation is called from an authenticated provider/staff account.
You can view a full list of potential inputs here.
Input | Info |
---|---|
id |
Required. ID of the Availability to edit. |
range_start |
Required. Date when the availability starts. For one-time availability, it's the date of the availability. Required date format: month_name DD, YYYY , e.g., August 16, 2022 . |
time |
Required. Time when the availability starts. Required time format: HH MM , e.g., 07 30 or 19 00 . |
end_time |
Required. Time when the availability ends. Required time format: HH MM , e.g., 07 30 or 19 00 . |
day_of_week |
Optional. A 0-indexed day of the week the availability is on (0 for Sunday, 1 for Monday, etc.). For repeating availabilities, it's the day of the availability every week. |
is_repeating |
Optional. If set to true , the availability will be repeating on the day specified in day_of_week . |
user_id |
Optional. ID of the user to create the availability for. |
Returns editAvailabilityPayload
.
Deleting an Availability
mutation deleteAvailability(
$id: ID
) {
deleteAvailability(input: {
id: $id
}) {
availability {
id
}
messages {
field
message
}
}
}
The deleteAvailability
mutation is called from an authenticated provider/staff account.
You can view a full list of potential inputs here.
Input | Info |
---|---|
id |
Required. The ID of the Availability to delete. |
Returns deleteAvailabilityPayload
.
Scheduling
Patient Self-Scheduling
Client self-scheduling is a common use case for our API. With the API, you can build a self-scheduling experience with a much more customized UI than Healthie's built-in embeds allow for.
Our example repo is an example (built with React) on how to implement self-scheduling with the API. Let's break down the API calls we make in that widget.
query appointmentTypes(
$clients_can_book: Boolean,
$provider_id: String
) {
appointmentTypes(provider_id: $provider_id,
clients_can_book: $clients_can_book ) {
id
name
length
available_contact_types
is_group
}
}
1) Get Potential Appointment and Contact Types
All self-scheduled appointments in Healthie require an appointment type and a contact type. In this appointmentTypes
query, we grab all appointment types that are bookable by clients, and the contact type options for each appointment type. We will use these in subsequent queries to receive accurate availability, and ultimately book the appointment. If you already know what appointment type and contact type you want the user to book, you can skip this step, and proceed to step 2.
query daysAvailableForRange(
$provider_id: String
$date_from_month: String
$org_level: Boolean
$timezone: String
$provider_ids: [String]
$appt_type_id: String
) {
daysAvailableForRange(
provider_id: $provider_id
date_from_month: $date_from_month
org_level: $org_level
timezone: $timezone
provider_ids: $provider_ids
appt_type_id: $appt_type_id
)
}
2) Get Available Days
A very common self-scheduling UI component is to present the client with days that have open time. The client then selects a day to see the specific available time slots. This interface is used in Healthie's hosted widgets, in the open source example, and in other popular tools like Calendly. To power this, we can call the availableDaysForRange
query.
This query example shows six arguments, but only three are required, provider_id
, date_from_month
, and appt_type_id
.
appt_type_id
is the ID of the appointment type you grabbed in step 1. provider_id
is the ID of the provider (or just a provider in the organization in the case of searching availability across multiple providers), and date_from_month
is a date (e.g "2021-10-10") in the month that you want to search available days in.
To briefly go over some of the other potential arguments, org_level
determines if you want availability just for the given provider_id
, or for multiple providers in the given provider's organization. It defaults to false.
If you send in org_level
as true, you can also send in an array of provider_ids
. This argument lets you search against a given subset of providers in the organization. For example, my organization has 10 providers, I can send in an array with five ids, and the query will only return availabilities for those five providers.
Finally, the timezone
argument determines what timezone to calculate the availability in. It takes in a TZ database name. If you don't send this in, the query will use the provider's timezone.
This query will return an array of days with availability from the given month. e.g ["2021-10-10", "2021-10-15", "2021-10-20"]. We then have the user select a day, and move to step 3. If we already knew the appointment type, contact type, and date the user wanted, we could have skipped the first two steps.
query availableSlotsForRange(
$provider_id: String
$start_date: String
$end_date: String
$org_level: Boolean
$timezone: String
$provider_ids: [String]
$appt_type_id: String
) {
availableSlotsForRange(
provider_id: $provider_id
start_date: $start_date
end_date: $end_date
timezone: $timezone
org_level: $org_level
provider_ids: $provider_ids
appt_type_id: $appt_type_id
) {
user_id
date
}
}
3) Get Available Slots
Now that we have the day, the next step is to get specific available time slots on that day. To do so, we use the availableSlotsForRange
query. Six of the above arguments match daysAvailableForRange
. In all cases, those arguments do the same thing, so let's focus on one, start_date
and end_date
.
start_date
and end_date
are both required and are used to set up the range of time that we check availability for. For example, if I send in a start_date
of "2021-10-10", an end_date
of "2021-10-12", and a timezone of America/New_York
, the query will check availability starting at "2021-10-10 00:00:00 EDT -04:00" to "2021-10-12 23:59:59 EDT -04:00".
In our case, we are just checking availability on one day, so we would send in an identical start_date
and end_date
.
Input | Info |
---|---|
provider_id |
Required. The ID of the provider to search available slots for. |
start_date |
Required. Date in the YYYY-MM-DD format indicating the lower date range boundary to return available slots for. |
end_date |
Required. Date in the YYYY-MM-DD format indicating the upper date range boundary to return available slots for. |
timezone |
Timezone used for the start_date and end_date arguments. Defaults to the patient's timezone. Timezone value must be on this list of timezone identifiers. |
start_date_boundary |
Date in the YYYY-MM-DD format indicating. If provided, the query will not return any slots before this date even if start_date is before this date. |
end_date_boundary |
Date in the YYYY-MM-DD format. If provided, the query will not return any slots after this date even if end_date is after this date. |
The query returns an array of PotentialAppointmentSlots
. We have the user select one of those slots, and use the user_id
and date
from the slot in the next (and final) step. Please note the date
here is a full datetime string (e.g "2021-10-10 10:00:00 EDT -04:00") and user_id
is the ID of the available provider.
mutation completeCheckout(
$appointment_type_id: String,
$contact_type: String,
$date: String,
$first_name: String,
$last_name: String,
$email: String,
$phone_number: String,
$provider_id: String,
$timezone: String,
) {
completeCheckout(
input: {
appointment_type_id: $appointment_type_id,
contact_type: $contact_type,
date: $date,
timezone: $timezone,
first_name: $first_name,
last_name: $last_name,
email: $email,
phone_number: $phone_number,
provider_id: $provider_id,
}
) {
appointment {
provider {
id
full_name
}
id
date
contact_type
appointment_type {
id
name
length
}
}
messages {
field
message
}
}
}
4) Book the Appointment
Now, we have the desired appointment type, contact type, appointment date/time, and provider. That means we have everything we need to schedule the appointment. To do so, we use the completeCheckout
mutation.
The contact_type
, date
, timezone
, appointment_type_id
, and provider_id
should match what we collected in the prior steps.
If the client is authenticated, then those are the only fields you need. However, in our example, the client is unauthenticated, so we need to send in some info about them as well. That extra info (email
, first_name
, last_name
, and phone_number
) are used to either match the appointment to an existing client in the provider's account, or create a client record on the fly.
Patient Rescheduling
In addition to scheduling new appointments, The Healthie API can also be used to allow patients to reschedule already booked ones. A patient's ability to reschedule is controlled by your appointment settings. You can find more info on adjusting those here.
The rescheduling workflow is similar to self-scheduling, but has some key differences. Here are the API steps we use for patient rescheduling. All of the below steps are meant to be taken from an authenticated patient account.
query appointments(
$filter: String, # "upcoming"
$should_paginate: Boolean, # true
$is_active: Boolean, # true
) {
appointmentsCount(filter: $filter, is_active: $is_active)
appointments(
is_active: $is_active,
filter: $filter,
should_paginate: $should_paginate,
) {
id
date
other_party_id
appointment_type_id
}
}
1) Get The Appointment ID
To check availability and reschedule an appointment, you will need the ID for the appointment, the provider (other_party_id
), and appointment type. Here's a query to get paginated upcoming active appointments for a patient. These IDs will be used in the subsequent steps.
query daysAvailableForRange(
$provider_id: String # Should be the other_party_id from Step 1
$date_from_month: String
$timezone: String
$appt_type_id: String # should be the appointment_type_id from Step 1
$appointment_to_reschedule_id: ID # should be the ID of the appointment from Step 1
) {
daysAvailableForRange(
provider_id: $provider_id
date_from_month: $date_from_month
timezone: $timezone
appt_type_id: $appt_type_id
appointment_to_reschedule_id: $appointment_to_reschedule_id
)
}
2) Get Available Days
In our rescheduling UI, we present the patient with days that have open time. The patient then selects a day to see the specific available time slots. To power this, we can call the availableDaysForRange
query.
Input | Info |
---|---|
provider_id |
Required. The provider whose availability should be checked. Needs to match the other_party_id of the appointment that is being rescheduled. |
appt_type_id |
Required. The ID of the appointment's appointment type. Needs to match the appointment_type_id of the appointment that is being rescheduled. |
date_from_month |
Required. A date (e.g "2021-11-20") from the month that you want to check available days in. |
appointment_to_reschedule_id |
Required. Needs to match the id of the appointment that is being rescheduled. |
timezone |
Optional. Determines what timezone to calculate the availability in. Defaults to the patient account's timezone. Timezone value must be on this list of timezone identifiers. |
This query will return an array of days with availability from the given month. e.g ["2021-11-20", "2021-11-28", "2021-11-29"]. We then have the patient select a day, and move to step 3.
query availableSlotsForRange(
$provider_id: String
$start_date: String
$end_date: String
$timezone: String
$appt_type_id: String
$appointment_to_reschedule_id: ID
) {
availableSlotsForRange(
provider_id: $provider_id
start_date: $start_date
end_date: $end_date
timezone: $timezone
appt_type_id: $appt_type_id
appointment_to_reschedule_id: $appointment_to_reschedule_id
) {
user_id
date
}
}
3) Get Available Slots
Now that we have the day, the next step is to get specific available time slots on that day. To do so, we use the availableSlotsForRange
query. The arguments are very similar to the daysAvailableForRange
. In all cases, those arguments do the same thing, so let's focus on the one difference, start_date
and end_date
.
start_date
and end_date
are both required and are used to set up the range of time that we check availability for. For example, if I send in a start_date
of "2021-10-10", an end_date
of "2021-10-12", and a timezone of America/New_York
, the query will check availability starting at "2021-10-10 00:00:00 EDT -04:00" to "2021-10-12 23:59:59 EDT -04:00".
In our case, we are just checking availability on one day, so we would send in an identical start_date
and end_date
.
Input | Info |
---|---|
provider_id |
Required. The ID of the provider to search available slots for. |
start_date |
Required. Date in the YYYY-MM-DD format indicating the lower date range boundary to return available slots for. |
end_date |
Required. Date in the YYYY-MM-DD format indicating the upper date range boundary to return available slots for. |
timezone |
Timezone used for the start_date and end_date arguments. Defaults to the patient's timezone. |
start_date_boundary |
Date in the YYYY-MM-DD format indicating. If provided, the query will not return any slots before this date even if start_date is before this date. |
end_date_boundary |
Date in the YYYY-MM-DD format. If provided, the query will not return any slots after this date even if end_date is after this date. |
The query returns an array of PotentialAppointmentSlots
. We have the user select one of those slots, and use the user_id
and date
from the slot in the next (and final) step. Please note the date
here is a full datetime string (e.g "2021-10-10 10:00:00 EDT -04:00") and user_id
is the ID of the available provider.
mutation updateAppointment(
$client_updating: Boolean, # true
$datetime: String,
$id: ID,
) {
updateAppointment(
input: {
$client_updating: Boolean,
$datetime: String,
$id: ID,
}
) {
appointment {
id
date
}
messages {
field
message
}
}
}
4) Update the Appointment
Finally, we can use the updateAppointment
mutation to move the appointment to the new available slot.
Input | Info |
---|---|
id |
Required. The ID of the appointment to update. |
datetime |
Required. This should be the date from the slot that was selected in the previous step. |
client_updating |
Required. Needs to be true. |
In the case that the slot is no longer available (e.g another patient simultaneously booked an appointment for the same slot), an error will be returned in the messages
of the response.
Payment Processing
Storing a Credit Card
mutation createStripeCustomerDetail(
$token: String # e.g tok_1JjpBZ2eZvKYlo2CdI1Qwgf7
$card_type_label: String # e.g 'personal'
$user_id: ID # e.g "61"
$is_default: Boolean # e.g true
) {
createStripeCustomerDetail(
input: {
token: $token
card_type_label: $card_type_label
user_id: $user_id
is_default: $is_default
}
) {
stripe_customer_detail {
id
}
messages {
field
message
}
}
}
Healthie (via our usage of Stripe) allows you to store a patient's credit/debit cards on file. You can store multiple cards on file for the same patient. There are two main ways to store a card. The first way happens automatically when a patient makes a payment (see "Charging a Patient" below). The second is via the createStripeCustomerDetail
mutation. That mutation takes in 4 input fields - card_type_label
, is_default
, token
, and user_id
. Let's go over each one.
user_id
is the ID of the patient that the card should be attached to. This is required.
token
is the tokenized card information. You generate this via Stripe's front-end libraries and can find info on how to do so here. This is required. To generate the token, you'll need Healthie's Stripe publishable key for the appropriate environment:
Staging / sandbox: pk_test_fAj7WlTrG0uc5Z9WHKQDdoTq
Production: pk_live_WzFpsrfurxhcz0HJspt9nbnn
card_type_label
lets you store the type of card. Options are personal
, hsa
, and fsa
. Defaults to personal
if nothing is passed in. The card_type_label
field is just for your own informational purposes, and does not affect how payments are processed.
is_default
sets the default payment source for the patient. If the patient only has one stored payment source, that payment source is automatically the default, so this field only matters for patients with multiple payment sources.
Charging a Patient
mutation createBillingItem(
$amount_paid: String, # e.g "567.53"
$sender_id: ID, # e.g "61"
$requested_payment_id: ID, # e.g 11
$stripe_idempotency_key: String, # Stripe reccommends using V4 UUIDs
$stripe_customer_detail_id: ID # e.g 21
$should_charge: Boolean, # true
) {
createBillingItem(input: {
amount_paid: $amount_paid,
sender_id: $sender_id,
should_charge: $should_charge,
stripe_customer_detail_id: $stripe_customer_detail_id,
requested_payment_id: $requested_payment_id,
stripe_idempotency_key: $stripe_idempotency_key,
}) {
billingItem {
id
}
messages {
field
message
}
}
}
Patients make payments in two ways. You can charge a patient from a provider/staff account (via the createBillingItem
mutation) or patients can make payments themselves (via completeCheckout
mutation). In this example, we will cover how to charge a patient using createBillingItem
. Only patients with a stored payment source can be charged using createBillingItem
. Take a look at "Storing a Credit Card" above if you are not yet storing payment sources.
Check out Creating a Billing Item or Creating an Invoice for further reference.
One-time vs. recurring payments
A client will automatically be billed on a recurring basis (a RecurringPayment
will be created) given all of the following conditions:
- The
BillingItem
is associated with anOffering
(also known as a "package" in Healthie) that has abilling_frequency
value that is not "One-Time" - You do not pass a
payment_due_date
parameter to thecreateBillingItem
mutation
A client will be billed only once given any of the following conditions:
- The
BillingItem
is associated with anOffering
that does not have abilling_frequency
value, or if thebilling_frequency
value is "One-Time" - The
BillingItem
is not associated with anOffering
at all - You pass a
payment_due_date
parameter to thecreateBillingItem
mutation with the current day's date
How to halt a recurring payment
If you want to pause or stop a RecurringPayment
via the API, you can use the updateBillingItem
mutation. To pause the RecurringPayment
, pass the parameter is_paused: true
. To stop the RecurringPayment
, pass the parameter is_canceled: true
. You can pass the ID of any BillingItem
that is part of the RecurringPayment
.
Billing Items
The Billing Item Object
{
"id": "1",
"amount_paid": "1234.00",
"currency": "USD",
"note": "Test payment",
"provider": {
"id": "1"
},
"recipient": {
"id": "1"
},
"sender": {
"id": "2"
}
}
Billing Items are BillingItem
objects.
You can view the full list of available fields here.
Listing Billing Items
query billingItems(
$offset: Int
keywords: String
sort_by: String
state: String
provider_id: ID
offerings_only: Boolean
) {
billingItems(
offset: $offset,
keywords: $keywords,
sort_by: $sort_by,
state: $state,
provider_id: $provider_id,
offerings_only: $offerings_only
) {
id
amount_paid
currency
created_at
failure_reason
sender {
id
}
recipient {
id
}
}
}
Listing Billing Item is done via the billingItems
query.
You can view a full list of potential arguments here.
Input | Info |
---|---|
offset |
Optional. Offset for pagination. |
sort_by |
Optional. Valid options are:
|
keywords |
Optional. Keywords to search by. Billing Items can be searched by amount_paid and payment_medium . |
state |
Optional. Valid options are:
|
offerings_only |
Optional. Return only Billing Items that are associated to an offering. |
Returns a list of BillingItem
objects.
Retrieving a Billing Item
query billingItem($id: ID) {
billingItem(id: $id) {
id
amount_paid
currency
created_at
failure_reason
sender {
id
}
recipient {
id
}
}
}
Retrieving a specific Billing Item is done via the billingItem
query.
Input | Info |
---|---|
id |
Required. ID of the Billing Item to query. |
Returns a BillingItem
object.
Creating a Billing Item
mutation createBillingItem(
$amount_paid: String, # e.g "567.53"
$sender_id: ID, # e.g "61"
$requested_payment_id: ID, # e.g 11
$stripe_idempotency_key: String, # Stripe recommends using V4 UUIDs
$stripe_customer_detail_id: ID # e.g 21
$should_charge: Boolean, # true
) {
createBillingItem(input: {
amount_paid: $amount_paid,
sender_id: $sender_id,
should_charge: $should_charge,
stripe_customer_detail_id: $stripe_customer_detail_id,
requested_payment_id: $requested_payment_id,
stripe_idempotency_key: $stripe_idempotency_key,
}) {
billingItem {
id
}
messages {
field
message
}
}
}
You can view a full list of potential inputs here.
Input | Info |
---|---|
created_at |
Optional. Used when tracking outside payments, and should be left out when actually charging a card. |
amount_paid |
Optional. The amount to charging the patient. If left blank, the patient will be charged the full amount of the package or invoice. |
note |
Optional. Used to provide info about outside payments. |
sender_id |
Required. The ID of the patient to charge. |
recipient_id |
Optional. The ID of the provider/staff member who should be listed as the recipient of the payment. This is primarily important in sitatuons where each provider has their own bank account. If left out, it defaults to the sender's primary provider. |
payment_medium |
Optional. Used for outside payments and can be ignored. |
offering_coupon_id |
Optional. is the ID of the promo code you want to apply to the payment. The promo code discount is applied to the initial payment amount. (e.g if you want to apply a 30% promo code, and have the client end up paying $700, you would pass in amount_paid of 1000). |
offering_id |
The ID of the package that the patient is paying for. If you are charging the patient for a package via an invoice connected to the package, you should send in the requested_payment_id instead. |
should_charge |
Determine whether the charge actually runs through our payment processor. This should be passed in as true if you want to charge the card. |
stripe_customer_detail_id |
Optional. The ID of the StripeCustomerDetail (e.g the payment source) that the patient is using to pay. If left out, the patient is charged on their default payment source. |
requested_payment_id |
Optional. The ID of the invoice that the patient is paying for. You can have the patient make multiple payments that apply to the same invoice. To do so, just include the same requested_payment_id in each charge. |
user_package_selection_id |
Optional. Is only used if you are charging a patient for an existing UserPackageSelection . |
stripe_idempotency_key |
Optional. A key generated on the front-end that is used by Stripe to prevent duplicate requests. Find more info about them here. This is optional, but highly recommended. |
Returns a createBillingItemPayload
object.
Sometimes the charge will be declined by the card issuer. The decline reason is included in the messages
you can get back from the mutation. For more information on specific decline reasons, please view Stripe's list of decline codes.
Updating a Billing Item
mutation updateBillingItem(
$id: ID,
$amount_paid: String, # e.g "567.53"
$sender_id: ID, # e.g "61"
$stripe_idempotency_key: String, # Stripe recommends using V4 UUIDs
$resend_payment: Boolean
) {
updateBillingItem(input: {
id: $id,
amount_paid: $amount_paid,
sender_id: $sender_id,
stripe_idempotency_key: $stripe_idempotency_key,
resend_payment: $resend_payment
}) {
billingItem {
id
}
messages {
field
message
}
}
}
You can view a full list of potential inputs here.
Input | Info |
---|---|
id |
Required. The ID of the Billing Item to update. |
created_at |
Optional. Used when tracking outside payments, and should be left out when actually charging a card. |
amount_paid |
Optional. The amount to charging the patient. If left blank, the patient will be charged the full amount of the package or invoice. |
note |
Optional. Used to provide info about outside payments. |
sender_id |
Optional. The ID of the patient to charge. |
recipient_id |
Optional. The ID of the provider/staff member who should be listed as the recipient of the payment. This is primarily important in sitatuons where each provider has their own bank account. If left out, it defaults to the sender's primary provider. |
payment_medium |
Optional. Used for outside payments and can be ignored. |
chosen_refund_amount |
Optional. If provided, a given amount will be refunded to the sender. |
is_canceled |
Optional. If set to true , the payment will be cancelled. |
resend_receipt |
Optional. Set to true to resend the receipt to sender. |
is_paused |
Optional. If set to true , will pause the recurring payment. |
resend_payment |
Optional. Use this to retry a failed payment. It's highly recommended to use combined with stripe_idempotency_key . |
new_payment_date |
Optional. Update the payment day of a recurring payment. |
stripe_idempotency_key |
Optional. A key generated on the front-end that is used by Stripe to prevent duplicate requests. Find more info about them here. This is optional, but highly recommended. |
Returns an updateBillingItemPayload
object.
Sometimes the charge will be declined by the card issuer. The decline reason is included in the messages
you can get back from the mutation. For more information on specific decline reasons, please view Stripe's list of decline codes.
Deleting a Billing Item
Billing Items can be (soft) deleted by authorized providers and staff members via the deleteBillingItem
mutation.
You can view a full list of potential inputs here.
mutation deleteBillingItem($id: ID) {
deleteBillingItem(input: { id: $id }) {
billingItem {
id
}
messages {
field
message
}
}
}
The deleteBillingItem
mutation is called from an authenticated provider/staff account.
Input | Info |
---|---|
id |
Required. ID of the Billing Item to delete. |
Returns a deleteBillingItemPayload
object.
Packages
The Package Object
// Some Example Fields for an Offering
{
"id": "1",
"name": "Example Package",
"price": "100.0",
"currency": "USD",
"repeat_times": null,
"show_offering": false,
"frequency_times_string": "a week",
"abbreviated_frequency_times_string": "",
"living_plate_meal_plan_name": null,
"billing_frequency": "Weekly",
"first_time_payment": null,
"archived": false,
"visibility_status": "hidden",
"initial_payment_amount": "100.0",
"initial_price_with_taxes": "100.0",
"offering_includes": [
{
"id": "1",
"quantity": "1",
"is_repeating": true,
"appointment_type": {
"id": "1",
"name": "Initial Consultation",
"clients_can_book": true
}
},
{
"id": "2",
"quantity": "3",
"is_repeating": true,
"appointment_type": {
"id": "2",
"name": "Follow-up Session",
"clients_can_book": true
}
}
]
}
Within the API, Packages are known as Offering
objects. In this documentation we will use both terms interchangeably.
You can view the full list of available fields here.
If you have questions about the overall functionality of Client Packages, you can find information here.
Retrieving a Package
query getOffernig(
$id: ID
) {
offering(id: $id) {
id
name
billing_frequency
currency
price
initial_payment_amount
initial_price_with_taxes
}
}
Retrieving a specific Package is done via the offering
query.
Input | Info |
---|---|
id |
Required. ID of the Package to query. |
Returns an Offering
object.
Creating a Package
mutation createOffering(
$name: String,
$price: String,
$billing_frequency: String,
$offering_includes: [OfferingIncludesFields],
$first_time_payment: String,
$visibility_status: String,
$charge_immediately: Boolean,
$course_ids: String
) {
createOffering(
input: {
name: $name,
price: $price,
billing_frequency: $billing_frequency,
offering_includes: $offering_includes,
first_time_payment: $first_time_payment,
visibility_status: $visibility_status,
charge_immediately: $charge_immediately,
course_ids: $course_ids
}
) {
offering {
id
}
messages {
field
message
}
}
}
Packages are created using the createOffering
mutation.
The createOffering
mutation is called from an authenticated provider/staff account.
You can view a full list of potential inputs here. We will go over some of inputs used to create an Offering.
Input | Info |
---|---|
name |
Required. The name of the Package. |
price |
Required. Package price as string. Check the minimumOfferingPrice query to get a minimum Package price. |
billing_frequency |
Required. Valid options are:
|
offering_includes |
Optional. A list of appointment sessions included in the Program. Objects of type OfferingIncludesFields . |
first_time_payment |
Optional. Price for the first payment. |
charge_immediately |
Optional. Whether to charge the Client immediately. |
course_ids |
Optional. A comma-separated string of Program IDs to include in the Package. |
Returns a createOfferingPayload
object.
Updating a Package
mutation updateOffering(
$name: String,
$price: String,
$billing_frequency: String,
$offering_includes: [OfferingIncludesFields],
$first_time_payment: String,
$visibility_status: String,
$charge_immediately: Boolean,
$course_ids: String
) {
updateOffering(
input: {
name: $name,
price: $price,
billing_frequency: $billing_frequency,
offering_includes: $offering_includes,
first_time_payment: $first_time_payment,
visibility_status: $visibility_status,
charge_immediately: $charge_immediately,
course_ids: $course_ids
}
) {
offering {
id
}
messages {
field
message
}
}
}
Packages are updated via the updateOffering
mutation.
The updateOffering
mutation is called from an authenticated account.
The updateOffering
mutation shares many common inputs with createOffering
and those inputs (e.g first_time_payment
or visibility_status
work the same in both places).
You can view a full list of potential inputs here.
Input | Info |
---|---|
id |
Required. ID of the Package to update. |
archived |
Optional. Set to true to archive the Package. |
Returns a updateOfferingPayload
object.
Cloning a Package
mutation copyOffering($id: ID) {
copyOffering(input: { id: $id }) {
offering {
id
}
messages {
field
message
}
}
}
You can create a copy of a Package by running the copyOffering
mutation.
The copyOffering
mutation is called from an authenticated provider/staff account.
You can view a full list of potential inputs here.
Input | Info |
---|---|
id |
Required. ID of the Package to clone. |
Returns a copyOfferingPayload
object.
Deleting a Package
mutation deleteOffering($id: ID) {
deleteOffering(input: { id: $id }) {
offering {
id
}
messages {
field
message
}
}
}
Packages can be (soft) deleted by authorized providers and staff members via the deleteOffering
mutation.
The deleteOffering
mutation is called from an authenticated provider/staff account.
You can view a full list of potential inputs here.
Input | Info |
---|---|
id |
Required. ID of the Package to delete. |
Returns a deleteOfferingPayload
object.
Listing all Packages
query getOfferings(
$offset: Int,
$should_paginate: Boolean,
$keywords: String,
$sort_by: String,
$provider_id: ID,
$offering_id: ID,
$offering_ids: [ID],
$only_client_visible: Boolean,
$status: String,
$client_visibility: String,
$offering_user_group_id: ID,
$show_only_visible: Boolean
) {
offerings(
offset: $offset,
should_paginate: $should_paginate,
keywords: $keywords,
sort_by: $sort_by,
provider_id: $provider_id,
offering_id: $offering_id,
offering_ids: $offering_ids,
only_client_visible: $only_client_visible,
status: $status,
client_visibility: $client_visibility,
offering_user_group_id: $offering_user_group_id,
show_only_visible: $show_only_visible
) {
id
name
billing_frequency
currency
price
initial_payment_amount
initial_price_with_taxes
}
}
A list of Packages can be retrieved via the offerings
query. This query is called from an authenticated account.
You can view a full list of potential arguments here.
We will go over some of them below.
Input | Info |
---|---|
keywords |
Optional. A term to search Packages by. Can be searched by the name, description and price fields. |
offering_id |
Optional. An ID of a single Offering to return. |
offering_ids |
Optional. An array of Offering IDs to return. |
client_visibility |
Optional. Filters by Package visibility to clients. Possible options:
|
show_only_visible |
Optional. Returns Packages that are visible to everyone. |
only_client_visible |
Optional. Returns Packages that are either visibile to everyone or to the Client Groups that the current user belongs to. |
offering_user_group_id |
Optional. Returns Packages that are visible only to a specific User Group. |
status |
Optional. Provide archived to fetch only archived Packages. |
Returns a list of Offering
objects.
Invoices
Invoices (called RequestedPayment
s in our API) allow you to request and track owed payments from patients.
Patients can be invoiced for a Package (Offering
in our API), a CMS1500, or for other custom products/services.
Invoices can be sent to third-party payers even if they don't have a Healthie account (helpful in cases like corporate wellness), but are most commonly sent directly to the patient. In the examples below, we'll cover how to send an invoice to a patient.
The Invoice Object
{
"id": "1",
"price": "1234.00",
"status": "Not Yet Paid",
"invoice_id": "UU-1-2022",
"details": "Other: example description",
"currency": "USD",
"invoice_type": "other",
"debt": 1234,
"notes": "Example notes",
"billing_item_id": null,
"billing_items": [],
"is_manually_paid": false,
"requested_payer": {
"id": "3"
},
"recipient": {
"id": "3",
"dietitian": {
"id": "1"
}
},
"sender": {
"id": "1"
},
"date_to_show": "2022-08-15 14:22:14 -0400"
}
Invoices are RequestedPayment
objects.
You can view the full list of available fields here.
Listing Invoices
query requestedPayments(
$offset: Int,
$keywords: String,
$sort_by: String,
$only_unpaid: Boolean,
$sender_id: ID,
$status_filter: String,
$preview: Boolean
) {
requestedPayments(
offset: $offset,
keywords: $keywords,
sort_by: $sort_by,
only_unpaid: $only_unpaid,
sender_id: $sender_id,
status_filter: $status_filter,
preview: $preview
) {
id
price
status
invoice_id
recipient {
id
}
sender {
id
}
}
}
Listing Invoices is done via the requestedPayments
query.
You can view a full list of potential arguments here.
Input | Info |
---|---|
offset |
Optional. Offset for pagination. |
keywords |
Optional. Keywords to search by. Billing Items can be searched by status, recipient's first or last name, and offering's name. |
sort_by |
Optional. Valid options are:
|
only_unpaid |
Optional. Return only unpaid Invoices. |
sender_id |
Optional. ID of the Sender. |
status_filter |
Optional. Valid options are:
|
preview |
Optional. Return only Invoices that are in preview. |
Returns a list of RequestedPayment
objects.
Retrieving an Invoice
query requestedPayment(
$id: ID,
$invoice_id: String,
$uuid: String,
$preview: Boolean
) {
requestedPayment(
id: $id,
invoice_id: $invoice_id,
uuid: $uuid,
preview: $preview
) {
id
price
status
invoice_id
recipient {
id
}
sender {
id
}
}
}
Retrieving a specific Invoice is done via the requestedPayment
query.
At least one of the id
, invoice_id
and uuid
inputs must be provided.
Input | Info |
---|---|
id |
ID of the Invoice to query. |
invoice_id |
Invoice ID, must be used together with uuid . |
uuid |
External ID of the Requested Payer. |
preview |
Optional. Return only if the Invoice is in preview. |
Returns a RequestedPayment
object.
Creating an Invoice
mutation createRequestedPayment(
$recipient_id: ID, # e.g "61"
$offering_id: ID, # e.g "11"
$price: String, # can be left blank since it will default to the price of the package
$invoice_type: String, # "offering"
) {
createRequestedPayment(input: {
recipient_id: $recipient_id,
offering_id: $offering_id,
price: $price,
invoice_type: $invoice_type,
})
{
requestedPayment {
id
}
messages {
field
message
}
}
}
Invoices are created via the createRequestedPayment
mutation. Let's break down the inputs that mutation accepts.
You can view a full list of potential inputs here.
Input | Info |
---|---|
invoice_type |
Required. The options are
|
recipient_id |
Ihe ID of the patient. This is required unless are you invoicing a third-party (in which case you'd send in requested_payer ). |
sender_id |
Optional. The ID of the staff member that the invoice is associated with. This defaults to the current user if not sent in. |
offering_id |
Optional. The ID of the package that the invoice is associated with. This is only used if you are invoicing for a package. |
cms1500_id |
Optional. The ID of the CMS1500 that the invoice is associated with. This is only used if you are invoicing for a CMS1500. |
price |
Required if invoicing without a package. The amount you are invoicing the patient for. If you are invoicing for a package, this field is optional (and the invoice will default to the package price if this is left out) |
notes |
Optional. Lets you add extra details to the invoice. These notes are visible to the patient. |
service_date |
Optional. Can only be used the invoice_type is other . |
services_provided |
Required if invoice_type is other . Otherwise, it should not be sent in. This lets you describe the products/services that you are invoicing the patient for. |
status |
Optional (and is normally not sent in). status lets you explicitly set the status of the invoice. The options are:
|
is_preview |
Optional. Defaults to false . When passed in as true , the invoice is kept in a preview state and hidden from the Healthie UI. |
user_package_selection_id |
Optional. Only used if you are invoicing a patient for an existing UserPackageSelection . |
So in sum, you'd normally send in a recipient_id
, invoice_type
, price
(unless it is for a package and you want to default to the package price), and what you are invoicing for (either a offering_id
, cms1500_id
, or services_provided
). The example on the sidebar shows creating an invoice for a package.
Returns a createRequestedPaymentPayload
object.
Updating an Invoice
mutation updateRequestedPayment(
$id: ID!,
$recipient_id: ID, # e.g "61"
$offering_id: ID, # e.g "11"
$price: String, # can be left blank since it will default to the price of the package
$invoice_type: String, # "offering",
$send_request_email: Boolean,
$resend_receipt: Boolean,
) {
updateRequestedPayment(input: {
id: $id,
recipient_id: $recipient_id,
offering_id: $offering_id,
price: $price,
invoice_type: $invoice_type,
send_request_email: $send_request_email,
resend_receipt: $resend_receipt
}) {
requestedPayment {
id
}
messages {
field
message
}
}
}
The updateRequestedPayment
mutation shares many common inputs with createRequestedPayment
and those inputs (e.g invoice_type
or is_preview
work the same in both places).
You can view a full list of potential inputs here.
Input | Info |
---|---|
id |
Required. The ID of the Invoice to update. |
send_request_email |
Optional. Whether to resend the Invoice email. |
resend_receipt |
Optional. Whether to resend the charge confirmation receipt. |
Returns an updateRequestedPaymentPayload
object.
Deleting an Invoice
Invoices can be (soft) deleted by authorized providers and staff members via the deleteRequestedPayment
mutation.
You can view a full list of potential inputs here.
mutation deleteRequestedPayment($id: ID) {
deleteRequestedPayment(input: { id: $id }) {
requestedPayment {
id
}
messages {
field
message
}
}
}
The deleteRequestedPayment
mutation is called from an authenticated provider/staff account.
Input | Info |
---|---|
id |
Required. ID of the Invoice to delete. |
Returns a deleteRequestedPaymentPayload
object.
Programs
You can learn more about the Programs feature in general on Healthie's Help platform.
The Program Object
{
"id": "2",
"name": "Program 1",
"description": null,
"created_at": "2022-08-16 08:33:14 -0400",
"course_memberships_count": "0",
"course_type": "rolling",
"start_date": null,
"end_date": null,
"course_items": [
{
"id": "4",
"scheduled_release": "1"
}
]
}
Programs are Course
objects.
Programs consist of one or many Modules (CourseItem
objects).
You can view the full list of available fields here.
Listing Programs
query courses(
$offset: Int,
$keywords: String,
$course_type: String,
$should_paginate: Boolean,
$only_available: Boolean
) {
courses(
offset: $offset,
keywords: $keywords,
course_type: $course_type,
should_paginate: $should_paginate,
only_available: $only_available
) {
id
name
description
course_type
course_items {
id
category
item_type
item_id
}
start_date
}
}
Listing Programs is done via the courses
query.
You can view a full list of potential arguments here.
Input | Info |
---|---|
offset |
Optional. Offset for pagination. |
keywords |
Optional. Keywords to search by. Programs can be searched by name and description. |
course_type |
Optional. Can be either rolling or fixed . |
only_available |
Optional. If set to true , will return Programs that are either rolling or with a fixed start date that occurs in the future. |
Returns a list of Course
objects.
Retrieving a Program
query course(
$course_id: ID
) {
course(
course_id: $course_id
) {
id
name
description
course_type
course_items {
id
category
item_type
item_id
}
start_date
}
}
Retrieving a specific Program is done via the course
query. This query is considered public.
Input | Info |
---|---|
course_id |
ID of the Program to query. |
Returns a Course
object.
Creating a Program
mutation createCourse(
$name: String
$description: String
$preview_video_content: String
$formatted_benefits: String
$external_preview_image_url: String
$preview_image: Upload
$course_type: String
$start_date: String
$late_enroll: Boolean
) {
createCourse(input: {
name: $name,
description: $description,
preview_video_content: $preview_video_content,
formatted_benefits: $formatted_benefits,
external_preview_image_url: $external_preview_image_url,
preview_image: $preview_image,
course_type: $course_type,
start_date: $start_date,
late_enroll: $late_enroll
})
{
course {
id
}
messages {
field
message
}
}
}
Programs are created via the createCourse
mutation. Let's break down the inputs that mutation accepts.
You can view a full list of potential inputs here.
Input | Info |
---|---|
name |
Required. |
course_type |
Required. Valid options are
|
start_date |
Required when course_type is fixed . |
preview_image |
Optional. Use this to upload a preview image for the Program. Please refer to the File Uploads section for more information. |
external_preview_image_url |
Optional. Link to external preview image. Can be used instead of uploading via the preview_image. |
formatted_benefits |
Optional. A HTML-formatted string with an <ul> or <ol> list of Program benefits. |
late_enroll |
Optional. This is an option for a fixed Program that determines whether clients can be enrolled in the program after the fixed start date has passed. If enabled, a client who is enrolled late will immediately receive access to all modules that all other clients in the program have access to. |
Returns a createCoursePayload
object.
Updating a Program
mutation updateCourse(
$id: String
$name: String
$description: String
$preview_video_content: String
$formatted_benefits: String
$external_preview_image_url: String
$preview_image: Upload
$course_type: String
$start_date: String
$late_enroll: Boolean,
$course_members: [CourseMembersInput]
) {
updateCourse(input: {
id: $id
name: $name,
description: $description,
preview_video_content: $preview_video_content,
formatted_benefits: $formatted_benefits,
external_preview_image_url: $external_preview_image_url,
preview_image: $preview_image,
course_type: $course_type,
start_date: $start_date,
late_enroll: $late_enroll,
course_members: $course_members
}) {
course {
id
}
messages {
field
message
}
}
}
The updateCourse
mutation shares many common inputs with createCourse
and those inputs (e.g preview_image
or start_date
work the same in both places).
You can view a full list of potential inputs here.
Input | Info |
---|---|
id |
Required. The ID of the Program to update. |
course_members |
Optional. A list of CourseMembersInput objects that defines Program membership for groups and users. See table below. |
Returns an updateCoursePayload
object.
The Course Members Object
{
"label": "John Doe",
"value": "user-1" // 1 is the ID of the user
}
Please refer to the GraphQL definition of the input here.
Input | Info |
---|---|
label |
Required. An arbitrary label, usually user or group name. |
value |
Required. A prefixed string representing either a Client Group ID or User (Patient) ID. Examples: group-1 or user-2 . |
Deleting a Program
Programs can be (soft) deleted by authorized providers and staff members via the deleteCourse
mutation.
You can view a full list of potential inputs here.
mutation deleteCourse($id: ID) {
deleteCourse(input: { id: $id }) {
course {
id
}
messages {
field
message
}
}
}
The deleteCourse
mutation is called from an authenticated provider/staff account.
Input | Info |
---|---|
id |
Required. ID of the Program to delete. |
Returns a deleteCoursePayload
object.
Journal Entries
You can learn more about the Journal feature in general on Healthie's Help platform.
The Journal Entry Object
{
"id": "15",
"description": "Example entry",
"created_at": "2022-08-03 09:55:55 -0400",
"type": "FoodEntry",
"name": "Food",
"viewed": false,
"healthiness_info_hex_value": "#35b032",
"has_subentries": false,
"previous_water_intake_stat": 0,
"default_water_intake_for_entry_user": 0,
"poster": {
"id": "7"
}
}
Journal Entries are Entry
objects.
You can view the full list of available fields here.
Listing Entries
query entries(
$offset: Int,
$type: String,
$keywords: String,
$entry_id: String,
$category: String,
$client_id: String,
$is_org: Boolean,
$end_range: String,
$start_range: String,
$group_id: String,
$sort_by: String,
$end_datetime_range: String,
$start_datetime_range: String,
$summary_view: Boolean
) {
entries(
offset: $offset,
type: $type,
keywords: $keywords,
entry_id: $entry_id,
category: $category,
client_id: $client_id,
is_org: $is_org,
end_range: $end_range,
start_range: $start_range,
group_id: $group_id,
sort_by: $sort_by,
end_datetime_range: $end_datetime_range,
start_datetime_range: $start_datetime_range,
summary_view: $summary_view
) {
id
type
added_by_user {
id
}
category
emotions
name
description
}
}
Listing Entries is done via the entries
query.
You can view a full list of potential arguments here.
Input | Info |
---|---|
type |
Optional. Entry type, can be one of: FoodEntry , MirrorEntry , MetricEntry , WorkoutEntry , SleepEntry , or NoteEntry . |
keywords |
Optional. Keywords to search by. Entries can be searched by description. |
entry_id |
Optional. ID of a single Entry to search. |
client_id |
Optional. ID of the Patient associated with this Entry. |
Returns a list of Entry
objects.
Retrieving a Program
query entry(
$id: ID,
$type: String,
$client_id: ID
) {
entry(
id: $id,
type: $type,
client_id: $client_id
) {
id
type
category
emotions
name
description
}
}
Retrieving a specific Entry is done via the entry
query.
Input | Info |
---|---|
id |
Optional. ID of the Entry to query. |
client_id |
Optional. Used together with type . Returns the last Entry for a specific Client. |
type |
Optional. Required when using client_id . Fetches an Entry of specific type. |
Returns an Entry
object.
Creating an Entry
mutation createEntry(
$type: String,
$description: String,
$user_id: String,
$percieved_hungriness: String
) {
createEntry(input: {
type: $type,
description: $description,
user_id: $user_id,
percieved_hungriness: $percieved_hungriness
})
{
entry {
id
}
messages {
field
message
}
}
}
Entries are created via the createEntry
mutation. Let's break down the inputs that mutation accepts.
You can view a full list of potential inputs here.
Input | Info |
---|---|
type |
Optional. Valid options are
|
created_at |
Optional. Date for the new Entry. |
user_id |
Optional. ID of the Patient, otherwise the currently authenticated User will be associated. |
percieved_hungriness |
Optional. A string index of hungriness.
|
Returns a createEntryPayload
object.
Updating an Entry
mutation updateEntry(
$id: ID,
$type: String,
$description: String,
$user_id: String,
$percieved_hungriness: String
) {
updateEntry(input: {
id: $id,
type: $type,
description: $description,
user_id: $user_id,
percieved_hungriness: $percieved_hungriness
}) {
entry {
id
}
messages {
field
message
}
}
}
The updateEntry
mutation shares many common inputs with createEntry
and those inputs (e.g type
or user_id
work the same in both places).
You can view a full list of potential inputs here.
Input | Info |
---|---|
id |
Required. The ID of the Entry to update. |
Returns an updateEntryPayload
object.
Deleting an Entry
Journal Entries can be (soft) deleted by authorized providers and staff members via the deleteEntry
mutation.
You can view a full list of potential inputs here.
mutation deleteEntry($id: ID) {
deleteEntry(input: { id: $id }) {
entry {
id
}
messages {
field
message
}
}
}
The deleteEntry
mutation is called from an authenticated provider/staff account.
Input | Info |
---|---|
id |
Required. ID of the Entry to delete. |
Returns a deleteEntryPayload
object.
Metrics
Storing Metric Data
mutation createEntry (
$metric_stat: String, # e.g "182"
$category: String, # e.g "Weight"
$type: String, # "MetricEntry"
$user_id: String # e.g "61"
$created_at: String, # e.g "2021-09-23 15:27:01 -0400"
) {
createEntry (input:{
category: $category,
type: $type,
metric_stat: $metric_stat,
user_id: $user_id,
created_at: $created_at,
})
{
entry {
id
}
messages
{
field
message
}
}
}
Oftentimes, you may want to store and retrieve quantitative data (e.g weight, stress level, or A1C) on a patient.
You would do this in Healthie using Entries
, and more specifically, via a MetricEntry
type.
The most important fields when creating an Entry
are type
, category
, metric_stat
, category
, user_id
, and created_at
. Let's go over each one.
Input | Info |
---|---|
type |
Specificies what type of Entry you are creating. We offer a (growing) range of entry types (e.g FoodEntry or WorkoutEntry ). Since we storing a datapoint on the patient, the type would be MetricEntry . |
category |
Specifies what kind of metric we are storing. Healthie has built in metric categories like Weight or A1C that can be turned on or off. You can also add any metric category you'd like via our custom metrics feature. Managing these can be done via the API (by updating FeatureToggle objects), but you may just want to set this up in our UI. Here is information about managing these in our UI |
metric_stat |
This is the actual data value for the metric. e.g if a patient weights 182 lbs, you would pass in 182 with a category of Weight . |
user_id |
The ID of the patient that this entry should be attached to. |
created_at |
A timestamp that specifies when the data is from. You can backdate data, and forward-date data (up to 3 months in the future). If you don't pass this in, the entry will be created with a timestamp of the current time. |
Those fields are used in a createEntry
mutation to create a Metric Entry. Please see the right sidebar for an example.
Using the Entries Query for Metric Data
query entries(
$category: String # e.g "Weight"
$type: String # "MetricEntry"
) {
entries(
category: $category
type: $type
) {
id
}
}
2248,2252
To retrieve metric data, you can use the entries
query. The query is paginated, with a page size of 10. To see the total amount of metric data, you can use the entriesCount
query.
You can view all the argument options here but lets go over some of the most important ones below.
First, since we only want metric data (and not other type of entry data like food), we will pass in MetricEntry
to the type
argument.
Input | Info |
---|---|
category |
Lets us specifiy what category of metric we want data for (e.g just get back "Weight" or "Blood Pressure" ). If left blank, the query will return all categories. |
client_id |
ID of the Client to fetch metric Entries for. |
is_org |
When set to true , the query will instead return Entries for all clients in the organization that the authenticated user can access. |
group_id |
Whether to return Entries for clients in the specified user group. |
If you are authenticating as a patient, then client_id
, is_org
, and group_id
will have no effect on the query.
If none of the client_id
, is_org
, or group_id
fields are passed in, the query will return entries posted by clients who have the authenticated user as their primary provider or care team member.
On the right, you can find an example of a simple query that returns metric data for the authenticated user's patients.
Documents
The Document Object
{
"id": "1",
"display_name": "dummy.pdf",
"file_content_type": "application/pdf",
"owner": {
"id": "1"
}
}
Documents are Document
objects.
You can view the full list of available fields here.
Listing Documents
query documents(
$offset: Int,
$keywords: String,
$folder_id: String,
$file_types: [String],
$file_type: String,
$sort_by: String,
$private_user_id: String,
$viewable_user_id: String,
$consolidated_user_id: String,
$filter: String,
$should_paginate: Boolean,
$for_template_use: Boolean,
$provider_id: ID
) {
documents(
offset: $offset,
keywords: $keywords,
folder_id: $folder_id,
file_types: $file_types,
file_type: $file_type,
sort_by: $sort_by,
private_user_id: $private_user_id,
viewable_user_id: $viewable_user_id,
consolidated_user_id: $consolidated_user_id,
filter: $filter,
should_paginate: $should_paginate,
for_template_use: $for_template_use,
provider_id: $provider_id
) {
id
display_name
file_content_type
opens {
id
}
owner {
id
email
}
users {
id
email
}
}
}
Listing Documents is done via the documents
query.
You can view a full list of potential arguments here.
Input | Info |
---|---|
sort_by |
Optional. Valid options are:
|
keywords |
Optional. Keywords to search by. Documents can be searched by the name, description or file name. |
file_type |
Optional. Can be either: Valid options are:
|
folder_id |
Optional. ID of the Folder containing files to retrieve. |
Returns a list of Document
objects.
Retrieving a Document
query document(
$id: ID,
$course_id: ID,
$custom_module_id: ID,
$care_plan_id: ID
) {
document(
id: $id,
course_id: $course_id,
custom_module_id: $custom_module_id,
care_plan_id: $care_plan_id
) {
id
display_name
file_content_type
opens {
id
}
owner {
id
email
}
users {
id
email
}
}
}
Retrieving a specific Document is done via the document
query.
Input | Info |
---|---|
id |
ID of the Document to query. |
course_id |
Optional. Find Document by the Program it's associated with. |
custom_module_id |
Optional. Find Document by the specific Program Module it's associated with. |
care_plan_id |
Optional. Find Document by the Care Plan it's associated with. |
Returns a Document
object.
Creating a Document
mutation createDocument(
$file: Upload,
$display_name: String,
$folder_id: String
) {
createDocument(input: {
file: $file,
display_name: $display_name,
folder_id: $folder_id
}) {
document {
id
}
messages {
field
message
}
}
}
You can view a full list of potential inputs here.
Input | Info |
---|---|
file |
Required. This is the uploaded file as a multipart/form-data . Please refer to the File Uploads section for more information. |
display_name |
Optional. If not provided, will use the original file name as a display name. |
folder_id |
Optional. ID of the Folder to place the new Document in. |
Returns a createDocumentPayload
object.
Updating a Document
mutation updateDocument(
$id: ID,
$display_name: String,
$folder_id: String
) {
updateDocument(input: {
id: $id,
display_name: $display_name,
folder_id: $folder_id
}) {
document {
id
}
messages {
field
message
}
}
}
You can view a full list of potential inputs here.
The updateDocument
mutation shares many common inputs with createDocument
and those inputs (e.g display_name
or folder_id
work the same in both places).
Input | Info |
---|---|
id |
Required. The ID of the Document to update. |
Returns an updateDocumentPayload
object.
Deleting a Document
Documents can be deleted by authorized providers and staff members via the deleteDocument
mutation.
You can view a full list of potential inputs here.
mutation deleteDocument($id: ID) {
deleteDocument(input: { id: $id }) {
document {
id
}
messages {
field
message
}
}
}
The deleteDocument
mutation is called from an authenticated provider/staff account.
Input | Info |
---|---|
id |
Required. ID of the Document to delete. |
Returns a deleteDocumentPayload
object.
Tasks
You can learn more about the Tasks feature in general on Healthie's Help platform.
The Task Object
{
"id": "1800",
"client": {
"id": 300
},
"user": {
"id": "200"
},
"creator": {
"id": "100"
},
"complete": false,
"completed_at": null,
"content": "Example task content",
"created_at": "2022-01-01 09:45:20 -0400",
"priority": 1,
"position": 0,
"seen": true,
"reminder": {
"id": "1190",
"interval_type": "once",
"interval_value": "2022-01-08",
"is_enabled": true
},
"hidden": false,
"due_date": "2022-01-10 00:00:00 -0400"
}
Tasks are Task
objects.
Some of the Task properties are described below:
Field | Description |
---|---|
priority |
0 when standard priority, 1 if high priority |
client |
A Patient object related to this Task. Null if the Task is not Patient-related |
creator |
User who crated the Task. It's not always the assignee (user ) |
position |
Position of the Task on the List |
seen |
Whether the Task has been seen by the owner (user ) |
complete |
Whether the Task is complete |
You can view the full list of available fields here.
Retrieving a Task
query getTask(
$id: ID
) {
task(
id: $id
) {
id
client {
id
}
user {
id
}
creator {
id
email
}
complete
completed_at
content
created_at
priority
position
seen
reminder {
id
interval_type
interval_value
is_enabled
}
hidden
due_date
}
}
Retrieving a Task is done via the task
query.
Input | Info |
---|---|
id |
Required. ID of the Task. |
Returns a Task
object.
Listing Tasks
query tasks(
$client_id: String,
$withoutPagination: Boolean,
$type: String,
$completed_status: String,
$sort_by: String,
$keywords: String,
$show_hidden: Boolean,
$created_by_self: Boolean
) {
tasks(
client_id: $client_id
withoutPagination: $withoutPagination
type: $type
completed_status: $completed_status
sort_by: $sort_by
keywords: $keywords
show_hidden: $show_hidden
created_by_self: $created_by_self
) {
id
client {
id
}
user {
id
}
creator {
id
email
}
complete
completed_at
content
created_at
priority
position
seen
reminder {
id
interval_type
interval_value
is_enabled
}
hidden
due_date
}
}
Listing Entries is done via the tasks
query.
You can view a full list of potential arguments here.
Input | Info |
---|---|
client_id |
Optional. ID of the Patient associated with the Task. |
withoutPagination |
Optional. Keywords to search by. Entries can be searched by description. |
type |
Optional. Possible values are:
|
completed_status |
Optional.
|
created_by_self |
Optional. If set to true , will return only Tasks created by the current User. |
show_hidden |
Optional. If set to true , will query hidden tasks. |
keywords |
Optional. Keywords to search Tasks by. Tasks can be searcheds by the content field. |
sort_by |
Optional. Possible values are:
|
Returns a list of Task
objects.
Creating a Task
mutation createTask(
$content: String,
$user_id: String,
$priority: Int,
$client_id: String,
$due_date: String,
$reminder: TaskReminderInput
) {
createTask(input: {
content: $content,
user_id: $user_id,
priority: $priority,
client_id: $client_id,
due_date: $due_date,
reminder: $reminder
}) {
task {
id
}
messages {
field
message
}
}
}
Tasks are created via the createTask
mutation. Let's break down the inputs that this mutation accepts.
You can view a full list of potential inputs here.
Input | Info |
---|---|
content |
Content of the Task |
user_id |
Optional. ID of the User to assign the Task to. If none provided, will assign the task to the current User |
priority |
Optional. Possible values are:
|
client_id |
Optional. ID of the Patient related to this Task |
complete |
Optional. Set to true if the Task is already complete |
due_date |
Optional. A due date of the Task |
reminder |
Optional. A configuration object for the reminder. See descriptions below |
reminder.is_enabled |
Set to true to enable the reminder, false otherwise |
reminder.reminder_time |
Time, expressed in number of minutes from midnight, to trigger the reminder at |
reminder.interval_type |
Frequency of the reminder. Possible options are:
|
reminder.interval_value |
Date when to trigger the reminder |
Returns a createTaskPayload
object.
Updating a Task
mutation updateTask(
$id: String,
$position: Int,
$content: String,
$user_id: String,
$priority: Int,
$client_id: String,
$due_date: String,
$reminder: TaskReminderInput
) {
updateTask(input: {
id: $id,
position: $position,
content: $content,
user_id: $user_id,
priority: $priority,
client_id: $client_id,
due_date: $due_date,
reminder: $reminder
}) {
task {
id
}
messages {
field
message
}
}
}
The updateTask
mutation shares many common inputs with createTask
and those inputs (e.g due_date
or priority
work the same in both places).
You can view a full list of potential inputs here.
Input | Info |
---|---|
id |
Required. The ID of the Task to update. |
position |
Optional. Set the position of the Task on the list. |
Returns an updateTaskPayload
object.
Marking all Tasks as seen
mutation markTasksAsSeen(
$seen: Boolean,
$update_all_tasks: Boolean
) {
updateTasksBulk(input: {
seen: $seen,
update_all_tasks: $update_all_tasks
}) {
messages {
message
}
}
}
You can programmatically mark all Tasks of the current User as seen by using the updateTasksBulk
mutation.
You can view a full list of potential inputs here.
Input | Info |
---|---|
seen |
Set to true to mark Tasks as seen by the current User, false to mark as unseen. |
update_all_tasks |
Boolean indicating whether all User Tasks should be updated. Set to true . |
Returns an bulkUpdateTasksPayload
object.
Deleting a Task
Tasks can be deleted via the deleteTask
mutation.
You can view a full list of potential inputs here.
mutation deleteTask($id: ID) {
deleteTask(input: { id: $id }) {
task {
id
}
messages {
field
message
}
}
}
The deleteTask
mutation is called from an authenticated provider/staff account.
Input | Info |
---|---|
id |
Required. ID of the Task to delete. |
Returns a deleteTaskPayload
object.
Care Plans
For more information on the Care Plans feature in general, view our help articles here.
The Care Plan object
{
"id": "1",
"name": "Care Plan 1",
"description": "Example description",
"recommendations": [
{
"id": "1",
"recommendation_body": "<p>Drink 8 glasses of water a day</p>",
"recommendation_type": "Nutrition",
"sanitized_body": "Drink 8 glasses of water a day"
}
],
"is_active": true,
"is_hidden": false
}
Care Plans are CarePlan
objects.
You can view the full list of available fields here.
Listing Care Plans
query carePlans(
$patient_id: ID,
$offset: Int,
$sort_by: String,
$templates_only: Boolean,
$template_search_keywords: String
) {
carePlans(
patient_id: $patient_id,
offset: $offset,
sort_by: $sort_by,
templates_only: $templates_only,
template_search_keywords: $template_search_keywords
) {
id
name
patient {
id
}
}
}
Listing Care Plans is done via the carePlans
query.
You can view a full list of potential arguments here.
Input | Info |
---|---|
sort_by |
Optional. Valid options are:
|
patient_id |
Optional. ID of the Client that is associated with the Care Plan. |
template_search_keywords |
Optional. Can be searched by name. |
Returns a list of Care Plan objects.
Retrieving a Care Plan
query carePlan(
$id: ID
) {
carePlan(
id: $id
) {
id
name
patient {
id
}
}
}
Retrieving a specific Care Plan is done via the carePlan
query.
Input | Info |
---|---|
id |
ID of the Care Plan to query. |
Returns a Care Plan object.
Creating a Care Plan
mutation createCarePlan(
$name: String,
$patient_id: ID,
$is_hidden: Boolean
) {
createCarePlan(input: {
$name: String,
$patient_id: ID,
$is_hidden: Boolean,
}) {
carePlan {
id
}
messages {
field
message
}
}
}
You can view a full list of potential inputs here.
Input | Info |
---|---|
name |
Required. Name of the care plan. Not visible to Patients. |
patient_id |
Optional. ID of the Patient to create a Care Plan for. |
is_hidden |
Optional. Whether the Care Plan should be hidden. |
Returns a createCarePlanPayload
object.
Updating a Care Plan
mutation updateCarePlan(
$id: ID,
$name: String,
$description: String,
$new_patient_id: ID,
$is_template: Boolean,
$is_hidden: Boolean
) {
updateCarePlan(input: {
id: $id,
name: $name,
description: $description,
new_patient_id: $new_patient_id,
is_template: $is_template,
is_hidden: $is_hidden
}) {
carePlan {
id
}
messages {
field
message
}
}
}
You can view a full list of potential inputs here.
Input | Info |
---|---|
id |
Required. The ID of the Care Plan to update. |
description |
Optional. A description visible to Patients. |
new_patient_id |
Optional. Provide a new Patient ID to move the Care Plan to another Patient. |
is_template |
Optional. Set to true in order to created a Care Plan template. Should be used without a patient_id . |
Returns an updateCarePlanPayload
object.
Deleting a Care Plan
Care Plans can be deleted by authorized providers and staff members via the deleteCarePlan
mutation.
You can view a full list of potential inputs here.
mutation deleteCarePlan($id: ID) {
deleteCarePlan(input: { id: $id }) {
carePlan {
id
}
messages {
field
message
}
}
}
Input | Info |
---|---|
id |
Required. ID of the Care Plan to delete. |
Returns a deleteCarePlanPayload
object.
Goals
For more information on the Goals feature in general, view our help articles here.
The Goal object
{
"id": "1",
"name": "Download e-book on mindful eating"
}
Goals are Goal
objects.
You can view the full list of available fields here.
Listing Goals
query goals(
$user_id: ID,
$sort_by: String,
$keywords: String,
$status_filter: String,
$frequency_filter: String
) {
goals(
user_id: $user_id,
sort_by: $sort_by,
keywords: $keywords,
status_filter: $status_filter,
frequency_filter: $frequency_filter
) {
id
name
}
}
Listing Goals is done via the goals
query.
You can view a full list of potential arguments here.
Input | Info |
---|---|
sort_by |
Optional. Valid options are:
|
user_id |
Optional. ID of the Patient that is associated with the Goal. |
keywords |
Optional. Can be searched by name or description. |
status_filter |
Optional. Filters Goals by the completeness. Can be complete or not_complete . |
frequency_filter |
Optional. Filters Goals by the frequency. Can be daily , weekly , or one_time . |
Returns a list of Goal objects.
Retrieving a Goal
query goal(
$id: ID,
$program_goal: Boolean,
$client_id: ID,
$last_client_goal: Boolean
) {
goal(
id: $id,
program_goal: $program_goal,
client_id: $client_id,
last_client_goal: $last_client_goal
) {
id
name
}
}
Retrieving a specific Goal is done via the goal
query.
Input | Info |
---|---|
id |
Optional when using client_id and last_client_goal . ID of the Goal to query. |
client_id |
Optional. Required when using last_client_goal . ID of the Patient associated with the Goal. |
last_client_goal |
Optional. If set to true , will return the last Goal for the Patient specified in client_goal . |
Returns a Goal object.
Creating a Goal
mutation createGoal(
$name: String,
$user_id: ID,
$program_goal: Boolean
$repeat: String,
$due_date: String,
$start_on: String,
$reminder: ReminderInput,
$care_plan_id: ID
) {
createGoal(input: {
name: $name,
user_id: $user_id,
program_goal: $program_goal,
repeat: $repeat,
due_date: $due_date,
start_on: $start_on,
reminder: $reminder,
care_plan_id: $care_plan_id
}) {
goal {
id
}
messages {
field
message
}
}
}
You can view a full list of potential inputs here.
Input | Info |
---|---|
name |
Required. Name of the Goal. |
user_id |
Optional. ID of the Patient to create the Goal for. |
program_goal |
Optional. Set to true to create the Goal for a Program. |
repeat |
Required. The frequency of this Goal. Possible values are:
|
due_date |
Optional. The date the Goal should end. Format should be: yyyy-mm-dd . |
start_on |
Optional. The date the Goal should start. Format should be: yyyy-mm-dd . |
reminder |
Optional. An input of type ReminderInput to set a reminder for this Goal. |
care_plan_id |
Optional. If provided, the Goal will be associated with a Care Plan. |
Returns a createGoalPayload
object.
Updating a Goal
mutation updateGoal(
$id: ID,
$name: String,
$user_id: ID,
$program_goal: Boolean
$repeat: String,
$due_date: String,
$start_on: String,
$reminder: ReminderInput
) {
updateGoal(input: {
id: $id,
name: $name,
user_id: $user_id,
program_goal: $program_goal,
repeat: $repeat,
due_date: $due_date,
start_on: $start_on,
reminder: $reminder
}) {
goal {
id
}
messages {
field
message
}
}
}
The updateGoal
has a very similar behavior to createGoal
mutation.
You can view a full list of potential inputs here.
Input | Info |
---|---|
id |
Required. The ID of the Goal to update. |
Returns an updateGoalPayload
object.
Deleting a Goal
Goals can be deleted by authorized providers and staff members via the deleteGoal
mutation.
You can view a full list of potential inputs here.
mutation deleteGoal($id: ID) {
deleteGoal(input: { id: $id }) {
goal {
id
}
messages {
field
message
}
}
}
Input | Info |
---|---|
id |
Required. ID of the Goal to delete. |
Returns a deleteGoalPayload
object.
The Goal History object
{
"id": "12345",
"goal": {
"id": "112233",
"name": "Drink more water",
"repeat": "Daily"
},
"completed_on": "2023-01-01"
}
Goal Hitstories are GoalHistory
objects.
You can view the full list of available fields here.
Listing Goal Histories
query goalHistories(
$category: String,
$include_subgoals: Boolean,
$offset: Int,
$should_paginate: Boolean,
$unique: Boolean,
$user_id: ID
) {
goalHistories(
category: $category,
include_subgoals: $include_subgoals,
offset: $offset,
should_paginate: $should_paginate,
unique: $unique,
user_id: $user_id
) {
id
name
goal {
id
name
repeat
}
completed_on
}
}
Listing Goal Histories is done via the goalHistories
query.
You can view a full list of potential arguments here.
Input | Info |
---|---|
category |
Optional. The frequency of the Goal. Valid options are:
|
include_subgoals |
Optional. Whether to include subgoals. |
user_id |
Optional. ID of the Patient that is associated with the Goal History. |
unique |
Optional. If set to true , will only return a single Goal History per Goal, while false will return all Goal Histories. |
Returns a list of GoalHistory objects.
Insurance
Please refer to Healthie's Help Portal for a general information about the Insurance feature.
Policies
The Policy object
{
"id": "1",
"name_and_id": "ExampleHealth - A1B2C3",
"is_accepted": true, // whether this Policy is accepted within the organization
"payer_id": "A1B2C3",
"payer_name": "ExampleHealth"
}
Within the API, Insurance Policies are InsurancePlan
objects.
You can view the full list of available fields here.
Listing Policies
query insurancePlans(
$ids: String,
$keywords: String,
$is_accepted: Boolean,
$sort_by: String
) {
insurancePlans(
ids: $ids,
keywords: $keywords,
is_accepted: $is_accepted,
sort_by: $sort_by
) {
id
name_and_id
payer_id
payer_name
}
}
Listing Policies is done via the insurancePlans
query.
You can view a full list of potential arguments here.
Input | Info |
---|---|
ids |
Optional. Array of plan IDs to fetch. |
keywords |
Optional. Can be searched by Payer name or ID. |
is_accepted |
Optional. Set to true to fetch only Accepted Policies. |
sort_by |
Optional. Valid options are:
|
Returns a list of InsurancePlan objects.
Accepting a Policy
mutation createAcceptedInsurancePlan(
$insurance_plan_ids: [ID]
) {
createAcceptedInsurancePlan(input: {
insurance_plan_ids: $insurance_plan_ids
}) {
accepted_insurance_plans {
id
insurance_plan {
id
}
}
messages {
field
message
}
}
}
You can view a full list of potential inputs here.
Input | Info |
---|---|
insurance_plan_ids |
Required. IDs of the Insurance Policies to accept. |
Returns a createAcceptedInsurancePlanPayload
object.
Removing a Policy from the list of accepted Policies
mutation deleteAcceptedInsurancePlan(
$id: ID
) {
deleteAcceptedInsurancePlan(input: {
id: $id
}) {
accepted_insurance_plan {
id
insurance_plan {
id
}
}
messages {
field
message
}
}
}
You can view a full list of potential inputs here.
Input | Info |
---|---|
id |
Required. The ID of the Policy to remove from the list of accepted Policies. |
Returns an deleteAcceptedInsurancePlanPayload
object.
Superbills
The Superbill object
{
"id": "1",
"amount_paid": "1000.00",
"balance_due": "500.00",
"provider": {
"id": "1"
},
"patient": {
"id": "2"
}
}
Superbills are SuperBill
objects.
You can view the full list of available fields here.
Listing Superbills
query superBills(
$keywords: String,
$sort_by: String,
$provider_id: ID,
$client_id: ID,
$status: String
) {
superBills(
keywords: $keywords,
sort_by: $sort_by,
provider_id: $provider_id,
client_id: $client_id,
status: $status
) {
id
amount_paid
balance_due
total_fee
status
}
}
Listing Superbills is done via the superBills
query.
You can view a full list of potential arguments here.
Input | Info |
---|---|
sort_by |
Optional. Valid options are:
|
keywords |
Optional. Can be searched by Superbill status, Patient first and last name, or Provider first and last name. |
client_id |
Optional. ID of the Patient that is associated with the Superbill. |
provider_id |
Optional. ID of the Provider that is associated with the Superbill. |
status |
Optional. Return only Superbills that have one of the following statuses:
|
Returns a list of Superbill objects.
Retrieving a Superbill
query superBill(
$id: ID
) {
superBill(
id: $id
) {
id
name
patient {
id
}
}
}
Retrieving a specific Superbill is done via the superBill
query.
Input | Info |
---|---|
id |
ID of the Superbill to query. |
Returns a Superbill object.
Creating a Superbill
mutation createSuperBill(
$patient_id: ID,
$patient_dob: String,
$dietetitian_id: ID,
$provider_name: String,
$service_date: String,
$amount_paid: String,
$status: String,
icd_codes_super_bills: [IcdCodesSuperBillInput]
cpt_codes_super_bills: [CptCodesSuperBillInput],
$receipt_line_items: [ReceiptLineItemInput]
) {
createSuperBill(input: {
patient_id: $patient_id,
patient_dob: $patient_dob,
dietetitian_id: $dietetitian_id,
provider_name: $provider_name,
service_date: $service_date,
amount_paid: $amount_paid,
status: $status,
icd_codes_super_bills: $icd_codes_super_bills,
cpt_codes_super_bills: $cpt_codes_super_bills,
receipt_line_items: $receipt_line_items
}) {
superBill {
id
}
messages {
field
message
}
}
}
You can view a full list of potential inputs here.
Input | Info |
---|---|
patient_id |
Required. ID of the Patient to create the Superbill for. |
patient_dob |
Required. Date of birth of the Patient. Format: YYYY-MM-DD . |
dietetitian_id |
Required. ID of the Provider. |
provider_name |
Required. Name of the Provider. |
service_date |
Required. Service date. Format: YYYY-MM-DD . |
icd_codes_super_bills |
Optional. An array of IcdCodesSuperBillInput input objects. |
cpt_codes_super_bills |
Optional. An array of CptCodesSuperBillInput input objects. |
receipt_line_items |
Optional. An array of ReceiptLineItemInput input objects. |
Returns a createSuperBillPayload
object.
Updating a Superbill
mutation updateSuperBill(
$id: ID,
$patient_id: ID,
$patient_dob: String,
$dietetitian_id: ID,
$provider_name: String,
$service_date: String,
$amount_paid: String,
$status: String,
icd_codes_super_bills: [IcdCodesSuperBillInput]
cpt_codes_super_bills: [CptCodesSuperBillInput],
$receipt_line_items: [ReceiptLineItemInput],
$should_email_to_client: Boolean
) {
updateSuperBill(input: {
id: $id,
patient_id: $patient_id,
patient_dob: $patient_dob,
dietetitian_id: $dietetitian_id,
provider_name: $provider_name,
service_date: $service_date,
amount_paid: $amount_paid,
status: $status,
icd_codes_super_bills: $icd_codes_super_bills,
cpt_codes_super_bills: $cpt_codes_super_bills,
receipt_line_items: $receipt_line_items,
should_email_to_client: $should_email_to_client
}) {
superBill {
id
}
messages {
field
message
}
}
}
The updateSuperBill
mutation shares many common inputs with createSuperBill
and those inputs (e.g service_date
or receipt_line_items
work the same in both places).
You can view a full list of potential inputs here.
Input | Info |
---|---|
id |
Required. The ID of the Superbill to update. |
should_email_to_client |
Optional. Set to true in order to resend the email to the associated Patient. |
Returns an updateSuperBillPayload
object.
Deleting a Superbill
Superbills can be deleted by authorized providers and staff members via the deleteSuperBill
mutation.
You can view a full list of potential inputs here.
mutation deleteSuperBill($id: ID) {
deleteSuperBill(input: { id: $id }) {
superBill {
id
}
messages {
field
message
}
}
}
Input | Info |
---|---|
id |
Required. ID of the Superbill to delete. |
Returns a deleteSuperBillPayload
object.
CMS1500s
Please refer to Healthie's Help Portal for a general information about CMS 1500 claims.
The CMS1500 object
{
"id": "1",
"amount_paid": "1500.00",
"amount_reimbursed": "1000.00",
"status": "Partially Paid",
"patient": {
"id": "2"
}
}
CMS 1500 Claims are Cms1500
objects.
You can view the full list of available fields here.
Listing CMS1500s
query cms1500s(
$keywords: String,
$sort_by: String,
$status: String,
$client_id: ID,
$provider_id: ID,
) {
cms1500s(
keywords: $keywords,
sort_by: $sort_by,
status: $status,
client_id: $client_id,
provider_id: $provider_id
) {
id
name
patient {
id
}
}
}
Listing CMS1500s is done via the cms1500s
query.
You can view a full list of potential arguments here.
Input | Info |
---|---|
sort_by |
Optional. Valid options are:
|
client_id |
Optional. ID of the Patient that is associated with the CMS1500. |
provider_id |
Optional. ID of the Provider. |
keywords |
Optional. Can be searched by status, Patient first and last name, Provider first and last name, Payer name or Payer ID. |
status |
Optional. Valid options are:
|
Returns a list of Cms1500 objects.
Retrieving a CMS1500
query cms1500(
$id: ID
) {
cms1500(
id: $id
) {
id
name
patient {
id
}
}
}
Retrieving a specific CMS1500 is done via the cms1500
query.
Input | Info |
---|---|
id |
ID of the CMS1500 to query. |
Returns a Cms1500 object.
Creating a CMS1500
mutation createCms1500(
$patient: PatientInput,
$dietitian: DietitianInput,
$service_location_id: String,
$amount_paid: String,
$cms1500_policies: [Cms1500PolicyInput!],
$icd_codes_cms1500s: [IcdCodesCms1500Input!]
$cpt_codes_cms1500s: [CptCodesCms1500Input!],
$client_sig_on_file: Boolean
) {
createCms1500(input: {
patient: $patient,
dietitian: $dietitian,
service_location_id: $service_location_id,
amount_paid: $amount_paid,
cms1500_policies: $cms1500_policies,
icd_codes_cms1500s: $icd_codes_cms1500s,
cpt_codes_cms1500s: $cpt_codes_cms1500s,
client_sig_on_file: $client_sig_on_file
}) {
cms1500 {
id
}
messages {
field
message
}
}
}
You can view a full list of potential inputs here.
Input | Info |
---|---|
patient |
Required. A PatientInput object to specify a Patient. Note that the PatientInput MUST include a LocationInput object. |
dietitian |
Required. A DietitianInput object to specify the provider. |
service_location_id |
Required The ID of the location where the patient received service. |
cms1500_policies |
Required An Array of Cms1500PolicyInput objects. Can be an empty array. |
amount_paid |
Optional. Amount paid. |
Returns a createCms1500Payload
object.
Updating a CMS1500
mutation updateCms1500(
$id: ID,
$patient: PatientInput,
$dietitian: DietitianInput,
$service_location_id: String,
$amount_paid: String,
$cms1500_policies: [Cms1500PolicyInput!],
$icd_codes_cms1500s: [IcdCodesCms1500Input!]
$cpt_codes_cms1500s: [CptCodesCms1500Input!],
$client_sig_on_file: Boolean
) {
updateCms1500(input: {
id: $id,
patient: $patient,
dietitian: $dietitian,
service_location_id: $service_location_id,
amount_paid: $amount_paid,
cms1500_policies: $cms1500_policies,
icd_codes_cms1500s: $icd_codes_cms1500s,
cpt_codes_cms1500s: $cpt_codes_cms1500s,
client_sig_on_file: $client_sig_on_file
}) {
cms1500 {
id
}
messages {
field
message
}
}
}
The updateCms1500
mutation shares many common inputs with createCms1500
and those inputs (e.g service_date
or receipt_line_items
work the same in both places).
You can view a full list of potential inputs here.
Input | Info |
---|---|
id |
Required. The ID of the CMS1500 to update. |
Returns an updateCms1500Payload
object.
Deleting a CMS1500
CMS1500s can be deleted by authorized providers and staff members via the deleteCms1500
mutation.
You can view a full list of potential inputs here.
mutation deleteCms1500($id: ID) {
deleteCms1500(input: { id: $id }) {
cms1500 {
id
}
messages {
field
message
}
}
}
Input | Info |
---|---|
id |
Required. ID of the CMS1500 to delete. |
Returns a deleteCms1500Payload
object.
Medications
For more information on the Medication History feature in general, visit our help center.
The Medication object
{
"id": "232132",
"active": true,
"comment": "Only when necessary",
"directions": "Inhale and hold",
"dosage": "55 mcg/inh",
"name": "Nasal spray"
}
Medications are Medication
objects.
You can view the full list of available fields here.
Listing Medications
query medications(
$active: Boolean,
$patient_id: ID
) {
medications(
active: $active,
patient_id: $patient_id
) {
id
name
active
directions
dosage
code
start_date
end_date
}
}
Listing Medications is done via the medications
query.
You can view a full list of potential arguments here.
Input | Info |
---|---|
active |
Optional. Fetch only active Medications. |
patient_id |
Optional. ID of the Patient to fetch Medications for. If omitted, will return Medications for the current User. |
Returns a list of Medication objects.
Creating a Medication
mutation createMedication(
$active: Boolean,
$comment: String,
$directions: String,
$dosage: String,
$end_date: String,
$name: String,
$start_date: String,
$user_id: String
) {
createMedication(input: {
active: $active,
comment: $comment,
directions: $directions,
dosage: $dosage,
end_date: $end_date,
name: $name,
start_date: $start_date,
user_id: $user_id
}) {
medication {
id
name
dosage
comment
}
messages {
field
message
}
}
}
To add a Medication to a Patient, use createMedication
mutation.
You can view a full list of potential inputs here.
Input | Info |
---|---|
user_id |
Required. ID of the Patient. |
name |
Required. Name of the Medication. |
active |
Optional. Whether the Patient still takes the Medication. |
dosage |
Optional. Medication dosage. |
comment |
Optional. Additional comment. |
start_date |
Optional. Start date of taking the Medication. For example: July 16, 2023 . |
end_date |
Optional. End date of taking the Medication. For example: July 16, 2023 . Does not apply if active is set to true . |
Returns a createMedicationPayload
object.
Updating a Medication
mutation updateMedication(
$id: ID,
$active: Boolean,
$comment: String,
$directions: String,
$dosage: String,
$end_date: String,
$name: String,
$start_date: String
) {
updateMedication(input: {
id: $id,
active: $active,
comment: $comment,
directions: $directions,
dosage: $dosage,
end_date: $end_date,
name: $name,
start_date: $start_date
}) {
medication {
id
name
dosage
comment
}
messages {
field
message
}
}
}
The updateMedication
has a very similar behavior to createMedication
mutation.
You can view a full list of potential inputs here.
Input | Info |
---|---|
id |
Required. The ID of the Medication to update. |
Returns an updateMedicationPayload
object.
Deleting a Medication
Medications can be deleted via the deleteMedication
mutation.
You can view a full list of potential inputs here.
mutation deleteMedication($id: ID) {
deleteMedication(input: { id: $id }) {
medication {
id
}
messages {
field
message
}
}
}
Input | Info |
---|---|
id |
Required. ID of the Medication to delete. |
Returns a destroyMedicationPayload
object.
Lab Orders
For more information on the Lab Orders feature in general, view our help articles here.
The Lab Order object
{
"id": "334455",
"status": "completed",
"lab": "Example test",
"lab_company": "ACME Labs",
"integration": "lab_testing_api",
"normalized_status": "completed"
}
Lab Orders are LabOrder
objects in Healthie.
You can view the full list of available fields here.
Listing Lab Orders
query labOrders(
$client_filter: String,
$client_id: ID,
$keywords: String,
$lab_filter: String,
$provider_filter: String,
$recent_orders: Boolean,
$offset: Int,
$sort_by: String,
$status_filter: String
) {
labOrders(
client_filter: $client_filter,
client_id: $client_id,
keywords: $keywords,
lab_filter: $lab_filter,
provider_filter: $provider_filter,
recent_orders: $recent_orders,
offset: $offset,
sort_by: $sort_by,
status_filter: $status_filter
) {
id
integration
lab
lab_company
patient {
id
full_name
}
orderer {
id
full_name
}
status
normalized_status
test_date
lab_results {
id
lab_observation_requests {
id
lab_analyte
lab_observation_results {
id
units
reference_range
qualitative_result
quantitative_result
abnormal_flag
}
}
}
}
}
Listing Lab Orders is done via the labOrders
query.
You can view a full list of potential arguments here.
Input | Info |
---|---|
client_id |
Optional. ID of the Patient to filter by. |
client_filter |
Optional. A comma-separeted list of Patient IDs to filter Lab Orders by. |
lab_filter |
Optional. A comma-separated list of Labs to filter the results by. You can obtain a list of possible values by using the labFiltersData query. |
keywords |
Optional. Can be searched by Patient's or Orderer's first and last name, as well as ID, lab or status. |
provider_filter |
Optional. A comma-separeted list of Provider IDs to filter Lab Orders by. |
status_filter |
Optional. Filters Labs by status. |
recent_orders |
Optional. If provided, will return 10 most recent Lab Orders. |
sort_by |
Optional. Valid options are:
|
Returns a list of LabOrder
objects.
Retrieving a Lab Order
query labOrder(
$id: ID,
) {
labOrder(
id: $id
) {
id
integration
lab
lab_company
patient {
id
full_name
}
orderer {
id
full_name
}
status
normalized_status
test_date
lab_results {
id
lab_observation_requests {
id
lab_analyte
lab_observation_results {
id
units
reference_range
qualitative_result
quantitative_result
abnormal_flag
}
}
}
}
}
Retrieving a specific Lab Order is done via the labOrder
query.
Input | Info |
---|---|
id |
ID of the Lab Order to query. |
Returns a LabOrder
object.
Creating a Lab Order
mutation createLabOrder(
$document_id: ID,
$lab: String,
$lab_company: String,
$orderer_id: ID,
$patient_id: ID
) {
createLabOrder(input: {
document_id: $document_id,
lab: $lab,
lab_company: $lab_company,
orderer_id: $orderer_id,
patient_id: $patient_id
}) {
labOrder {
id
}
messages {
field
message
}
}
}
You can view a full list of potential inputs here.
Input | Info |
---|---|
document_id |
Required. The ID of the document containing the lab results. Needs to be pre-created and have a rel_user_id matching the patient ID. |
lab |
Required. The Lab panel that was ordered. |
lab_company |
Required. The Lab Company who processed the Order. |
orderer_id |
Required. The ID of the User in Healthie who ordered the Lab. |
patient_id |
Required. The ID of the Patient. |
Returns a createLabOrderPayload
object.
Creating a Rupa Lab Order
mutation createRupaOrder(
$document_id: ID,
$lab: String,
$lab_company: String,
$orderer_id: ID,
$patient_id: ID
) {
createRupaOrder(input: {
document_id: $document_id,
lab: $lab,
lab_company: $lab_company,
orderer_id: $orderer_id,
patient_id: $patient_id
}) {
rupa_order_url
messages {
field
message
}
}
}
You can view a full list of potential inputs here.
Input | Info |
---|---|
client_id |
Required. ID of the Patient to create the Order for. |
Returns a createRupaOrderPayload
object.
The rupa_order_url
field returned is the URL to which the user should be redirected (or opened in a modal) in order to proceed on the Rupa side.
Updating a Lab Order
mutation updateLabOrder(
$id: ID,
$appt_confirmation_code: String,
$patient_id: ID
) {
updateLabOrder(input: {
id: $id,
appt_confirmation_code: $appt_confirmation_code,
patient_id: $patient_id
}) {
labOrder {
id
}
messages {
field
message
}
}
}
You can view a full list of potential inputs here.
Input | Info |
---|---|
id |
Required. The ID of the Lab Order to update. |
appt_confirmation_code |
Optional. Confirmation code for the Lab Appointment. |
patient_id |
Required. The ID of the Patient. |
Returns an updateLabOrderPayload
object.
Syncing with Google/Outlook
Healthie supports a 2-way calendar sync with Google Calendar and Outlook, as well as a 1-way ICS calendar feed (to be used with iCal).
You can read information about the capabilities here
To enable Google/Outlook sync, the provider will need to authorize the sync via OAuth. By default, Healthie has its own verified OAuth apps with both Google and Outlook. Due to the nature of OAuth, utilizing those will ask the provider if they want to allow "Healthie" access to their calendar data, and redirect them back to a Healthie-controlled URL.
Here are the scopes that our OAuth apps request
Service | Scopes |
---|---|
Google Calendar | "email", "calendar", "userinfo.profile" |
Outlook | 'openid', 'email', 'offline_access', 'https://graph.microsoft.com/Calendars.ReadWrite' |
If you want to brand the OAuth connection (or have more direct control), you have a couple of options. Both involve your company creating and maintaing OAuth apps with Google Calendar and/or Outlook. You create the application, and provide Healthie with a Client ID and Secret for your apps.
For either option, our support team needs to make configuration changes to your account. Please reach out to us if you'd like to do either of the options.
1) Healthie-controlled redirect flow (Full Web Whitelabel Required)
If you have a full web whitelabel, Healthie can replace the links to our authorized OAuth apps with yours. Your users will be able to follow the same steps mentioned in the help article above to connect. However, they will be taken to authorize your OAuth app, and then be redirected back to your whitelabeled URL after. Healthie maintains the sync from there, and no development work is needed on your end.
2) Via the API (You handle the redirect/authorization flow)
If you do not have a full web whitelabel, or want to handle the connection without sending users to the (branded or otherwise) Healthie platform, you can use the API.
You handle the OAuth authorization flow and redirection yourself, fully outside of Healthie. Once you have the user's authorize and refresh tokens, you can then make an API call to Healthie to store their calendar sync information. We then maintain the sync from there.
Automation Examples
Below we describe a set of examples how to leverage Healthie API Webhooks to drive common Clinical Operations. We will focus on Healthie's Task platform and fetching Resource data from the API.
Metric value is out of range
In this example we are going to implement an automation script that assign a Task to Provider every time a Patient weighs themselves and the measured body weight is over a specific threshold. We are going to use 220
lbs as an example limit, however you should adjust it to your business needs.
Steps necessary to implement this automation:
- listen to a
metric_entry.created
webhook - fetch the Entry from API using the received
resource_id
key - verify if the Metric is of the correct category (
Weight
) - if Metric value is above
220
, send a mutation to create a new Task
Implementation
{
"resource_id": "555",
"resource_id_type": "Entry",
"event_type": "metric_entry.created"
}
The webhook payload may look like the JSON on the right side.
There was a new Metric created with an ID 555
.
query getEntry($id: ID) {
entry(id: $id) {
id
type
category
metric_stat
third_party_source
poster {
id
full_name
dietitian_id
}
added_by_user {
id
full_name
is_patient
dietitian_id
}
}
}
Variables (replace with the
resource_id
from webhook payload):
{
"id": "555"
}
Now, let's query the Healthie API to retrieve information about the Metric.
{
"data": {
"entry": {
"id": "555",
"type": "MetricEntry",
"category": "Weight",
"metric_stat": 250,
"third_party_source": null,
"poster": {
"id": "2",
"full_name": "John Doe",
"dietitian_id": "1"
},
"added_by_user": {
"id": "2",
"full_name": "John Doe",
"dietitian_id": "1",
"is_patient": true
}
}
}
}
The response should look like this.
You may want to check the value of added_by_user.is_patient
in order to determine if the Metric was added by the Patient and not by the Provider.
If necessary, perform additional checks, e.g. if the Metric was added from a third-party source, like Apple Health (third_party_source
field).
In our example, the body weight is 250 lbs, which 30 lbs above the safe threshold.
mutation createTask($input: createTaskInput!) {
createTask(input: $input) {
task {
id
content
}
}
}
Input:
{
"content": "Follow up to double check the body weight excessed by 30 lbs",
"user_id": "1", // Provider ID
"client_id": "2" // Patient ID
}
Once performing all checks and confirming that the metric_stat
is out of the safe range (250 > 220
), we can run our mutation.
Replace the following input variables with data from the response:
Input | Response variable |
---|---|
user_id |
poster.dietitian_id |
client_id |
poster.id |
This mutation will create a new Task for the Provider and associate it with the Patient.
Please refer to Tasks documentation to learn more about additional options like reminders and due dates.
Client completed an Intake Form
The following example demonstrates how to listen to Intake Form submission events and create a new Task for a Provider based on the Form answer. Let's say that we want to send a thank-you gift every time a person refers a Client.
Steps necessary to implement this automation:
- listen to a
form_answer_group.created
webhook - fetch the Form Answer Group from API using the received
resource_id
key - verify if this is a Referral-related Form
- if the Form contains an answer with the name of the referrer, create a Task to send a thank-you gift
Implementation
{
"resource_id": "123",
"resource_id_type": "FormAnswerGroup",
"event_type": "form_answer_group.created"
}
The webhook payload may look like the JSON on the right side.
query getFormAnswerGroups($id: ID) {
formAnswerGroup(id: $id) {
id
finished
user {
id
dietitian_id
}
custom_module_form {
id
}
form_answers {
id
label
answer
}
}
}
Variables (replace with the
resource_id
from webhook payload):
{
"id": "123"
}
Now, let's query the Healthie API to retrieve Form answers.
{
"data": {
"formAnswerGroup": {
"id": "123",
"finished": true,
"user": {
"id": "200",
"dietitian_id": "100"
},
"custom_module_form": {
"id": "50"
},
"form_answers": [
{
"id": "640",
"label": "Please enter some information about the person who referred you.",
"answer": ""
},
{
"id": "641",
"label": "Name",
"answer": "Chris"
}
]
}
}
}
The response should look like this.
First, check if custom_module_form.id
is equal to the ID of the referral form. We can ignore the webhook otherwise.
Next, find the Name
question answer in the form_answers
array. The answer
property contains the user input.
mutation createTask($input: createTaskInput!) {
createTask(input: $input) {
task {
id
content
}
}
}
Input:
{
"content": "Send a thank-you gift to Chris",
"user_id": "100", // Provider ID
"client_id": "200" // Patient ID
}
Now let's create a task for the Provider.
Replace the following input variables with data from the response:
Input | Response variable |
---|---|
user_id |
user.dietitian_id |
client_id |
user.id |
This mutation will create a new Task for the Provider and associate it with the Patient.
Please refer to Tasks documentation to learn more about additional options like reminders and due dates.
Clinician has signed a Chart Note
The following example demonstrates how to listen to form_answer_group.locked
events and create a Task for another Organization Member to submit a CMS 1500 Claim. We assume that Biller is a designated person in your Organization and you can hardcode their User ID in the webhook handler.
Steps necessary to implement this automation:
- listen to a
form_answer_group.locked
webhook - query the Healthie API to get the information about the related Patient
- create a task for the Biller to submit a CMS 1500 Claim
Implementation
{
"resource_id": "123",
"resource_id_type": "FormAnswerGroup",
"event_type": "form_answer_group.locked"
}
The webhook payload may look like the JSON on the right side.
This event indicates that a Form has been signed and locked.
query getFormAnswerGroups($id: ID) {
formAnswerGroup(id: $id) {
id
user {
id
}
}
}
Variables (replace with the
resource_id
from webhook payload):
{
"id": "123"
}
Now, let's query the Healthie API to retrieve Form Answers.
{
"data": {
"formAnswerGroup": {
"id": "123",
"user": {
"id": "200"
}
}
}
}
The response should look like this.
The user.id
property contains the Patient ID which we will use to create a Task related to this Patient.
mutation createTask($input: createTaskInput!) {
createTask(input: $input) {
task {
id
content
}
}
}
Input:
{
"content": "Submit a CMS 1500 Claim",
"user_id": "190", // Biller ID
"client_id": "200" // Patient ID
}
Now let's create a task for the Provider.
Replace the following input variables with data from the response:
Input | Response variable |
---|---|
user_id |
Use your hardcoded ID of the Biller |
client_id |
user.id |
This mutation will create a new Task for the Biller and associate it with the Patient.
Please refer to Tasks documentation to learn more about additional options like reminders and due dates.
Websockets and Subscriptions
Healthie offers GraphQL subscriptions via Websockets. This enables you to build interfaces that automatically listen for new data changes (e.g a new chat message comes in). Healthie's GraphQL subscription implementation is currently offered via AnyCable (which itself is a more peformant implementation of the ActionCable spec).
Please find step-by-step instructions on how to connect and use GraphQL subscriptions via Healthie's API.
1) Open the Websocket Connection
Open a Websocket Connection to the URL listed below. URL parameters are used for authentication via Websockets, because the Websockets protocol specification does not support headers.
Name | URL |
---|---|
Sandbox | wss://ws.staging.gethealthie.com/subscriptions?token=API_KEY_HERE |
Production | wss://ws.gethealthie.com/subscriptions?token=API_KEY_HERE |
// Example Welcome
{
"type": "welcome",
"sid": "0czVwnltbucBy23AanKFJ"
}
// Example Ping
{
"type": "ping",
"message": 1664558222
}
2) Receive Connection Confirmation
Once you connect in Step 1, You should receive a connection successful/welcome message, and will start to receive pings. See examples to the right
// This is Vanilla JS
const channelId = Math.round(
Date.now() + Math.random() * 100000
).toString(16);
3) Generate a Channel ID
ActionCable works via channels. Generate a unique channel ID and then subscribe to it. See generation example to the right.
{
"command": "subscribe",
"identifier": "{\"channel\":\"GraphqlChannel\",\"channelId\":\"<ID_HERE>\"}"
}
4) Subscribe to the Generated ID
Once you have the channel ID, you need to subscribe to it. Do so via sending in the subscribe command to the open Websocket connection.
# Subscription query
subscription onNoteAddedSubscription($id: String) {
noteAddedSubscription(conversationId: $id) {
...NoteFragment
__typename
}
}
fragment NoteFragment on Note {
id
content
user_id
conversation_id
attached_image_url
attached_audio_url
document_id
created_at
updated_at
is_autoresponse
deleted_by_user
scheduled_at
image_name
document_name
on_behalf_user {
id
full_name
__typename
}
creator {
id
full_name
avatar_url
is_patient
first_name_last_initial
__typename
}
__typename
}
// Use the query above to send it as the `data` property below
{
"command": "message",
"identifier": "{\"channel\":\"GraphqlChannel\",\"channelId\":\"<ID_HERE>\"}",
"data": "<STRINGIFIED_QUERY_HERE>"
}
5) Subscribe to the specific GraphQL Subscription
Once you subscribe to channel, you then need to let it know which GraphQL subscription you want to listen to.
Do so via sending in the message command to the open Websocket channel. You can get a list of subscriptions here. Here's an example for noteAddedSubscription
.
{
"identifier": "{\"channel\":\"GraphqlChannel\",\"channelId\":\"1838f674dff\"}",
"message": {
"more": true,
"result": {
"data": {
"noteAddedSubscription": {
"__typename": "Note",
"attached_audio_url": null,
"attached_image_url": null,
"content": "\u003cp\u003eCheck Check Check\u003c/p\u003e",
"conversation_id": "2060148",
"created_at": "2022-09-30 17:20:14 UTC",
"creator": {
"__typename": "User",
"avatar_url": "https://example.com",
"first_name_last_initial": "Dr. Melanie K",
"full_name": "Dr. Melanie Klesse",
"id": "30",
"is_patient": false
},
"deleted_by_user": false,
"document_id": null,
"document_name": null,
"id": "10637345",
"image_name": null,
"is_autoresponse": false,
"on_behalf_user": null,
"scheduled_at": null,
"updated_at": "2022-09-30 17:20:14 UTC",
"user_id": "30"
}
}
}
}
}
6) Receive Subscription Events
Now, you will begin to receive new subscription events that are sent to that channel. Here's an example, also for the noteAddedSubscription
.
Configuring Apollo Client
npm i @rails/actioncable graphql-ruby-client
# or
yarn add @rails/actioncable graphql-ruby-client
First, install the necessary dependencies and import them.
import {
ApolloClient,
HttpLink,
InMemoryCache,
split
} from '@apollo/client'
import { getMainDefinition } from '@apollo/client/utilities'
import * as ActionCable from '@rails/actioncable'
import ActionCableLink from 'graphql-ruby-client/subscriptions/ActionCableLink'
Next, let's import all necessary modules
const API_ENDPOINT = 'https://api.gethealthie.com/graphql'
// Add your API Key to the `?token=` param
const WS_ENDPOINT = 'wss://ws.gethealthie.com/subscriptions?token=<YOUR_TOKEN_HERE>'
Then define API endpoints. Please make sure to provide your API token to the WebSocket URL.
const httpLink = new HttpLink({
uri: API_ENDPOINT,
headers: {
// ...
}
})
Use the HTTP Link as usual. Customize it to your needs, e.g. proivide authorization headers.
const cable = ActionCable.createConsumer(WS_ENDPOINT)
const wsLink = new ActionCableLink({ cable })
Now, let's use our Action Cable to configure the Websocket Link for Apollo Client.
const link = split(
// split based on operation type
({ query }) => {
const { kind, operation } = getMainDefinition(query)
return kind === 'OperationDefinition' &&
operation === 'subscription'
},
wsLink,
httpLink
)
const client = new ApolloClient({
link,
cache: new InMemoryCache(),
})
Lastly, configure a split based on the operation type. We still want to send regular queries and mutations via the HTTP instead of Websockets.
Your Apollo Client is ready to use.
Integrations
Healthie offers a number of integrations with other apps and software platforms. Here we describe some of the ways in which you use those integrations via the Healthie API, and limitations on using certain integrations via the API.
Change Healthcare: E-Labs
The Change Healthcare + Healthie integration for E-labs enables providers to order, track & receive lab results from over 500 unique lab companies directly within Healthie. Lab vendors within the Change Healthcare network include Quest Diagnostics and LabCorp.
When using the integration, data from Change Healthcare feeds into Healthie LabOrder
objects. You cannot order labs via Change Healthcare directly via the API when using the integration -- it functions in an iframe in the Healthie portal. You can, however, retrieve the ingested results via API.
Fullscript
Fullscript integrates with Healthie to make it easy for you to provide product & supplement recommendations within Healthie, and directly connect your Fullscript account with your Healthie account.
When using the integration via the API, start by looking at the following queries in our API Explorer:
dynamicLink
fullscriptPractitioners
fullscriptPractitionerTypes
treatmentPlans