# AiTmed SDK/API Front-End Documentation

> Developed for the Front-End team by its team leader, **Christopher** :)

Written with **TypeScript** and **immutablejs**

## Update:

- This documentation is now outdated. Please look at the source code for the new API changes
- Error response codes (These come with error status 200 in the request response for new APIs)

| Error                                  | Code |
| -------------------------------------- | ---- |
| NONE                                   | 0    |
| WRONG_USERNAME_OR_PASSWORD             | 1    |
| NO_PERMISSION                          | 2    |
| INSTANCE_NOT_EXIST                     | 404  |
| INTERNAL_SERVER_ERROR                  | 500  |
| NOT_IMPLEMENTED                        | 501  |
| PHONE_NUMBER_ALREADY_EXIST             | 1001 |
| PHONE_NUMBER_DOES_NOT_MATCH            | 1002 |
| INVALID_PARAMETER                      | 9000 |
| INVALID_PHONE_NUMBER_VERIFICATION_CODE | 9001 |
| INVALID_LANGUAGE_CODE                  | 9002 |
| INVALID_PRIMARY_LANGUAGE_NUMBER        | 9003 |
| INVALID_INVITER_CODE                   | 9004 |


## Plans

- [ ] Do all of the error handlings for those
- [ ] Provide the developer the ability to customize each with an options/configuration object

## Usage

ES5

```js
const aitmedApi = require('../src/client')
const api = aitmedApi({ env: 'testapi' })
```

ES6

```js
import aitmedApi from '../src/client'
const api = aitmedApi({ env: 'testapi' })
```

*Most* of methods in the sdk can be chainable if they are not prefixed with `get` or `fetch`. For example:

```js
client
  .setAccountType('marketer')
  .setBaseUrl('https://api.aitmed.com')
  .updateSchema('appointments', { idAttribute: 'tv_uid' })
  .setTransformRequest((data, headers) => {
    if (typeof data === 'object' && Object.keys(data).length) {
      const formData = new FormData()
      Object.keys(data).forEach((key) => formData.append(key, data[key]))
      return formData
    }
    if (!headers['Content-Type'] !== 'application/json') {
      headers['Content-Type'] = 'application/json'
    }
  })
  .setToken('eyJ0eXAifasS0d9mkasmdlopWZds...')
  .addRequestInterceptor(('gorilla', (...args) => {
    if (typeof args[1] !== undefined) {
      // do stuff
    }
  }))
  .fetchAppointments({
    status: [0, 1, 2, 3],
    limit: 1000,
    filter: (appointment, appointmentId, allAppointments) => {
      if (!appointment.is_walk_in && appointment.provider.email !== 'bb123@gmail.com') {
        return false
      }
      return true
    }
  })
  .then((appointments) => {
    console.log(appointments)
    console.log(`${appointments.length} appointments passed the filter.`)
  })
```

## API

The app consists of multiple `accountType`s to differentiate functionality. Here is a table listing all of the available account types that is used throughout the application:

| Account                          | Description                 |
| -------------------------------- | --------------------------- |
| [patient](/interfaces#patient)   | Patients                    |
| [provider](/interfaces#provider) | Providers                   |
| company                          | Corporations / Companies    |
| marketer                         | Marketers                   |
| staffing_company                 | Staffing Companies          |
| guest                            | Users who are not logged in |
| admin                            | Admin                       |

To begin using any of these methods an `accountType` must be set on the instance. Most of the time you won't have to worry about this since `.login` will internally set the `accountType` for you if the call was a success. On the first login, the instance will internally set the JWT `token`, authorization headers and the `accountType` for future requests to work.

In addition, if the `accountType` is a `patient` it will make *all* of the [truevault sdk](https://truevault.github.io/truevault-js-sdk/#truevaultclient) *methods* available on the `.tvClient` property (this is a really powerful feature when combined with our api):

```js
api.login('patient', { username: 'user123', password: 'pw123' })
  .then((user) => {
    const tvClient = api.tvClient
    tvClient.readCurrentUser(true)
      .then((tvUser) => {
        console.log(tvUser) // Returns all the information about the user stored in truevault, including attributes
      })
  })
```

**Note**, PLEASE READ: As of **05/31/2019** it is now recommended for patients to use `api.loginAsPatient({ tv_uid, tv_token })` to login **during** the portal. This means that `api.login('patient', ...args)` still applies for the *sign in* page for patients, since they need to use their username and password to log in initially.

### Methods

#### Logging in as a user: (admins not yet supported)

> .login(**type**: [AccountType](/interfaces#AccountType), **...args**)

This method will also set the `accountType`, `token`, and the authorization `header` for subsequent api calls if the credentials are valid.

If the second argument is an object, it must be passed in using one of the following formats:

1. `{ email: string, password: string }`
2. `{ username: string, password: string }`
3. `{ tv_uid: string, tv_token: string }`

If the first one is used for the login process, it must be an account type of anything *other than* patients:

```js
api.login('provider', {
  email: 'myemail123@gmail.com',
  password: 'pw'
}).then((response) => {
  console.log(response)
})
```

Returns: [Provider](/interfaces#provider)

If the second one is used, it *must be a patient*:

```js
api.login('patient', {
  username: 'goku@gmail.com',
  password: 'abc3444',
}).then((response) => {
  console.log(response)
})
```

**EDIT**: The login method immediately below this is deprecated for patients as of 05/31/2019. Please use `api.loginAsPatient` instead.

The SDK will first authenticate the patient through truevault to receive their `tv_uid` and `tv_token`, passing it along as the parameters to the second request to the backend (example immediately below)

```js
api.login('patient', {
  tv_uid: '32423-fdfsdfds-4234234-fdg',
  tv_token: '.v2-4324234239fjsdifjidsfj342jijidfjijdsfds'
}).then((response) => {
  console.log(response)
})
```

Returns: { user: [Patient](/interfaces#patient), members: [[Patient]](/interfaces#patient) }

You can also login *without* the credentials by attaching the `user_id` as the second argument and the JWT token as the third argument:

```js
api.login(
  'provider',
  'kij423-dsji234-zzzz43-423-dfsd',
  'JIj32FFF343kKKQQQWEKKfdsf9kKAdmskdkasOOKASd34OPOppFOOISIFASFI=='
).then((response) => {
  console.log(response)
})
```

Returns: [Provider](/interfaces#provider)

If the `accountType` is a patient it will set all of the truevault sdk methods available on the `api.tvClient` property, making it available to be used as an instance for truevault API's.

Admins can log in using the same flow:

```js
api.login('admin', { email: 'email123', password: 'password123' })
  .then((response) => console.log(response))
```

If the email or password isn't provided as arguments, the method will throw with a rejected `MissingParametersError` promise.

Returns the token as the only value.

#### Logging in as a patient using tv_uid and tv_token ()

> .loginAsPatient({ **tv_uid**: string, **tv_token**: string })

To login as a patient, you would need to call this method and pass in the patient's `tv_uid` and `tv_token` parameters given from *truevault*. If the call was a success, it will proceed to login as a patient in the backend to retrieve their members and JWT token to access the official APIs.

As a side note it is important to know that the `api.tvClient` property has also been initiated during the process, and makes all of the sdk methods from the [truevault sdk](https://truevault.github.io/truevault-js-sdk/) available.

Also, the `members` property in the return result is normalized into an object hash structure to support fast lookups.

```js
client.loginAsPatient({ tv_uid: '9394s-ajjsm-4342-sdasZ', tv_token: 'v2.833ccac3509340758503f09ddc97f381.ebf4c0bc9746b9b0d1a86d3c09e5de1e8cf52813accbc16b0c0cfbf743eda56d'})
  .then((result) => {
    console.log(result)
  })
```

Returns: [PatientFullBackend](/interfaces#PatientFullBackend)

#### Activating/approving a provider

> .approveProvider(**userId**: string)
> .activateProvider(**userId**: string)

To activate a provider, you can call this method and pass in the provider's `user_id`. If the `accountType` is **not** `"admin"`, the method will throw with a `PermissionError` error. If the call was a success it will return the [Provider](/interfaces#provider) object back with the updated `is_approved` property set to `true`.

`approveProvider` is just an alias of `activateProvider`, and function exactly the same way.

```js
const userId = '4f500ac6-zxcv-423c-at77-6602316b7c09'

client.activateProvider(userId)
  .then((result) => console.log(result))

// or:

client.approveProvider(userId)
  .then((result) => console.log(result))
```

Returns: [Provider](/interfaces#provider)

#### Authenticating an offline provider

> .authOfflineProvider(**token**: string)

*This is a stub*

#### Confirming a user's email

> .confirmEmail(**key**: string)

To set a request to confirm the user's email, a `key` parameter is required to be passed in.

After a new user clicks on a confirmation link in an email sent to them immediately after signing up from the website, they should be redirected to a page on the website attaching the `key` paremeter to the URL.

By extracting that parameter you can pass it as an argument to `confirmEmail` and the call will proceed to mark their account as confirmed.

If the call was successful, it will return a `"success"` string.

```js
const currentUrl = window.location.href // aitmed.com/.../?key=abc123
const searchStr = window.location.search // ?key=abc123
const key = new URLSearchParams(searchStr).get('key') // abc123
client.confirmEmail(key)
  .then((response) => console.log(response))
```

Returns: `"success"`

#### Canceling an appointment

> .cancelAppointment(**id**: string, **reason**: string)

*This is a stub*

#### Completing an appointment

> .completeAppointment(**id**: string)

Completes an appointment by passing in the **appointment id**. If the call succeeds the appointment status will update to `3` in the database. Returns the [appointment](/interfaces#Appointment) object. If the `id` parameter is invalid the function will return a rejected `InvalidParametersError` promise.

```js
client.completeAppointment('appointment_id123')
  .then((response) => console.log(response))
```

Returns: [Appointment](/interfaces#Appointment)

#### Creating an appointment

> .createAppointment({ **patient**: string, **provider**: string, **specialty**: string, **purpose**?: string })

Creates an appointment by applying the patient's `user_id`, the provider's `user_id`, the `specialty` code, and optionally a `purpose` as parameters. If this method is used by an `accountType` other than patients or providers the function will return a rejected promise with a `WrongAccountType` error.

If the call succeeds, all appointments will be returned in the result object. You can pass in an object as the second argument to configure it to disable this. If it is disabled and the call succeeds, the return value will instead be a string `"success"`.

```js
const patient = 'fdsii-3iidfs-mdifidsf-fiienriewnew'
const provider = 'kAienriewnew-3iidfs-mdifidsf-3iidfs'
const specialty = 'FAMILY_MEDICINE'
const purpose = 'I broke my arm'
const params = { patient, provider, specialty, purpose }

api.login('patient', '...', '...')
  .setAccountType('patient')
  .createAppointment(params)
  .then((result) => {
    console.log(result)
  })
```


Returns: { **pagination**: [Pagination](/interfaces#pagination), **appointments**: { [**id**: string]: [Appointment](/interfaces#appointment) }, **ids**: string[] }

If `options.returnAll === false`, it will instead return `"success"`

#### Creating an appointment review

> .createAppointmentReview(**values**: [CreateAppointmentReviewParams](/interfaces#CreateAppointmentReviewParams))

After a patient has finished their appointment with a provider, the patient will be able  use this method to create a review on that provider about their experience.

The values passed into this method are:

The parameters used are:

| Parameter             | Description                                            |
| --------------------- | ------------------------------------------------------ |
| `appointment`: string | The appointment id                                     |
| `rating`: string      | A number ranging from 0 to 5                           |
| `comment`: string     | The patient's comment about the appointment / provider |

If the rating passed in is lower than 0 (ex: -100) it will be converted to `0`. In addition, if the rating is higher than 5 (ex: 15) it will be converted to `5`.

Returns { **appointment**: string, **rating**: number, **comment**: string }

#### Creating a blob (not specific to patients)

> .createBlob(**file**: any, **ownerId**?: string)

*This is a stub*

#### Creating a chase payment profile

> .createChasePaymentProfile(**params**: [ChasePaymentProfileParams](/interfaces#ChasePaymentProfileParams))

To create a chase payment profile, pass in an object of parameters as shown below:

```js
const params = {
  card_number: '...',
  expiration_date: '...',
  cvv_code: '...',
  billing_first_name: '...',
  billing_last_name: '...',
  billing_address: '...',
  billing_city: '...',
  billing_state: '...',
  billing_zip_code: '...',
}

api.createChasePaymentProfile(params)
  .then((result) => console.log(result))
```

Returns: { **payment_profile_id**: string }

#### Creating a patient

> .createPatient(**username**: string, **password**: string, **otherData**?: [CreatePatientOtherDataArgs](/interfaces#CreatePatientOtherDataArgs))

This method will make 2 trips.

The first trip will be to create a truevault account for the patient to store their `attributes`. Once the call is successful, the function will extract the `user_id` and `access_token` from the response and immediately make a second request to the backend to finally create the patient's account.

On success it will return an object of the patient's account:

```ts
{
  user_id: string
  tv_uid: string
  tv_group_id: string
  languages: { [string]: boolean }
  inviter_code: string
  activated_key: string
}
```

#### Creating a patient blob

> .createPatientBlob({ **blobId**: string, **userId**: string, **fileName**: string })

Uploads information about a previously created blob (sent to truevault) for the backend to keep a reference to it.

The argument passed into this method is an *object* of:

| Parameter            | Description                    |
| -------------------- | ------------------------------ |
| `patient`: string    | Patient's `user_id`            |
| `tv_blob_id`: string | The `blob_id` from *truevault* |
| `filename`: string   | File name                      |
| `created`?: string   | Creation date in ISO format    |

If the call was a success, it returns back the `"success"` string and a `201` status code.

The server responds with a `400` status code if something went wrong with the request (ex: bad request syntax). If the user creating this blob is not related to the `patient`, the server will response with a `403` status code. In addition, if the `accountType` is not a patient the method will throw with a `WrongAccountTypeError` error.

```js
const blobInfo = {
  patient: 'fdsfds-34343-bmbbzz-Sfsa34',
  tv_blob_id: 'abc1235ogf-fdsfds',
  filename: 'apple.pdf',
  created: '2019-04-05T04:54:59.069Z'
}
api.createPatientBlob(blobInfo)
  .then((result) => {
    console.log(result)
  })
```

Returns: `"success"`

#### Creating a referring patient

> .createReferringPatient(**email**: string)

*This is a stub*

#### Deactivating a patient

> .deactivatePatient(**userId**: string)

Deactivates a patient account. The `userId` parameter is the patient's `user_id` from the backend database table.

```js
const userId = 'mo324-assdd-fasf23-vxvxer-423dds'
client.deactivatePatient(userId)
  .then((result) => console.log(result))
```

Returns:

#### Deleting a date from the the provider's availability agenda

> .deleteFromAvailabilityAgenda(**start**: string, **end**: string)

*This is a stub*

#### Deleting a patient blob

> .deletePatientBlob(**id**: string)

Deletes a blob object using the blob `id`. In the backend, this is most likely the `tv_blob_id` found in patient accounts under the `tv_blobs` array.

If the blob id is not found, the server will response with a `404` status code. If the user requesting this call is not related to the patient in question, the server will respond with a `403` status code.

*This API can only be used by `patient`s. If the `accountType` is not a `patient` then the  method will throw with a `WrongAccountTypeError` error.

```js
const blobId = 'dsi3isad-423kkas-zzzzzz'
api.deletePatientBlob(blobId)
  .then((result) => {
    console.log(result)
  })
```

Returns: `"success"`

#### Deleting a chase payment profile

> .deleteChasePaymentProfile(**id**: string)

Deletes the user's chase payment profile using the user's *payment profile id*.

```js
const profileId = 1839423822
api.deleteChasePaymentProfile(profileId)
  .then((result) => {
    console.log(result)
  })
```

Returns: `"success"`

#### Fetching an appointment

> .fetchAppointment(**id**: string)

The `id` argument is an appointment id. Pass it into `fetchAppointment` and it will return the details if the call was successful.

```js
const id = '00czxc3f6-edc6-4442a-9d99-f83edd3398fe'
api.fetchAppointment(id)
  .then((appointment) => console.log(appointment))
```

Returns the appointment object: [Appointment](/interfaces#appointment)

#### Fetching patient images from an appointment

> .fetchAppointmentPatientImages(**appointmentId**: string)

*This is a stub*

#### Fetching an appointment review

> .fetchAppointmentReview(**id**: string)

The `id` argument is an appointment review id.

```js
const id = '0fsdfdsdsfdsfdsfdse'
api.fetchAppointmentReview(id)
  .then((apptReview) => console.log(apptReview))
```

Returns the appointment review object: [AppointmentReview](/interfaces#AppointmentReview)

#### Fetching multiple appointment reviews for a specific provider

> .fetchAppointmentReviews(**userId**: string)

The `userId` argument is the provider's `user_id`.

This method will fetch and return multiple [appointment reviews](/interfaces#AppointmentReview) for a specific provider.

```js
const userId = '423oidsf-3423-fdsfds-4324234-gfgdfg'
api.fetchAppointmentReviews(userId)
  .then((apptReviews) => console.log(apptReviews))
```

Returns: { **pagination**: [Pagination](/interfaces#Pagination), reviews: { [**appointment**: string]: [AppointmentReview](/interfaces#AppointmentReview), **ids**: string[] } }

Each appointment `review` object is mapped by their **appointment id**.

#### Fetching a provider's availability dates

> .fetchAvailabilityAgenda(**start**: string, **end**: string)

**This is a stub**

#### Fetching a list of categorized specialties

> .fetchCategories()

Fetch and return an array of categorized specialties as [category](/interfaces#categoryobject) objects.

```js
api.fetchCategories()
  .then((categories) => console.log(categories))
```

Returns: [[CategoryObjects]](/interfaces#categoryobjects)

> .fetchCoupons()

Fetch and return an array of [coupons](/interfaces#Coupon).

This API is only usable by patients, and will throw an error if the `accountType` set in the store store is different.

```js
api.Coupon()
  .then((coupons) => console.log(coupons))
```

Returns: [Coupon](/interfaces#Coupon)[]

#### Fetching the user's dwolla IAV token

> .fetchDwollaToken()

Fetch and return the user's dwolla `IAV token` as a string. At the moment, the only account that is using this API are `providers`, but may be extensible in the future to support others.

```js
api.fetchDwollaToken()
  .then((token) => console.log(token))
  // result: C2LBPgrgPjCqhCel4acOFOPb21stoU2dmhVsH50hRd2sePtdBi
```

Returns: `string`

#### Fetching the user's list of coupons

> .fetchCoupons()

Fetch and return an array of [coupons](/interfaces#Coupon).

This API is only usable by patients, and will throw an error if the `accountType` set in the store store is different.

```js
api.Coupon()
  .then((coupons) => console.log(coupons))
```

Returns: [Coupon](/interfaces#Coupon)[]

#### Fetching a list of genders

> .fetchGenders()

Fetch and return an array of [gender](/interfaces#gender) objects

```js
api.fetchGenders()
  .then((genders) => console.log(genders))
```

Returns: [[GenderObjects]](/interfaces#genderobjects)

#### Fetching a list of languages

> .fetchLanguages()

Fetch and return an array of [language objects](/interfaces#languageobject).

```js
api.fetchLanguages()
  .then((languages) => console.log(languages))
```

Returns: [[LanguageObjects]](/interfaces#languageobjects)

#### Fetching a meeting's token

> .fetchMeetingToken(**appointmentId**: string)

*This is a stub*

### Fetching a patient by `user_id`

> .fetchPatient(userId: string)

Fetch a patient's information ([documentized](/interfaces#PatientDocumentized)) by passing in a `user_id`. This method requires the current `accountType` user to have access to the patient in question.

```js
const userId = 'ki2433-asdfZZ-34923943-dfkisoas'
client.fetchPatient(userId)
  .then((user) => console.log(user))
```

Returns: [PatientDocumentized](/interfaces#PatientDocumentized)

### Fetching a list of patients

> .fetchPatients(**params**?: [FetchPatientsParams](/interfaces#FetchPatientsParams))

*This is a stub*

### Fetching a patient's blob

> .fetchPatientBlob(id: string)

Fetch a patient's blob by using the `tv_blob_id`. This method requires the current `accountType` user to have access to the patient blob in question.

```js
const blobId = 'bdsfods-423423-0025d'
client.fetchPatientBlob(blobId)
  .then((blobObject) => console.log(blobObject))
```

Returns: [PatientBlob](/interfaces#PatientBlob)

### Fetching a *list* of blobs

> .fetchPatientBlobs(userIds: string|string[])

Fetch a list of blobs by passing in an array of `user_id`s. If `userIds` was passed as a string, it will be converted to an array with the string as the first index. The method will iterate over each `user_id` to accumulate all of the possible blob objects. The current `accountType` set on the sdk instance must have permissions to access these objects from the users in question.

If the call was a success, the method will return an object with the shape `{ pagination, blobs, ids }` where `pagination` is the [Pagination](/interfaces#Pagination) object and `blobs` is an object of [PatientBlob](/interfaces#patientblob) objects where the keys are the `tv_blob_id`'s and the value is the actual `blob` object itself.

```js
const userIds = ['sfds394-dsfidsfi-423423-fdsf, fdsfds-ZZZff-34234df-zfzfs']
client.fetchPatientBlobs(userIds)
  .then((blobObjects) => console.log(blobObjects))
```

Returns: { **pagination**: [Pagination](/interfaces#pagination), **blobs**: [PatientBlobs](/interfaces#PatientBlobs), **ids**: string[] }

#### Fetching a patient document

> .fetchPatientDocument(**id**: string)

Fetching a patient's document is relatively simple. By passing in a single document id string as an argument, the sdk will use that to make the API call to truevault and retrieve the document object with the document's data. If the document id argument is an invalid type it will return a rejected promise with an `InvalidParametersError`. If the argument is a truthy type but it is *not* a string, the function will return a rejected promise with the `TypeError` error.

```js
client.fetchPatientDocument('zzzzfs3-9861-4983-8cb3-ed22076ee8f0')
  .then((result) => console.log(result))

client.fetchPatientDocument(['432dfsdsf', 'fdsofi343'])
  .catch((err) => console.error(err)) // Error: The document id passed in is not a string
```

Returns:

```ts
{
  document?: { [**key**: string]: boolean | string, **medications**?: string[], **allergies**?: string[] }
  id: string
  owner_id: string
}
```

#### Fetching multiple patient documents

> .fetchPatientDocuments(**docIds**: string[])

To fetch multiple documents an array of strings must be used as the first argument. The function will return a rejected promise if `docIds` is not an array type.

This will make an API call to truevault to fetch all the of the documents using their document ids specified in the `docIds` array.

Once the the data arrives it will be transformed into a normalized data structure in the shape of `{ documents, ids }`, maximizing the efficiently when retrieving document data when needed.

`documents` is an object literal holding multiple documents as objects, where the `key` is the document id and its value is the document's data. In addition, each object includes an `id` (document id) and `owner_id` (the caller's `user_id`)

```js
client.fetchPatientDocuments(['zzzzfs3-9861-4983-8cb3-ed22076ee8f0', 'dfsfs34-dfsfz-zzzz-33234'])
  .then((results) => console.log(results))
```

Returns: { **documents**: { [**id**: string]: string }, **ids**: [] }

#### Fetching a patient's members

> .fetchPatientMembers(**tvUids**: string | string[], { full?: boolean })

To fetch a list of the primary patient's members, a string or an array of strings is required to be passed in as the first argument.

The method will iterate through each of the tv_uid's of the members and fetch their entire `attributes` from the `truevault` api.

If the call was successful, it will normalize the data result from the response to an object to support fast object lookups.

```js
client.fetchPatientMembers(['asdz1-49323-ea34fe-asw33-a3423'])
  .then((results) => console.log(results))
```

Returns: { **members**: { [**user_id**: string]: [PatientDocumentized](/interfaces#PatientDocumentized), **ids**: string[] }}

#### Fetching a single provider

> .fetchProvider(**userId**: string)

Using the `fetchProvider` api will return the entire [provider](/interfaces#Provider) object. To use this API you can call the method and pass in the provider's `user_id` given from the backend.

```js
const params = { username: 'admin@aitmed.com', password: 'letmein123' }
const userId = 's19423-asz324-23123-433f-3424'
client
  .loginAsAdmin(params)
  .then(() => client.fetchProvider(userId))
  .then((provider) => console.log(provider))
  .catch((error) => console.error(error))
```

Returns: [Provider](/interfaces#Provider)

#### Fetching a list of providers

> .fetchProviders({ **limit**?: number, **name**?: string[] | object, **order_by**?: string, **page**?: number, **referral_code**?: string})

Fetch and return an object with providers included in the result, in the shape of { **pagination**, **providers**, **ids**}

If the optional `name` parameter is an array of strings, it should have a length no less than 1, and additionally a length no greater than 3. If there is only one item in the array, it will be used to query providers by their first name. If there are two items in the array, the first item will be used as the first name and the second item will be used as the last name. Finally, if there are 3 items in the array the first item will be used as the first name, second item as the middle name, and the last item as the last name.

If the optional `name` parameter is an object, it must contain at least one of these properties: `firstName`, `middleName`, or `lastName` or the method will throw with a `MissingParametersError` error. This option may be used to query the same results as with the array version.

If `name` is defined but is not an array or object, the method will throw with a `TypeError` error.

If the optional `order_by` is passed in, it will be used to query a column in the provider database table.

The optional `referral_code` can be used to filter the results to a list of providers that contain that referral code.

```js
const params = {
  limit: 10,
  page: 1,
  referral_code: 'ae9Xz31',
  order_by: 'state',
  name: { // or: ['george', 'H.', 'lopez']
    firstName: 'George',
    middleName: 'H.',
    lastName: 'Espinoza',
  }
}
client.fetchProviders(params)
  .then((results) => console.log(results))
```

Returns: `{ **pagination**: [Pagination](/interfaces#Pagination), **ids**: string[], **providers**: { [**user_id**: string]: [Provider](/interfaces#Provider)}}

#### Fetching a list of provider specialties

> .fetchSpecialties()

Fetch and return an array of [specialty objects](/interfaces#specialtyobject) in the shape of `{ specialties, ids }`

Each item in the `specialty` object is mapped by their specialty `code`.

Example:

```json
{
  "FAMILY_MEDICINE": {
    "code": "FAMILY_MEDICINE",
    "category": "MEDICAL",
    "type": "COMMON",
  }
  ...etc
}
```

```js
api.fetchSpecialties()
  .then((result) => console.log(result))
```

Returns: { **specialties**: { [**code**: string]: [SpecialtyCode](/interfaces#specialtycode), ...etc }, **ids**: string[] }

#### Fetching upcoming/previous appointments

> .fetchPreviousAppointments({ **limit?**: number, **order_by?**: string, **filter?**: function })
>
> .fetchUpcomingAppointments({ **limit?**: number, **order_by?**: string, **filter?**: function })
>
> .fetchAppointments({ **statuses**?: number[], **limit?**: number, **order_by?**: string, **filter?**: function })

*(**fetchPreviousAppointments** and **fetchUpcomingAppointments** is basically **fetchAppointments** but added for convenience)*

You can use the `limit` parameter to limit the amount of items returned from the function. The `order_by` parameter is used to order the items accordingly. The `filter` parameter can be used to filter the data to narrow down the results being returned.

If `filter` is used, it must be a function. The function will be passed an appointment object as the first argument, the current appointment id as the second, and the entire collection itself as the third argument. In addition it should also be used like a normal filter callback function:

```js
const filter = (appointment, id, allAppointments) => !appointment.is_walk_in
api.fetchUpcomingAppointments({ limit: 3, filter })
  .then((results) => {
    console.log(results.appointments)
  })
```

`fetchUpcomingAppointments`, `fetchPreviousAppointments`, and `fetchAppointments` restructures the data to enable blazingly fast and efficient in-memory data retrieval and returns an object where *ids* is an array of appointment ids, *pagination* is the [pagination](/interfaces#pagination) object, and *appointments* is an object of appointment objects where each key is the appointment id and its value is the appointment object itself.

Returns: { **pagination**: [Pagination](/interfaces#Pagination), **appointments**: { [**id**: string]: [Appointment](/interfaces#appointment) }, **ids**: string[] }

#### Fetching a payment profile

> .fetchPaymentProfile()

*This is a stub*

#### Finding providers to schedule appointments #1

> .findProviders(**params**: [FindProvidersParams](/interfaces#FindProvidersParams))

To search for providers that are available to schedule appointments for patients based on a `time` query, `findProviders` can be used. `findProviders` takes 4 parameters:

| Parameter                                                | Description                                                 |
| -------------------------------------------------------- | ----------------------------------------------------------- |
| `time`: string                                           | Filters appointments to be within a time frame (ISO format) |
| `state`: string                                          | A single state (ex: `CA`)                                   |
| `specialty?`: [SpecialtyCode](/interfaces#SpecialtyCode) | A specialty by their *specialty code*                       |
| `language?`: [LanguageCode](/interfaces#LanguageCode)    | A language by their *language code* (ex: `en-US`)           |

Each result object will contain a `time_slot_id`, `start` and `end` property.

Returns: { **pagination**: [Pagination](/interfaces#Pagination), **providers**: { [**time_slot_id**: string]: [FindProviderResult](/interfaces#FindProviderResult), ...etc }, **timeSlotIds**: string[] }

Each provider object in the returned result is mapped by their `time_slot_id`.

```js
const params = {
  state: 'CA',
  specialty: 'FAMILY_MEDICINE',
  time: moment()
    .subtract(2, 'days')
    .toISOString()
}
 api.findProviders(params)
  .then((res) => {
     console.log(res)
  })
```

#### Fetching a blob from truevault

> .fetchTvBlob(**blobId**: string)

*This is a stub*

Returns: { **pagination**: [Pagination](/interfaces#pagination), **providers**: { [**time_lot_id**: string]: [FindProviderResult](/interfaces#FindProviderResult), ...etc }, **timeSlotIds**: number[] }

#### Finding providers to schedule appointments #2

> .findProviderAvailabilities(**params**: [FindProviderAvailabilitiesParams](/interfaces#FindProviderAvailabilitiesParams))

This method works exactly like `findProviders` except it returns multiple `time_slots` for that provider if at least one of their time slots relatively matches the `time` query.

`findProviderAvailabilities` takes 4 parameters:

| Parameter                                                | Description                                                 |
| -------------------------------------------------------- | ----------------------------------------------------------- |
| `time`: string                                           | Filters appointments to be within a time frame (ISO format) |
| `state`: string                                          | A single state (ex: `CA`)                                   |
| `specialty?`: [SpecialtyCode](/interfaces#SpecialtyCode) | An array of provider specialties by their specialty code    |
| `language?`: [LanguageCode](/interfaces#LanguageCode)    | An array of languages by their language code (ex: `en-US`)  |

If `specialty` is a string, it will be converted to an array using the `.join` method, delimited by commas. Each specialty must be separated by commas and there must be *no spaces in between each*.

```js
const specialty = 'FAMILY_MEDICINE,PSYCHIATRIST,OBGYN'
// becomes ['FAMILY_MEDICINE', 'PSYCHIATRIST', 'OBGYN']

const specialty = 'FAMILY_MEDICINE, PSYCHIATRIST OBGYN'
// becomes ["FAMILY_MEDICINE", " PSYCHIATRIST OBGYN"] (formatting is wrong!)
```

Returns: { **pagination**: [Pagination](/interfaces#Pagination), **providers**: { [**user_id**: string]: [FindProviderAvailabilityResult](/interfaces#FindProviderAvailabilityResult), ...etc }, **ids**: string[] }

Each provider object in the returned result is mapped by their `user_id`.

#### Sending a *forgot password* request for the user

> .forgotPassword(**email**: string)

**NOTE**: This method requires an [accountType](/interfaces#accounttype) set on anything **other than** `guest`s, so make sure to set `.setAccountType` before calling this method!

*Patients*: To begin a *forgot password* flow for the patient, only one parameter, `username` (the alias for `email`) is required to be passed in. The call will send this request to truevault, and truevault will send the patient an email with a link via *SendGrid*. The link in the email will contain a single-use token that allows that patient to reset their password, for a limited time. If the call is a success it will return a `"success"` string, otherwise it will return a rejected promise.

```js
let accountType = client.getCurrentAccountType()

if (accountType === 'guest') {
  const isPatient = 'tv-forgot-password'.test(window.location.href)
  if (isPatient) {
    client.setAccountType('patient')
  }
}

client.forgotPassword('myemail@gmail.com')
  .then((response) => {
    console.log(response) // Result: "success"
  })
```

*Non-patients*: Similarily to the patient flow, only one parameter is required (`email`). The API call seconds a request directly to the backend. If the call was a success, the user should expect an email containing a link to reset their password.

```js
client.setAccountType('provider')
  .forgotPassword('myemail@gmail.com')
  .then((response) => {
    console.log(response) // Result: "Email sent"
  })
```

#### Forgot password for provider accounts

> .forgotPasswordProvider(**email**: string)

To send a *forgot password* request for the provider, you can call `forgotPasswordProvider` and password in the provider's `email` as the argument.

If the call was a success, the provider should be receiving an email that contains a link, redirecting them to a *reset password* page upon clicking the link.

In addition, this method will return a string value of `'success'` if the call succeeds.

```js
const email = 'someemail@gmail.com'
api.forgotPasswordProvider(email)
  .then((result) => console.log(result))
```

#### Fetching an account's funding status

> .getFundingStatus()

*This is a stub*

Returns: `"success"`

#### Sending a confirmation email

> .sendConfirmationEmail(**email**: string)

*This is a stub*

#### Suspending a provider

> .suspendProvider(**userId**: string)

To suspend a provider, you can call this method and pass in the provider's `user_id`. If the `accountType` is **not** `"admin"`, the method will throw with a `PermissionError` error. If the call was a success it will return the [Provider](/interfaces#provider) object back with the updated `is_approved` property set to `false`.

```js
const userId = '4f500ac6-zxcv-423c-at77-6602316b7c09'
client.suspendProvider(userId)
  .then((result) => console.log(result))
```

Returns: [Provider](/interfaces#provider)

#### Getting the current `accountType`

> .getCurrentAccountType()

```js
const accountType = client.getCurrentAccountType()
```

Returns the current [accountType](/interfaces#AccountType) set in the internal store.

#### Getting previous API calls

> .getPreviousApiCalls(**limit**?: number)

Returns the previous API endpoints that were called. You can optionally supply a limit parameter to limit the results returned. The SDK limits the amount of items present in the array to 30 so it will not be possible to receive more than 30 results, respectively.

```js
const limit = 25
const prevCalls = client.getPreviousApiCalls(limit)
console.log(prevCalls)
```

Returns:

```json
["/v2/account/patient_blob/blobId123/", "/v2/info/categories/", ...etc]
```

#### Joining an appointment/meeting

> .joinMeeting(id: string)

`.joinMeeting` is used to join an appointment by using the appointment `id`. If the appointment `id` is valid and the call was a success, the appointment's [status](/interfaces#AppointmentStatus) will be incremented in the database and the value returned will include a meeting token, session id, and the creation time for the newly created appointment.

```js
const appointmentId = 'fdsfidsf-423423423-dsfdsfd-ooo-zzzz'
client.joinMeeting(appointmentId)
  .then((result) => console.log(result))
```

Returns:

```ts
{
  session: {
     session_id: string
     created: string
   }
   token: string
   created: string
}
```

#### Logging in using a JWT token

> .loginWithJwtToken(**accountType**: [AccountType](/interfaces#AccountType, params: [LoginWithJwtTokensParams](/interfaces#LoginWithJwtTokensParams)))

To login using the JWT token, you will need to pass in the `accountType` as the first argument and an object as the second argument.

The second argument should be an object with the shape `{ userId, token }` where `userId` is the `user_id` and `token` is the `token`, both given from the backend.

The method will throw if either `accountType`, `userId`, or `token` is `undefined`.

This method will return a user object depending on the `accountType`. Since this method sets the `token`, `headers` and `accountType` of the sdk client, it may be used as an alternative way to login a user.

```js
const params = {
  userId: '4f500ac6-73f4-423c-9a20-6602316b7c09',
  token: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NTkzNDc1MTUsImlkIjoiNGY1MDBhYzYtNzNmNC00MjNjLTlhMjAtNjYwMjMxNmI3YzA5In0.VOU7TP9H_1b5pws1vsSPDiUhzSV_kRA-p7tLuAG6aYw'
}
api
  .loginWithJwtToken('patient', params)
  .then((result) => {
    console.log(result)
  })
```

#### Querying appointments

> .queryAppointments(**options**: [ConstructQueryAppointmentsUrlArgs](/interfaces#ConstructQueryAppointmentsUrlArgs))

The way `queryAppointments` works is similar to `fetchAppointments` except you can use specific query options to narrow down the results. These query options will be appended to the URL as query parameters:

| Parameter              | Description                                                       |
| ---------------------- | ----------------------------------------------------------------- |
| `patients`?: string[]  | The patient's `user_id`                                           |
| `providers`?: string[] | The provider's `user_id`                                          |
| `status`?: number[]    | An array of [appointment statuses](/interfaces#appointmentstatus) |
| `start_time`?: string  | A date in ISO format                                              |
| `end_time`?: string    | A date in ISO format                                              |
| `filter`?: function    | Used to filter the results                                        |

If the `accountType` is anything other than `patient`, the method will return a rejected promise using the `WrongAccountType` error.
If a string is passed in any of the `patients`, `providers`, or `status` parameter it will be converted to an array as the only index, and will be treated as one value of that query.

```js
const patients = ['gdfgf8c-28d6-481a-843226-c38gdfgdff192', 'aszff-fd3f-481a-123fs-zsdeeat3f']
const providers = ['o239ds-fs3tf-sfdf-43234g-aposdsfids']
const status = [1, 2]
const start_time = '2019-03-29T17:38:47.274Z'
const end_time = '2019-03-31T17:38:47.274Z'

const options = { patients, providers, status, start_time, end_time }

client.queryAppointments(options)
  .then((result) => {
    console.log(result)
  })
```

The `filter` parameter can be used to filter the data to narrow down the results being returned.

If `filter` is used, it must be a function. The function will be passed an appointment object as the first argument, the current index as the second, and the entire collection itself as the third argument. In addition it should also be used like a normal filter callback function:

```js
const filter = (appointment, index, allAppointments) => !appointment.is_walk_in
options.filter = filter

api.queryAppointments(options)
  .then((results) => {
    console.log(results.appointments)
  })
```

Returns: { **pagination**: [Pagination](/interfaces#pagination), **appointments**: { [**id**: string]: [Appointment](/interfaces#appointment) }, **ids**: [] }

#### Resetting the user's password

> .resetPassword(**key**: string, **password**: string)

To reset a user's password, the first step is for the user to receive an email by going through the *forgot password* process. That email contains a *link* that redirects them to a page containing the *key* which should be passed into `.resetPassword` as the first argument.

If the call was a success, the user's password is reset according to the value provided as the second argument. In addition, this method will return a string value of `'success'`.

```js
const key = 'IjcyYzY5ZjMzLTZkMjgtNGUxYy04Y2Q3LWY0ZTczYjAzMTIzZSI..........'
const password = 'georgia1B24'
api.resetPassword(key, password)
  .then((result) => console.log(result))
```

Returns: `"success"`

#### Setting the `accountType`

> .setAccountType(accountType: string)

You can manually update/set the `accountType` by using the `setAccountType` method:

```js
client.setAccountType('staffing_company')
```

Returns the sdk instance itself.


#### Store

```ts
  interface Store {
    prevCalls: string[]
    accountType?: AccountType
  }
```

#### Setting availability dates

> .setAvailabilityDates(**dates**: [insert type here])

*This is a stub*

#### Updating an appointment

> .updateAppointment(**id**: string, **updates**: [UpdateAppointmentOptions](/interfaces#UpdateAppointmentOptions))

You can update an appointment by passing in the appointment `id` as the first argument, and passing in the new changes as an object as the second argument.

Only providers will be able to update an appointment. If the `accountType`is anything other than a `provider`, the method will return a rejected promise with the `WrongAccountType` error.

Only these keys are able to update an appointment: `purpose`, `document_present_history_illness`, `document_provider_note`, `document_prescription`, `document_absence`, and `document_legal_absence`. Other keys will be ignored by the backend.

If the call succeeds it will return the appointment object with the new changes.

Returns: [Appointment](/interfaces#Appointment)


#### Updating an appointment review

> .updateAppointmentReview(**params**: [AppointmentReview(/interfaces#AppointmentReview)])

You can update an appointment review by passing in parameters of the [AppointmentReview](/interfaces#AppointmentReview) object.

| Parameter              | Description                                             |
| ---------------------- | ------------------------------------------------------- |
| `appointment`?: string | The appointment id                                      |
| `rating`?: string      | Rating for the appointment/provider                     |
| `comment`?: string     | Comments/concerns of the patient about their experience |

Only patients are able to update their appointment review. If the `accountType`is anything other than a `patient`, the method will return a rejected promise with the `WrongAccountType` error.

If all parameters of the `review` object is not passed in, the function will return a rejected `InvalidParametersError` promise.

If the call succeeds it will return the updated appointment review object with the new changes. In addition, it will update the provider's `rating` and their *star rating*.

Returns: [AppointmentReview](/interfaces#AppointmentReview)

#### Updating the patient object in the backend

> .updateBackendPatient(**userId**: string, **changes**: [UpdatePatientOptions(/interfaces#UpdatePatientOptions)])

To update the patient object in the backend, the method `updateBackendPatient` can be used. The first parameter is the patient's `user_id` from the database. The second parameter is a `changes` object where desired updates to their account can be passed in to update their information.

```js
const userId = 'fafas2-faasf22-bfgsdf3-bbbba-lsdod3'
const changes = {
  languages: {
    'en-US': true
  },
  profile: {
    document_procedures_and_treatments: 'aaaaaaaaaaaaaaaaa'
  }
}
client.updateBackendPatient(userId, changes)
  .then((result) => console.log(result))
```

Returns the [backend patient object](/interfaces#PatientDocumentized) on success.

#### Updating a user's chase payment profile

> .updateChasePaymentProfile(**id**: string, **changes**: [ChasePaymentProfileParams](/interfaces#ChasePaymentProfileParams))

*This is a stub*

#### Updating a patient's document

> .updatePatientDocument(**docId**: string, **changes**: object)

To update a patient's document, you can call this method and pass in the *document id* as the first parameter, and *changes* as the second parameter. The method will throw and return a rejected promise if `changes` is anything other than an object. In addition, `docId` must be valid non-empty string type or it will also throw.

This will submit a `PUT` request to truevault to update that document and return an object of the document's `id` and the patient or provider's `tv_uid` on success.

```js
const docId = 'zzz3-sfds-cxvcvxm-343423'
const changes = {
  any_surgery_or_surgeries_in_the_past_bool: true,
  hospitalized_bool: true,
}
client.updatePatientDocument(docId, changes)
  .then((result) => {
    console.log(result)
  })
```

Returns: { **id**: string, **ownerId**: string }

#### Updating a provider

> .updateProvider(**userId**: string, **changes**: object)

To update information for a provider, this method must be invoked and provided with the provider's `userId` as the first parameter, and an object of `changes` as the second argument.

The account using this method must have permission rights to update this provider and will automatically be thrown with a rejected promise if he/she doesn't.

The [`changes`](/interfaces#UpdateProviderOptions) object is an object of any parameters that the user wishes to update the provider with in the database table.

```js
const userId = 'aaa1-sfds-pof23-343423-aasda'
const changes = {
  first_name: 'Harry,
  email: 'standford@ucla.edu,
}
client.updateProvider(userId, changes)
  .then((result) => console.log(result))
```

Returns: [Provider](/interfaces#provider)

#### Validating an inviter code

> .validateInviterCode(**code**: string)

*This is a stub*

#### Verifying a payment code

> .verifyPaymentCode(**code**: string)

To verify a payment code, you can invoke this method and pass in the payment code as the argument.

```js
const paymentCode = 'j9as82f'
client.verifyPaymentCode(paymentCode)
  .then((result) => console.log(result))
```

Returns:

