Skip to content

Organizations

Facilities

A facility is an Organization that has a type: facility. In this guide you will learn how to create main and branch facilities, start/cancel a trial/active subscription.

As soon as you verify an account's login credential(s), it can be used to create a facility (though only one main facility is allowed to be associated to an account). That account will then be the owner of the subscription for said facility and be the only one allowed to manipulate it.

If no subscription payload is supplied when creating the facility, an inactive subscription will be auto created

Creating a facility

A facility (Organization with type: 'facility') can be created using the organizations API's create method. it can optionally contain a subscription object w/c will be used to create its subscription

Example

sh
# Creating a free facility
curl <base-uri>/organizations \
-X POST \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer <access_token>",
-d "{
  "type": "facility",
  "types": ["aesthetics-clinic"], // for specialized facilities
  "name": "My Clinic",
  "superadmin": "account-uid-her"
}"

# Response
{
  "id": "facility-id",
  "createdAt": 12345567, // js timestamp
  "createdBy": "some-uid",
  "type": "facility",
  "types": ["aesthetics-clinic"],
  "name": "My Clinic"
}
# Creating a free facility
curl <base-uri>/organizations \
-X POST \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer <access_token>",
-d "{
  "type": "facility",
  "types": ["aesthetics-clinic"], // for specialized facilities
  "name": "My Clinic",
  "superadmin": "account-uid-her"
}"

# Response
{
  "id": "facility-id",
  "createdAt": 12345567, // js timestamp
  "createdBy": "some-uid",
  "type": "facility",
  "types": ["aesthetics-clinic"],
  "name": "My Clinic"
}
sh
# Creating a free facility while signing up
curl <base-uri>/accounts \
-X POST \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer <access_token>",
-d "{
  "email": "email@domain.com",
  "password": "password",
  "personalDetails": {
    "name": {
      "firstName": "Name",
      "lastName": "Last"
    }
  },
  "organization": {
    "type": "facility",
    "types": ["aesthetics-clinic"], // for specialized facilities
    "name": "My Clinic",
    // optional subscription for starting trial/paid subscription instead of free
    // "subscription": {
    //   ...payload
    // }
  }
}"

# Response
{
  "uid": "some-id",
  "createdAt": 12345567, // js timestamp
  "email": "email@domain.com",
  "organization": {
    "id": "facility-id",
    "createdAt": 12345567, // js timestamp
    "createdBy": "some-uid",
    "type": "facility",
    "types": ["aesthetics-clinic"],
    "name": "My Clinic"
    // optional subscription object
    // "subscription": { ...payload }
  }
}
# Creating a free facility while signing up
curl <base-uri>/accounts \
-X POST \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer <access_token>",
-d "{
  "email": "email@domain.com",
  "password": "password",
  "personalDetails": {
    "name": {
      "firstName": "Name",
      "lastName": "Last"
    }
  },
  "organization": {
    "type": "facility",
    "types": ["aesthetics-clinic"], // for specialized facilities
    "name": "My Clinic",
    // optional subscription for starting trial/paid subscription instead of free
    // "subscription": {
    //   ...payload
    // }
  }
}"

# Response
{
  "uid": "some-id",
  "createdAt": 12345567, // js timestamp
  "email": "email@domain.com",
  "organization": {
    "id": "facility-id",
    "createdAt": 12345567, // js timestamp
    "createdBy": "some-uid",
    "type": "facility",
    "types": ["aesthetics-clinic"],
    "name": "My Clinic"
    // optional subscription object
    // "subscription": { ...payload }
  }
}

Adding / Replacing Credit Card

Sources (credit card) operations can be done using the billing customers API

Stripe Subscription details

By adding the query operator $stripeSubscriptionExpand: true in the query params, subscriptions API's find and get method will populate the field stripeSubscription with the stripe Subscription object

Stripe Customer details

By adding the query operator $stripeCustomerExpand: true in the query params, subscriptions API's find and get method will populate the field stripeCustomer with the stripe Customer object

Starting a trial subscription

A trial subscription can be made only when there are no previous active subscription for the facility where the trial subscription is intended. A trial subscription can be started using the subscriptions API's create or patch method by providing a trial flag as one of the fields in the payload

Caveats

  • storageMax, memberSeatsMax cannot be set greater the their default (free) values when starting a trial
  • trial mode is only available when the subscription has never been to an active status and has never been in trial mode
  • when in trial mode, storageMax, memberSeatsMax cannot be changed
  • a child facility cannot be created when on trial mode

Example

sh
# Creating a free facility with trial subscription
curl <base-uri>/organizations \
-X POST \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer <access_token>",
-d "{
  "type": "facility",
  "types": ["aesthetics-clinic"], // for specialized facilities
  "name": "My Clinic",
  "subscription": {
    // "storageMax": 2, // this will throw an error if included #refer to caveat
    "trial": true, // aside from the caveates, this is the only difference between trial and paid
    "lis": true, // if no billable flag was toggled, no stripe session will be created
    "ris": true
  }
}"

# Response
{
  "id": "facility-id",
  "createdAt": 12345567, // js timestamp
  "createdBy": "some-uid",
  "type": "facility",
  "types": ["aesthetics-clinic"],
  "name": "My Clinic",
  "subscription": {
    "id": "facility-id",
    "createdAt": 12345567, // js timestamp
    "createdBy": "some-uid",
    "lis": false,
    "ris": false,
    "pendingUpdates": {
      "stripeSession": "some-session-id",
      "lis": true,
      "ris": true
    }
  }
}
# Creating a free facility with trial subscription
curl <base-uri>/organizations \
-X POST \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer <access_token>",
-d "{
  "type": "facility",
  "types": ["aesthetics-clinic"], // for specialized facilities
  "name": "My Clinic",
  "subscription": {
    // "storageMax": 2, // this will throw an error if included #refer to caveat
    "trial": true, // aside from the caveates, this is the only difference between trial and paid
    "lis": true, // if no billable flag was toggled, no stripe session will be created
    "ris": true
  }
}"

# Response
{
  "id": "facility-id",
  "createdAt": 12345567, // js timestamp
  "createdBy": "some-uid",
  "type": "facility",
  "types": ["aesthetics-clinic"],
  "name": "My Clinic",
  "subscription": {
    "id": "facility-id",
    "createdAt": 12345567, // js timestamp
    "createdBy": "some-uid",
    "lis": false,
    "ris": false,
    "pendingUpdates": {
      "stripeSession": "some-session-id",
      "lis": true,
      "ris": true
    }
  }
}

Starting with paid subscription

A paid subscription can be made only when there are no previous subscription associated to the owning account. A paid subscription can be started using the subscriptions API's create or patch method by providing flags for billable modules and/or providing values for storageMax, memberSeatsMax that are greater than the base/free values (currently all 1 for all). the returned Subscription object will then contain a Subscription.updatesPending.stripeSession field w/c will contain the stripe session that can be used by the receiving client to redirect the user (via stripe checkout) to a page where billing details will be collected (implicitly confirming availment intent). A stripe webhook will then be fired by stripe that will be handled by the API w/c will update the current subscription (optionally applying pendingUpdates to current subscription if the status is either active or trialing)

Caveats

  • only one top-level (not child) facility can be made per account.

Examples

sh
# Creating a facility with paid subscription
curl <base-uri>/organizations \
-X POST \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer <access_token>",
-d "{
  "type": "facility",
  "types": ["aesthetics-clinic"], // for specialized facilities
  "name": "My Clinic",
  "subscription": {
    "storageMax": 2, // this will be billed as `n - base` storage plan, where base is the free value (1)
    "lis": true, // if no billable flag or billable additions (storage, member seats) was toggled, no stripe session will be created
    "ris": true,
  },
}"

# Response
{
  "id": "facility-id",
  "createdAt": 12345567, // js timestamp
  "createdBy": "some-uid",
  "type": "facility",
  "types": ["aesthetics-clinic"],
  "name": "My Clinic",
  "subscription": {
    "id": "facility-id",
    "createdAt": 12345567, // js timestamp
    "createdBy": "some-uid",
    "lis": false,
    "ris": false,
    "pendingUpdates": {
      "stripeSession": "some-session-id",
      "storageMax": 2,
      "lis": true,
      "ris": true,
    },
  },
}
# Creating a facility with paid subscription
curl <base-uri>/organizations \
-X POST \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer <access_token>",
-d "{
  "type": "facility",
  "types": ["aesthetics-clinic"], // for specialized facilities
  "name": "My Clinic",
  "subscription": {
    "storageMax": 2, // this will be billed as `n - base` storage plan, where base is the free value (1)
    "lis": true, // if no billable flag or billable additions (storage, member seats) was toggled, no stripe session will be created
    "ris": true,
  },
}"

# Response
{
  "id": "facility-id",
  "createdAt": 12345567, // js timestamp
  "createdBy": "some-uid",
  "type": "facility",
  "types": ["aesthetics-clinic"],
  "name": "My Clinic",
  "subscription": {
    "id": "facility-id",
    "createdAt": 12345567, // js timestamp
    "createdBy": "some-uid",
    "lis": false,
    "ris": false,
    "pendingUpdates": {
      "stripeSession": "some-session-id",
      "storageMax": 2,
      "lis": true,
      "ris": true,
    },
  },
}

Availing/Discontuing additional modules, storage capacity, member seats

Active subscriptions can add/remove modules, storage capacity, member seats by updating their Subscription using the subscriptions API's patch method.

Caveats

  • only active subscriptions can add additional modules, storage capacity, and seats
  • storageMax must be an non-negative integer. it cannot be less than the current storageUsed.
  • memberSeatsMax must be an non-negative integer. it cannot be less than the current memberSeatsOccupied.

Examples

sh
# Adding/removing modules, storage, and/or seats
curl <base-uri>/subscriptions/sub-id \
-X PATCH \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer <access_token>",
-d "{
  "storageMax": 1, // move back to free. remove from billable subcription items
  "memberSeatsMax": 3, // add additional seat. (n - base) is billable, where base is the free value (1)
  "lis": false, // discontinue LIS
  "pme": true, // enable PME module
}"

# Response
{
  "id": "sub-id",
  "createdAt": 12345567, // js timestamp
  "createdBy": "some-uid",
  "status": "active",
  "storageMax": 1,
  "memberSeatsMax": 3,
  "lis": false,
  "ris": true,
  "pme": true,
  "stripeSubscription": "stripe-subs-id",
  "stripeCustomer": "stripe-customer-id"
}
# Adding/removing modules, storage, and/or seats
curl <base-uri>/subscriptions/sub-id \
-X PATCH \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer <access_token>",
-d "{
  "storageMax": 1, // move back to free. remove from billable subcription items
  "memberSeatsMax": 3, // add additional seat. (n - base) is billable, where base is the free value (1)
  "lis": false, // discontinue LIS
  "pme": true, // enable PME module
}"

# Response
{
  "id": "sub-id",
  "createdAt": 12345567, // js timestamp
  "createdBy": "some-uid",
  "status": "active",
  "storageMax": 1,
  "memberSeatsMax": 3,
  "lis": false,
  "ris": true,
  "pme": true,
  "stripeSubscription": "stripe-subs-id",
  "stripeCustomer": "stripe-customer-id"
}

Cancelling a trial susbcription

A trial Subscription can be cancelled (making the subscription active, thus billing the customer) using the subscriptions API's patch method by providing a trial: false flag in the update payload.

Examples

sh
# Cancelling a trial subscription (subscription with `status`: 'trialing')
curl <base-uri>/subscriptions/sub-id \
-X PATCH \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer <access_token>",
-d "{
  "trial": false // stop trial, proceed to paid subscription
}"

# Response
{
  "id": "sub-id",
  "createdAt": 12345567, // js timestamp
  "createdBy": "some-uid",
  "status": "active", // status becoms 'active' from 'trialing'
  "storageMax": 1,
  "trialStartedAt": 12345567,
  "trialEndAt": 12345567,
  "memberSeatsMax": 3,
  "lis": true,
  "ris": true,
  "pme": true,
  "stripeSubscription": "stripe-subs-id",
  "stripeCustomer": "stripe-customer-id"
}
# Cancelling a trial subscription (subscription with `status`: 'trialing')
curl <base-uri>/subscriptions/sub-id \
-X PATCH \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer <access_token>",
-d "{
  "trial": false // stop trial, proceed to paid subscription
}"

# Response
{
  "id": "sub-id",
  "createdAt": 12345567, // js timestamp
  "createdBy": "some-uid",
  "status": "active", // status becoms 'active' from 'trialing'
  "storageMax": 1,
  "trialStartedAt": 12345567,
  "trialEndAt": 12345567,
  "memberSeatsMax": 3,
  "lis": true,
  "ris": true,
  "pme": true,
  "stripeSubscription": "stripe-subs-id",
  "stripeCustomer": "stripe-customer-id"
}

Cancelling an active subscription

An active Subscription can be cancelled (returning everything to free mode) using the subscriptions API's patch method by providing a cancel: true flag in the update payload.

Examples

sh
# Creating a free facility with trial subscription
curl <base-uri>/subscriptions/sub-id \
-X PATCH \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer <access_token>",
-d "{
  "cancel": true // stop subscription, reverting to free facility
}"

# Response
{
  "id": "sub-id",
  "createdAt": 12345567, // js timestamp
  "createdBy": "some-uid",
  "status": "canceled",
  "storageMax": 1,
  "memberSeatsMax": 1,
  "lis": false,
  "ris": false,
  "pme": false,
  "stripeSubscription": "stripe-subs-id",
  "stripeCustomer": "stripe-customer-id"
}
# Creating a free facility with trial subscription
curl <base-uri>/subscriptions/sub-id \
-X PATCH \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer <access_token>",
-d "{
  "cancel": true // stop subscription, reverting to free facility
}"

# Response
{
  "id": "sub-id",
  "createdAt": 12345567, // js timestamp
  "createdBy": "some-uid",
  "status": "canceled",
  "storageMax": 1,
  "memberSeatsMax": 1,
  "lis": false,
  "ris": false,
  "pme": false,
  "stripeSubscription": "stripe-subs-id",
  "stripeCustomer": "stripe-customer-id"
}

Creating a branch facility

A branch Facility can be created using the organizations API's create method by providing a parent field w/c is the id of the parent facility. it can optionally contain a subscription object w/c will be used to create its subscription

Caveats

  • only one (1) branch with non-active subscription is allowed to be created. If one wishes to create additional branches, previous non-active branch subscriptions must be made active first

Example

sh
# Creating a branch facility
curl <base-uri>/organizations \
-X POST \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer <access_token>",
-d "{
  "parent": "parent-facility-id",
  "type": "facility",
  "types": ["aesthetics-clinic"], // for specialized facilities
  "name": "My Clinic",
  "subscription": {
    // the same BillingCustomer can be used as the one used/autocreated
    // with the subscription of the main facility
    "customer": "parent-subscription-customer",
    // any other field, same rule from "stargin trial/paid" subscription applies
  },
}"

# Response
{
  "id": "facility-id",
  "createdAt": 12345567, // js timestamp
  "createdBy": "some-uid",
  "type": "facility",
  "types": ["aesthetics-clinic"],
  "name": "My Clinic"
}
# Creating a branch facility
curl <base-uri>/organizations \
-X POST \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer <access_token>",
-d "{
  "parent": "parent-facility-id",
  "type": "facility",
  "types": ["aesthetics-clinic"], // for specialized facilities
  "name": "My Clinic",
  "subscription": {
    // the same BillingCustomer can be used as the one used/autocreated
    // with the subscription of the main facility
    "customer": "parent-subscription-customer",
    // any other field, same rule from "stargin trial/paid" subscription applies
  },
}"

# Response
{
  "id": "facility-id",
  "createdAt": 12345567, // js timestamp
  "createdBy": "some-uid",
  "type": "facility",
  "types": ["aesthetics-clinic"],
  "name": "My Clinic"
}

QR Code

Generates a JWT encoding selected data (set per service) including an access token that can be used to fetch the associated document without loggin in a privileged user.

Encoding

sh
# trigger the operator
GET /organizations/randomid?$generateQRCode=true

# responsed with a JWT which is expected to be encoded
# in the fronted to a QR image
ey****.***.***
# trigger the operator
GET /organizations/randomid?$generateQRCode=true

# responsed with a JWT which is expected to be encoded
# in the fronted to a QR image
ey****.***.***

Scanning and Parsing

The QR code must be decoded and parsed as a JWT. This JWT contains the ff standard fields (w/c may include additional fields depending on the service)

  • typ the type of the JWT which is usually the service where the QR code was generated
  • sub id of the document associated to the JWT
  • accessToken (optional) access token that can be used to re/query all relevant encoded data in the JWT
javascript
const decodedFromQRCode = 'ey***.***.****';
const payload = parsedJWTPayload(decodedFromQRCode);
// { typ: 'organization', sub: 'randomid', name: 'Facility name', accessToken: 'ey****.***.***' }

const requeried = await http.get(`/organizations/${payload.sub}`, {
  headers: { Authorization: `Bearer ${payload.accessToken}` }
});
// { id: 'randomid', ... }
const decodedFromQRCode = 'ey***.***.****';
const payload = parsedJWTPayload(decodedFromQRCode);
// { typ: 'organization', sub: 'randomid', name: 'Facility name', accessToken: 'ey****.***.***' }

const requeried = await http.get(`/organizations/${payload.sub}`, {
  headers: { Authorization: `Bearer ${payload.accessToken}` }
});
// { id: 'randomid', ... }

Organization Member

Each Organization will have 1 or more OrganizationMembers that would represent the roles and permissions/privileges of each user member. It would also contain configurations specific to users (e.g., associated stockRooms for inventory staffs, withholding tax for doctors)

Roles


Roles are used primarily by client apps, privileges are what the server uses for authorization. Usually, roles are used by client apps to represent a group of privileges (eg. doctor_* roles should have med_records privilege). Some Roles used:

  • admin
  • admin_analyst
  • proofreader
  • releasing
  • frontdesk_head
  • frontdesk
  • frontdesk_encoder
  • nurse_head
  • nurse
  • therapist
  • doctor
  • doctor_pathologist
  • doctor_radiologist
  • doctor_sonologist
  • doctor_pme
  • lab_tech
  • lab_qc
  • lab_head
  • imaging_tech
  • imaging_qc
  • imaging_head
  • billing
  • billing_encoder
  • billing_head
  • warehouse
  • warehouse_manager
  • warehouse_head
  • pme_head
  • pme_staff

Privileges


Privileges are what the server uses for authorizing client requests. aside from the special privileges (superadmin, admin), most privileges follow the following pattern:

  • <basePrivilege>
    • eg: members (privilege to manage facility members)
    • equivalent to having all sub privilege of the same base privilege
  • <basePrivilege>Read
    • eg: membersRead
    • read/fetch privilege
  • <basePrivilege>Create
    • eg: membersCreate
    • implies read/fetch privilege
    • resource createion privilege
  • <basePrivilege>Update
    • eg: membersUpdate
    • implies read/fetch privilege
    • resource patching (update properties) privilege
  • <basePrivilege>Remove
    • eg: membersUpdate
    • implies read/fetch privilege
    • resource removal privilege

Inviting A Facility Member


A facility membership invitation can be created using the account-invitations API's create method with a type of org-membership. As soon as it is accepted (or a user registers using sent invitation), the facility memebrship will be created.

Creating, Updating, Removing A Facility Member


A facility membership can be created, updated, and removed using the organization-members API's create, update, and remove methods respectively using an account with the correct privileges (members or membersCreate, membersUpdate, and membersRemove);

Membership Visibility


facility memebers can be searched only if they are configured to be public (flagged using the field isPublic)

Searching/Filtering/Sorting


Text searching/filtering via the member's personal-details can be done using the $search read (GET) operator The $search operator can include the following fields:

  • organization (required) the organization filter to use in searching
  • doc_specialties (optional) the doc_specialties filter to use in searching
  • text (optional) the string to use for searching the members's name
  • skip (optional, defaults to query.$skip) the skip to use in search the member's personal details
  • limit (optional, defaults to query.$limit) the limit to use in search the member's personal details
  • sort (optional) the sort configuration to use in search the member's personal details

Released under the MIT License.