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, It is recommended to read Introduction to GraphQL and Queries and Mutations over at Learn about GraphQL before you begin development.

Using our GraphQL endpoint, you can do three types of operations - Queries, Mutations, and Subscriptions.

Operation Usage
Queries Get data
Mutations Perform actions and alter objects
Subscriptions Automatically listen for data changes (e.g a new chat message comes in)

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

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.

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. Actions done via a user's API key are considered to be done by the user.

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

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

Errors in GraphQL work differently than in a traditional REST API. Notably, you may receive errors even though the HTTP status code is 200. Broadly, they are seperated into two categories - Server Errors and GraphQL Errors.

Error Error Code Description
Server Error 5xx Server internal errors that prevent a successful response.
GraphQL Error 200 Some fields / attributes have errors and (may) appear alongside successful data.

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.

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 POST the following body to a URL you provide us. We support different URLs for each event, as well as multiple URLs for the same event.

You can see an example POST body on 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

}

Please note no data about the event is included besides the resource ID and event category. It is expected that you make another API call after, using the resource ID, to get the data you need.

The available event types are currently

Type
appointment.created
appointment.updated
appointment.deleted
billing_item.created
billing_item.updated
cms1500.created
cms1500.updated
cms1500.deleted
comment.created
comment.updated
comment.deleted
conversation_membership.viewed
document.created
document.updated
document.deleted
entry.created
entry.updated
entry.deleted
form_answer_group.created
form_answer_group.deleted
form_answer_group.locked
goal.created
goal.updated
goal.deleted
goal_history.created
insurance_authorization.created
insurance_authorization.updated
insurance_authorization.deleted
message.created
message.deleted
metric_entry.created
metric_entry.updated
metric_entry.deleted
patient.created
patient.updated
payment.created
payment.updated
payment.deleted
policy.created
policy.updated
policy.deleted
requested_form_completion.created
requested_form_completion.updated
requested_form_completion.deleted
task.created
task.updated
task.deleted

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.

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.

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

It's worth to mention 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:

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.

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 password strength requirement.
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.

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. Patients can be searched by first_name, last_name, email, legal_name, dob, and record_identifier.
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.

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, 
  $skipped_email: Boolean, 
  $phone_number: String, 
  $dietitian_id: String, 
  $user_group_id: String, 
  $dont_send_welcome: Boolean
) {
  createOrganizationMembership(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 
  }) {
    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
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 password strength requirement.
send_invite_email Optional. Whether to send an invite email.
organization_id Optional. Specifies to which organization to invite the user. Uses the current organization by default.

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.

Updating an Organization Membership

mutation updateOrganizationMembership(
  $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
) {
  updateOrganizationMembership(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 
  }) {
    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
    custom_modules {
      id
      name
      has_matrix_field
      prefill
      is_video
      use_for_charting
      use_for_program
      has_non_readonly_modules

      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,
) {
  createCustomModuleForm(input: {
    name: $name
  }) {
    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.

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.

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(
  $finished: Boolean, # Should almost always true
  $custom_module_form_id: String, # ID of the custom_module_form (e.g "100")
  $user_id: String, # ID of the patient (e.g "61")
  $form_answers: [FormAnswerInput!]!, # e.g [{custom_module_id: "1", answer: "foo", user_id: "61"}, 
                      #     {custom_module_id: "2", answer: "bar", user_id: "61"}]
) {
  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.

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

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 conversation has multiple ConversationMemberships which determine the users in the conversation. A message in a conversation is a Note.

For more information on the feature in general, view our help articles here.

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). When this happens, our system automatically creates a two-person conversation between the patient and the provider (if one does not exist already).

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.

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.

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.

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"
      },
      "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.

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. 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 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 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,
  $sort_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,
    sort_by: $sort_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 true. 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"]
sort_by Defaults to "date_asc". View the query 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.

Patient Self-Scheduling

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

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.

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.

2) Get Available Days

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

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.

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.

4) Book the Appointment


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

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

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

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.

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.

2) Get Available Days

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

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.

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.

The query returns an array of PotentialAppointmentSlots. We have the patient select one of those slots, and use the 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").

4) Update the Appointment


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

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.

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

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. E.g. August 16, 2022.
time Required. Time when the availability starts. Format: H mm, e.g. 7 30.
end_time Required. Time when the availability ends. Format: H mm, e.g. 7 30.
day_of_week Optional. A 0-indexed day of the week the availability is on. 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. E.g. August 16, 2022.
time Required. Time when the availability starts. Format: H mm, e.g. 7 30.
end_time Required. Time when the availability ends. Format: H mm, e.g. 7 30.
day_of_week Optional. A 0-indexed day of the week the availability is on. 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.

Patient Self-Scheduling

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

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.

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.

2) Get Available Days

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

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.

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.

4) Book the Appointment


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

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

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

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.

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.

2) Get Available Days

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

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.

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.

The query returns an array of PotentialAppointmentSlots. We have the patient select one of those slots, and use the 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").

4) Update the Appointment


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

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.

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.

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.

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.

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
    patient {
      id
    }
  }
}

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.
    dietitian Required. A DietitianInput object to specify a Patient.
    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.

    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.