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
# 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"
}
# 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 trialtrial
mode is only available when the subscription has never been to anactive
status and has never been intrial
mode- when in
trial
mode,storageMax
,memberSeatsMax
cannot be changed - a
child
facility cannot be created when ontrial
mode
Example
# 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
# 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 currentstorageUsed
.memberSeatsMax
must be an non-negative integer. it cannot be less than the currentmemberSeatsOccupied
.
Examples
# 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
# 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
# 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, previousnon-active
branch subscriptions must be made active first
Example
# 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
# 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 generatedsub
id of the document associated to the JWTaccessToken
(optional) access token that can be used to re/query all relevant encoded data in the JWT
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 OrganizationMember
s 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
- eg:
<basePrivilege>Read
- eg:
membersRead
- read/fetch privilege
- eg:
<basePrivilege>Create
- eg:
membersCreate
- implies read/fetch privilege
- resource createion privilege
- eg:
<basePrivilege>Update
- eg:
membersUpdate
- implies read/fetch privilege
- resource patching (update properties) privilege
- eg:
<basePrivilege>Remove
- eg:
membersUpdate
- implies read/fetch privilege
- resource removal privilege
- eg:
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) theorganization
filter to use in searchingdoc_specialties
(optional) thedoc_specialties
filter to use in searchingtext
(optional) the string to use for searching the members's nameskip
(optional, defaults toquery.$skip
) the skip to use in search the member's personal detailslimit
(optional, defaults toquery.$limit
) the limit to use in search the member's personal detailssort
(optional) the sort configuration to use in search the member's personal details