NAV
graphql

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.

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):

Examples of changes we do not consider breaking (and so would make)

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:

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).

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:

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

  1. 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.

  2. Compute the HMAC: Using the same key that the server used, compute the HMAC with SHA-256 of the data string.

  3. 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:

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:

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:

List of Deprecated Features

Below is a list of features that are currently deprecated within our GraphQL API.

String-based sort_by query 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:

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.

Launch the Explorer

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:

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 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

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.
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:
  • all (default)
  • charting
  • program
  • intake
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:
  • last_updated_asc
  • last_updated_desc (default)
  • created_by_asc
  • created_by_desc
  • type_asc
  • type_desc
  • name_asc
  • name_desc

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:
  • onboarding_flow_name_asc (default)
  • onboarding_flow_name_desc
  • created_at_asc
  • created_at_desc
  • forms_count_asc
  • forms_count_desc

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.
  • type - Onboarding Item type (see below)
  • id - ID of the related Item
You can obtain the list of available Item types using the Listing Available Item Types query.

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:
  • Daily
  • Weekly
  • Monthly
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"
    }
  }
}

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 conversationsquery. 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:

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:

  1. a list of any possible conflicting events for a specific date and time (as well as for recurring event instances)
  2. 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.

Creating Availabilities in bulk

mutation bulkCreateAvailability(
  availabilities: [AvailabilityInput],
  $user_id: String
) {
  bulkCreateAvailability(input: {
    availabilities: $availabilities,
    user_id: $user_id,
  }) {
    availabilities {
      id
    }
    messages {
      field
      message
    }
  }
}

You can create multiple Availabilities at once using the bulkCreateAvailability mutation.

The bulkCreateAvailability has a very similar behavior to createAvailability mutation.

You can view a full list of potential inputs here and here.

Input Info
availabilities Required. An array of AvailabilityInput objects. Check out createAvailability documentation for reference.
user_id Optional. ID of the user to create the availability for.

Returns bulkCreateAvailabilityPayload.

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:

A client will be billed only once given any of the following conditions:

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:
  • newest (default)
  • oldest
  • smallestamount
  • largestamount
  • patient_name_asc
  • patient_name_desc
  • provider_name_asc
  • provider_name_desc
  • method_asc
  • method_desc
  • state_asc
  • state_desc
keywords Optional. Keywords to search by. Billing Items can be searched by amount_paid and payment_medium.
state Optional. Valid options are:
  • failed
  • scheduled
  • succeeded
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:
  • One-Time
  • Monthly
  • Yearly
  • Weekly
  • Bi-Weekly
  • Every 4 Weeks
  • Every 8 Weeks
  • Quarterly
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:
  • all - return all Packages
  • visible - Returns Packages with visibility_status equal to visible_to_all or visible_to_specific_groups
  • hidden - return only hidden Packages
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 RequestedPayments 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:
  • requested_on_asc
  • requested_on_desc (default)
  • client_asc
  • client_desc
  • provider_asc
  • provider_desc
  • amount_asc
  • amount_desc
  • status_asc
  • status_desc
  • type_asc
  • type_desc
  • invoice_id_asc
  • invoice_id_desc
only_unpaid Optional. Return only unpaid Invoices.
sender_id Optional. ID of the Sender.
status_filter Optional. Valid options are:
  • paid
  • partial
  • unpaid
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
  • cms1500
  • offering
  • other
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:
  • Paid
  • Not Yet Paid
  • Partial
Normally it is best to let Healthie automatically determine the status based on payments applied to the invoice.
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
  • rolling
  • fixed
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
  • MetricEntry
  • FoodEntry
  • WorkoutEntry
  • MirrorEntry
  • SleepEntry
  • NoteEntry
  • WaterIntakeEntry
  • PoopEntry
  • SymptomEntry
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.
  • "1" - not hungry
  • "2" - somewhat hungry
  • "3" - very hungry

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:
  • oldestfirst
  • newestfirst
  • namedesc
  • nameaesc (default
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:
  • all
  • other - this will return all files that are not: pdf, image, text, video, spreadsheet, presentation
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:
  • all - returns all Tasks that are either assigned or created by the current User
  • assigned - Tasks created by other Users and assigned to the current User
completed_status Optional.
  • complete - return only complete Tasks
  • incomplete - return only incomplete Tasks
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:
  • task_asc
  • task_desc
  • creator_asc
  • creator_desc
  • assignee_asc
  • assignee_desc
  • created_asc
  • created_desc
  • priority_asc
  • priority_desc
  • due_date_asc
  • due_date_desc
  • completed_asc
  • completed_desc
By default, Healthie API returns Tasks ordered by position, creation date (newest first) and incomplete first.

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:
  • 0 - normal priority
  • 1 - high priority
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:
  • daily
  • weekly
  • once - default
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:
  • oldest
  • newest (default)
  • name_asc
  • name_desc
  • status_asc
  • status_desc
  • client_name_desc
  • client_name_asc
  • deactivated_desc, activated_desc
  • deactivated_asc, activated_asc
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:
  • start_date_asc
  • start_date_desc (default)
  • frequency_asc
  • frequency_desc
  • title_asc
  • title_desc
  • status_asc
  • status_desc
  • end_date_asc
  • end_date_desc
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:
  • Daily
  • Weekly
  • Once
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:
  • Daily
  • Weekly
  • Once
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:
  • accepted
  • payer_name_asc

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:
  • name_asc
  • name_desc
  • billed_date_asc
  • billed_date_desc
  • created_date_asc or oldest (default)
  • created_date_desc or newest
  • amount_billed_asc
  • amount_billed_desc
  • amount_paid_asc
  • amount_paid_desc
  • status
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:
  • Sent
  • Not Sent
  • Fully Paid
  • Rejected

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:
  • created_date_asc
  • created_date_desc (default)
  • status_asc
  • status_desc
  • primary_plan_name_asc
  • primary_plan_name_desc
  • patient_name_asc
  • patient_name_desc
  • service_date_asc
  • service_date_desc
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:
  • Not Sent
  • Sent
  • Batched
  • Rejected by Clearinghouse
  • Denied by Insurance
  • Client's Deductible Applies
  • Partially Paid
  • Fully Paid
  • Closed

    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:
    • updated_at_asc
    • updated_at_desc (default)
    • name_asc
    • name_desc
    • lab_asc
    • lab_desc
    • appt_asc
    • appt_desc
    • client_name_asc
    • client_name_desc
    • provider_name_asc
    • provider_name_desc

    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:

    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:

    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:

    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: