MENU navbar-image

Introduction

The MYBOS building-management API.

Every request is made on behalf of a principal -- a user, or a company's service account -- and nearly all data is limited to the buildings that principal can reach. See the Authentication section for how to get a token before calling anything else.

Conventions

Responses follow JSON:API shapes (data/errors, type/id/attributes/relationships, meta, links) and are served as plain application/json. The API is not a fully compliant JSON:API server: it does not implement JSON:API content negotiation.

Authenticating requests

To authenticate requests, include an Authorization header with the value "Bearer {ACCESS_TOKEN}". Endpoints that don't require this are marked unauthenticated.

How to get an access token

This API does not issue tokens. Access tokens are short-lived signed tokens issued by the MYBOS Auth service; this API only verifies them.

A service account is a login for your company's own system -- a reporting job, a data sync -- rather than for a person. It keeps working when staff change.

1. Create the service account. In the MYBOS company portal, go to Settings › API / Service Accounts and choose New Service Account. You get a client ID and a client secret. The secret is shown once: store it like a password. It expires after one year; create a new one before then.

2. Exchange them for an access token at the Auth service's token endpoint, as a form post:

curl -s https://auth.mybos.com/oauth/token \
  -H 'Accept: application/json' \
  -d grant_type=client_credentials \
  -d client_id='YOUR_CLIENT_ID' \
  -d client_secret='YOUR_CLIENT_SECRET'
{
  "token_type": "Bearer",
  "expires_in": 900,
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIs..."
}

3. Call this API with the token in the Authorization header:

curl -s https://keystone.mybos.com/api/v1/cases \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer ACCESS_TOKEN'

4. Get a new token when it expires. Tokens last expires_in seconds (15 minutes). There is no refresh token for service accounts: repeat step 2. Keep the client ID and secret in your system, not the token.

A service-account token acts as the company: it can read data in the buildings the company owns, and cannot change anything.

If step 2 fails with 401 invalid_client, the client ID or secret is wrong, the secret has expired, or the service account was revoked.

Environment Auth service (token endpoint) This API
Production https://auth.mybos.com/oauth/token https://keystone.mybos.com
Staging https://auth.stg.mybos.com/oauth/token https://keystone.stg.mybos.com

Other ways to get a token

What a token can reach

A token's access is decided per request, from the principal it names: the buildings that principal can reach, and the permissions it holds in each (for example, reading cases). The same credential sees the same data whichever endpoint it calls.

An expired token is refused with 401 token_expired; any other invalid or missing token with 401 invalid_token.

Credential

Who am I

requires authentication

Describes the principal this token acts for -- a user, or a company's service account -- and the session the token carries.

Example request:
curl --request GET \
    --get "https://keystone.stg.mybos.com/api/v1/me" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Accept: application/json"
const url = new URL(
    "https://keystone.stg.mybos.com/api/v1/me"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200, A user's token):


{
    "data": {
        "type": "users",
        "id": "6650a0e9c4e8b90012ab3401",
        "attributes": {
            "firstName": "Alex",
            "lastName": "Taylor",
            "email": "alex.taylor@example.com",
            "username": "alex.taylor",
            "group": "BUILDING MANAGER",
            "status": "Active"
        }
    },
    "meta": {
        "session": {
            "subject": "9d1c6f0e-2b7a-4c1e-8f3a-5b6d7e8f9a01",
            "audience": "00000000-0000-7000-9000-000000000001",
            "ownerType": "user",
            "ownerId": "9d1c6f0e-2b7a-4c1e-8f3a-5b6d7e8f9a01",
            "scopes": [],
            "expiresAt": "2026-10-27T10:00:00+00:00"
        }
    },
    "links": {
        "self": "/api/v1/me"
    }
}
 

Example response (401, Missing, invalid or expired token):


{
    "errors": [
        {
            "status": "401",
            "code": "invalid_token",
            "title": "Unauthenticated"
        }
    ]
}
 

Request      

GET api/v1/me

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Accept        

Example: application/json

Buildings

List buildings

requires authentication

The buildings this credential can reach, or the named ones. Their identifiers are what every buildings parameter takes.

Example request:
curl --request GET \
    --get "https://keystone.stg.mybos.com/api/v1/buildings?buildings=6650a1f2c4e8b90012ab34c1&page%5Bsize%5D=50&page%5Bcursor%5D=eyJmIjoiOGQ5ZiIsImQiOiJuZXh0IiwidiI6W3siJG9pZCI6IjY3MTJlOGIxZDFjM2U1MDAxYTJiM2MxMSJ9XX0" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Accept: application/json"
const url = new URL(
    "https://keystone.stg.mybos.com/api/v1/buildings"
);

const params = {
    "buildings": "6650a1f2c4e8b90012ab34c1",
    "page[size]": "50",
    "page[cursor]": "eyJmIjoiOGQ5ZiIsImQiOiJuZXh0IiwidiI6W3siJG9pZCI6IjY3MTJlOGIxZDFjM2U1MDAxYTJiM2MxMSJ9XX0",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "data": [
        {
            "type": "buildings",
            "id": "6650a1f2c4e8b90012ab34c1",
            "attributes": {
                "name": "Harbour View",
                "plan": "SP 12345",
                "units": 120,
                "timezone": "Australia/Sydney",
                "status": "Active"
            }
        }
    ],
    "meta": {
        "total": 1,
        "scope": {
            "origin": "defaulted"
        }
    },
    "links": {
        "self": "/api/v1/buildings",
        "first": "/api/v1/buildings",
        "next": null,
        "prev": null
    }
}
 

Example response (400, A cursor from a different request):


{
    "errors": [
        {
            "status": "400",
            "code": "invalid_cursor",
            "title": "Invalid cursor",
            "detail": "The cursor does not belong to this query. Restart without page[cursor].",
            "source": {
                "parameter": "page[cursor]"
            }
        }
    ]
}
 

Example response (401, Missing, invalid or expired token):


{
    "errors": [
        {
            "status": "401",
            "code": "invalid_token",
            "title": "Unauthenticated"
        }
    ]
}
 

Example response (403, A named building is not accessible):


{
    "errors": [
        {
            "status": "403",
            "code": "building_not_accessible",
            "title": "Forbidden",
            "detail": "One or more requested buildings are not accessible with this credential.",
            "meta": {
                "buildings": [
                    "6650a1f2c4e8b90012ab34c2"
                ]
            }
        }
    ]
}
 

Example response (422, A malformed building identifier):


{
    "errors": [
        {
            "status": "422",
            "code": "validation_failed",
            "title": "Unprocessable",
            "detail": "Each building identifier is malformed.",
            "source": {
                "parameter": "buildings"
            }
        }
    ]
}
 

Request      

GET api/v1/buildings

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Accept        

Example: application/json

Query Parameters

buildings   string  optional    

Comma-separated building identifiers, from GET /api/v1/buildings. Omit to cover every building this credential can reach. Example: 6650a1f2c4e8b90012ab34c1

page[size]   integer  optional    

How many results to return per page, from 1 to 250. Defaults to 50. Example: 50

page[cursor]   string  optional    

Continue a listing. Take it from links.next or links.prev as given: it is opaque, and only valid for the request that produced it. Example: eyJmIjoiOGQ5ZiIsImQiOiJuZXh0IiwidiI6W3siJG9pZCI6IjY3MTJlOGIxZDFjM2U1MDAxYTJiM2MxMSJ9XX0

Cases

Scribe documents the {case} placeholder as {id}, so the tag must be named id to attach to it (see the openapi-scribe-docs skill).

List cases

requires authentication

Maintenance and work cases in the buildings where this credential may read them, in every state, newest first. A building the credential can reach but may not read cases in is left out, unless it was named in buildings, which fails the request instead.

Filters and sorting are not offered yet: any filter[...] or sort is refused rather than ignored.

Example request:
curl --request GET \
    --get "https://keystone.stg.mybos.com/api/v1/cases?buildings=6650a1f2c4e8b90012ab34c1&page%5Bsize%5D=50&page%5Bcursor%5D=eyJmIjoiOGQ5ZiIsImQiOiJuZXh0IiwidiI6W3siJG9pZCI6IjY3MTJlOGIxZDFjM2U1MDAxYTJiM2MxMSJ9XX0" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Accept: application/json"
const url = new URL(
    "https://keystone.stg.mybos.com/api/v1/cases"
);

const params = {
    "buildings": "6650a1f2c4e8b90012ab34c1",
    "page[size]": "50",
    "page[cursor]": "eyJmIjoiOGQ5ZiIsImQiOiJuZXh0IiwidiI6W3siJG9pZCI6IjY3MTJlOGIxZDFjM2U1MDAxYTJiM2MxMSJ9XX0",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "data": [
        {
            "type": "cases",
            "id": "6712f0a4d1c3e5001a2b3c4d",
            "attributes": {
                "number": 1042,
                "subject": "Leak in level 3 corridor",
                "detail": "<p>Water is leaking from the ceiling in the level 3 corridor.</p>",
                "history_note": null,
                "areas": [
                    "common_asset"
                ],
                "state": "open",
                "starred": true,
                "starts_at": null,
                "due_at": "2026-10-27T07:00:00+00:00",
                "completed_at": null,
                "purchase_order_number": null,
                "work_order": "WO-1042",
                "logged_by": "Concierge",
                "created_at": "2026-10-20T03:15:00+00:00",
                "updated_at": "2026-10-20T03:15:00+00:00"
            },
            "relationships": {
                "building": {
                    "data": {
                        "type": "buildings",
                        "id": "6650a1f2c4e8b90012ab34c1"
                    }
                },
                "status": {
                    "data": {
                        "type": "case-statuses",
                        "id": "6650a2a0c4e8b90012ab3502"
                    }
                },
                "type": {
                    "data": {
                        "type": "case-types",
                        "id": "6650a2b5c4e8b90012ab3601"
                    }
                },
                "priority": {
                    "data": {
                        "type": "case-priorities",
                        "id": "6650a2c9c4e8b90012ab3701"
                    }
                }
            }
        },
        {
            "type": "cases",
            "id": "6712e8b1d1c3e5001a2b3c11",
            "attributes": {
                "number": 1041,
                "subject": "Replace foyer light",
                "detail": "<p>The foyer downlight near the lifts has failed.</p>",
                "history_note": null,
                "areas": [
                    "common_non_asset"
                ],
                "state": "completed",
                "starred": false,
                "starts_at": null,
                "due_at": "2026-10-19T07:00:00+00:00",
                "completed_at": "2026-10-19T05:30:00+00:00",
                "purchase_order_number": null,
                "work_order": "WO-1038",
                "logged_by": "Concierge",
                "created_at": "2026-10-18T22:40:00+00:00",
                "updated_at": "2026-10-18T22:40:00+00:00"
            },
            "relationships": {
                "building": {
                    "data": {
                        "type": "buildings",
                        "id": "6650a1f2c4e8b90012ab34c1"
                    }
                },
                "status": {
                    "data": {
                        "type": "case-statuses",
                        "id": "6650a2a0c4e8b90012ab3504"
                    }
                },
                "type": {
                    "data": {
                        "type": "case-types",
                        "id": "6650a2b5c4e8b90012ab3602"
                    }
                },
                "priority": {
                    "data": {
                        "type": "case-priorities",
                        "id": "6650a2c9c4e8b90012ab3702"
                    }
                }
            }
        }
    ],
    "meta": {
        "total": 62,
        "scope": {
            "origin": "defaulted"
        }
    },
    "links": {
        "self": "/api/v1/cases?page%5Bsize%5D=2",
        "first": "/api/v1/cases?page%5Bsize%5D=2",
        "next": "/api/v1/cases?page%5Bsize%5D=2&page%5Bcursor%5D=eyJmIjoiOGQ5ZiIsImQiOiJuZXh0IiwidiI6W3siJG9pZCI6IjY3MTJlOGIxZDFjM2U1MDAxYTJiM2MxMSJ9XX0",
        "prev": null
    }
}
 

Example response (400, A filter or sort was sent):


{
    "errors": [
        {
            "status": "400",
            "code": "invalid_filter",
            "title": "Bad Request",
            "detail": "This collection offers no filters.",
            "source": {
                "parameter": "filter[state]"
            }
        }
    ]
}
 

Example response (400, A cursor from a different request):


{
    "errors": [
        {
            "status": "400",
            "code": "invalid_cursor",
            "title": "Invalid cursor",
            "detail": "The cursor does not belong to this query. Restart without page[cursor].",
            "source": {
                "parameter": "page[cursor]"
            }
        }
    ]
}
 

Example response (401, Missing, invalid or expired token):


{
    "errors": [
        {
            "status": "401",
            "code": "invalid_token",
            "title": "Unauthenticated"
        }
    ]
}
 

Example response (403, A named building is not accessible):


{
    "errors": [
        {
            "status": "403",
            "code": "building_not_accessible",
            "title": "Forbidden",
            "detail": "One or more requested buildings are not accessible with this credential.",
            "meta": {
                "buildings": [
                    "6650a1f2c4e8b90012ab34c2"
                ]
            }
        }
    ]
}
 

Example response (403, Cases may not be read in a named building):


{
    "errors": [
        {
            "status": "403",
            "code": "forbidden",
            "title": "Forbidden",
            "detail": "This credential is not permitted to read cases in one or more of the requested buildings."
        }
    ]
}
 

Example response (422, An invalid page size):


{
    "errors": [
        {
            "status": "422",
            "code": "validation_failed",
            "title": "Unprocessable",
            "detail": "The page[size] must be a whole number between 1 and 250.",
            "source": {
                "parameter": "page[size]"
            }
        }
    ]
}
 

Request      

GET api/v1/cases

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Accept        

Example: application/json

Query Parameters

buildings   string  optional    

Comma-separated building identifiers, from GET /api/v1/buildings. Omit to cover every building this credential can reach. Example: 6650a1f2c4e8b90012ab34c1

page[size]   integer  optional    

How many results to return per page, from 1 to 250. Defaults to 50. Example: 50

page[cursor]   string  optional    

Continue a listing. Take it from links.next or links.prev as given: it is opaque, and only valid for the request that produced it. Example: eyJmIjoiOGQ5ZiIsImQiOiJuZXh0IiwidiI6W3siJG9pZCI6IjY3MTJlOGIxZDFjM2U1MDAxYTJiM2MxMSJ9XX0

Get a case

requires authentication

One case. A case that does not exist and one this credential cannot reach are both 404, so the response never reveals that a case exists.

Example request:
curl --request GET \
    --get "https://keystone.stg.mybos.com/api/v1/cases/6712f0a4d1c3e5001a2b3c4d" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Accept: application/json"
const url = new URL(
    "https://keystone.stg.mybos.com/api/v1/cases/6712f0a4d1c3e5001a2b3c4d"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "data": {
        "type": "cases",
        "id": "6712f0a4d1c3e5001a2b3c4d",
        "attributes": {
            "number": 1042,
            "subject": "Leak in level 3 corridor",
            "detail": "<p>Water is leaking from the ceiling in the level 3 corridor.</p>",
            "history_note": null,
            "areas": [
                "common_asset"
            ],
            "state": "open",
            "starred": true,
            "starts_at": null,
            "due_at": "2026-10-27T07:00:00+00:00",
            "completed_at": null,
            "purchase_order_number": null,
            "work_order": "WO-1042",
            "logged_by": "Concierge",
            "created_at": "2026-10-20T03:15:00+00:00",
            "updated_at": "2026-10-20T03:15:00+00:00"
        },
        "relationships": {
            "building": {
                "data": {
                    "type": "buildings",
                    "id": "6650a1f2c4e8b90012ab34c1"
                }
            },
            "status": {
                "data": {
                    "type": "case-statuses",
                    "id": "6650a2a0c4e8b90012ab3502"
                }
            },
            "type": {
                "data": {
                    "type": "case-types",
                    "id": "6650a2b5c4e8b90012ab3601"
                }
            },
            "priority": {
                "data": {
                    "type": "case-priorities",
                    "id": "6650a2c9c4e8b90012ab3701"
                }
            }
        }
    },
    "meta": {},
    "links": {
        "self": "/api/v1/cases/6712f0a4d1c3e5001a2b3c4d"
    }
}
 

Example response (401, Missing, invalid or expired token):


{
    "errors": [
        {
            "status": "401",
            "code": "invalid_token",
            "title": "Unauthenticated"
        }
    ]
}
 

Example response (403, The case is reachable, but cases may not be read in its building):


{
    "errors": [
        {
            "status": "403",
            "code": "forbidden",
            "title": "Forbidden",
            "detail": "This credential is not permitted to read this case."
        }
    ]
}
 

Example response (404, No such case, or not reachable):


{
    "errors": [
        {
            "status": "404",
            "code": "not_found",
            "title": "Not Found",
            "detail": "No case with that id is accessible to this credential."
        }
    ]
}
 

Request      

GET api/v1/cases/{id}

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Accept        

Example: application/json

URL Parameters

id   string     

The case's identifier, from GET /api/v1/cases. Example: 6712f0a4d1c3e5001a2b3c4d

List a case's activity

requires authentication

What has happened to one case, newest first: each entry says what changed, who did it and when. by is null when the person no longer exists. The order is fixed: any filter[...] or sort is refused rather than ignored.

A case that does not exist and one this credential cannot reach are both 404, so the response never reveals that a case exists.

Example request:
curl --request GET \
    --get "https://keystone.stg.mybos.com/api/v1/cases/6712f0a4d1c3e5001a2b3c4d/activity?page%5Bsize%5D=50&page%5Bcursor%5D=eyJmIjoiOGQ5ZiIsImQiOiJuZXh0IiwidiI6WzE3MjkzOTQxMDAwMDAsM119" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Accept: application/json"
const url = new URL(
    "https://keystone.stg.mybos.com/api/v1/cases/6712f0a4d1c3e5001a2b3c4d/activity"
);

const params = {
    "page[size]": "50",
    "page[cursor]": "eyJmIjoiOGQ5ZiIsImQiOiJuZXh0IiwidiI6WzE3MjkzOTQxMDAwMDAsM119",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "data": [
        {
            "type": "case-activities",
            "id": "6714a1c2d1c3e5001a2b3d02",
            "attributes": {
                "description": "Status changed from New to In Progress",
                "by": "Jordan Lee",
                "created_at": "2026-10-21T01:40:00+00:00"
            }
        },
        {
            "type": "case-activities",
            "id": "6712f0a4d1c3e5001a2b3d01",
            "attributes": {
                "description": "Case created",
                "by": "Jordan Lee",
                "created_at": "2026-10-20T03:15:00+00:00"
            }
        }
    ],
    "meta": {
        "total": 2
    },
    "links": {
        "self": "/api/v1/cases/6712f0a4d1c3e5001a2b3c4d/activity",
        "first": "/api/v1/cases/6712f0a4d1c3e5001a2b3c4d/activity",
        "next": null,
        "prev": null
    }
}
 

Example response (400, A filter or sort was sent):


{
    "errors": [
        {
            "status": "400",
            "code": "invalid_filter",
            "title": "Bad Request",
            "detail": "This collection offers no filters.",
            "source": {
                "parameter": "filter[by]"
            }
        }
    ]
}
 

Example response (400, A cursor from a different request):


{
    "errors": [
        {
            "status": "400",
            "code": "invalid_cursor",
            "title": "Invalid cursor",
            "detail": "The cursor does not belong to this query. Restart without page[cursor].",
            "source": {
                "parameter": "page[cursor]"
            }
        }
    ]
}
 

Example response (401, Missing, invalid or expired token):


{
    "errors": [
        {
            "status": "401",
            "code": "invalid_token",
            "title": "Unauthenticated"
        }
    ]
}
 

Example response (403, The case is reachable, but cases may not be read in its building):


{
    "errors": [
        {
            "status": "403",
            "code": "forbidden",
            "title": "Forbidden",
            "detail": "This credential is not permitted to read this case."
        }
    ]
}
 

Example response (404, No such case, or not reachable):


{
    "errors": [
        {
            "status": "404",
            "code": "not_found",
            "title": "Not Found",
            "detail": "No case with that id is accessible to this credential."
        }
    ]
}
 

Example response (422, An invalid page size):


{
    "errors": [
        {
            "status": "422",
            "code": "validation_failed",
            "title": "Unprocessable",
            "detail": "The page[size] must be a whole number between 1 and 250.",
            "source": {
                "parameter": "page[size]"
            }
        }
    ]
}
 

Request      

GET api/v1/cases/{case_id}/activity

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Accept        

Example: application/json

URL Parameters

case_id   string     

The case's identifier, from GET /api/v1/cases. Example: 6712f0a4d1c3e5001a2b3c4d

Query Parameters

page[size]   integer  optional    

How many entries to return per page, from 1 to 250. Defaults to 50. Example: 50

page[cursor]   string  optional    

Continue a listing. Take it from links.next or links.prev as given: it is opaque, and only valid for the request that produced it. Example: eyJmIjoiOGQ5ZiIsImQiOiJuZXh0IiwidiI6WzE3MjkzOTQxMDAwMDAsM119

Case options

List case statuses

requires authentication

The case statuses of the buildings where this credential may read cases, ordered by building, then each building's own display order. Each status also gives the state a case is in while it has that status: open, completed or trashed. Every status is returned at once: this collection is not paginated.

Example request:
curl --request GET \
    --get "https://keystone.stg.mybos.com/api/v1/case-statuses?buildings=6650a1f2c4e8b90012ab34c1" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Accept: application/json"
const url = new URL(
    "https://keystone.stg.mybos.com/api/v1/case-statuses"
);

const params = {
    "buildings": "6650a1f2c4e8b90012ab34c1",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "data": [
        {
            "type": "case-statuses",
            "id": "6650a2a0c4e8b90012ab3501",
            "attributes": {
                "name": "New",
                "state": "open"
            },
            "relationships": {
                "building": {
                    "data": {
                        "type": "buildings",
                        "id": "6650a1f2c4e8b90012ab34c1"
                    }
                }
            }
        },
        {
            "type": "case-statuses",
            "id": "6650a2a0c4e8b90012ab3502",
            "attributes": {
                "name": "In Progress",
                "state": "open"
            },
            "relationships": {
                "building": {
                    "data": {
                        "type": "buildings",
                        "id": "6650a1f2c4e8b90012ab34c1"
                    }
                }
            }
        },
        {
            "type": "case-statuses",
            "id": "6650a2a0c4e8b90012ab3504",
            "attributes": {
                "name": "Completed",
                "state": "completed"
            },
            "relationships": {
                "building": {
                    "data": {
                        "type": "buildings",
                        "id": "6650a1f2c4e8b90012ab34c1"
                    }
                }
            }
        },
        {
            "type": "case-statuses",
            "id": "6650a2a0c4e8b90012ab3505",
            "attributes": {
                "name": "Deleted",
                "state": "trashed"
            },
            "relationships": {
                "building": {
                    "data": {
                        "type": "buildings",
                        "id": "6650a1f2c4e8b90012ab34c1"
                    }
                }
            }
        }
    ],
    "meta": {
        "total": 4,
        "scope": {
            "origin": "defaulted"
        }
    },
    "links": {
        "self": "/api/v1/case-statuses",
        "first": "/api/v1/case-statuses",
        "next": null,
        "prev": null
    }
}
 

Example response (400, Paging, sorting or filtering was requested):


{
    "errors": [
        {
            "status": "400",
            "code": "invalid_query",
            "title": "Bad Request",
            "detail": "This collection is not paginated: every option is returned at once.",
            "source": {
                "parameter": "page"
            }
        }
    ]
}
 

Example response (401, Missing, invalid or expired token):


{
    "errors": [
        {
            "status": "401",
            "code": "invalid_token",
            "title": "Unauthenticated"
        }
    ]
}
 

Example response (403, A named building is not accessible):


{
    "errors": [
        {
            "status": "403",
            "code": "building_not_accessible",
            "title": "Forbidden",
            "detail": "One or more requested buildings are not accessible with this credential.",
            "meta": {
                "buildings": [
                    "6650a1f2c4e8b90012ab34c2"
                ]
            }
        }
    ]
}
 

Example response (403, Cases may not be read in a named building):


{
    "errors": [
        {
            "status": "403",
            "code": "forbidden",
            "title": "Forbidden",
            "detail": "This credential is not permitted to read cases in one or more of the requested buildings."
        }
    ]
}
 

Example response (422, The credential can reach every building and named none):


{
    "errors": [
        {
            "status": "422",
            "code": "validation_failed",
            "title": "Unprocessable",
            "detail": "Name the buildings whose options to list: this credential can reach every building.",
            "source": {
                "parameter": "buildings"
            }
        }
    ]
}
 

Request      

GET api/v1/case-statuses

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Accept        

Example: application/json

Query Parameters

buildings   string  optional    

Comma-separated building identifiers, from GET /api/v1/buildings. Omit to cover every building where this credential may read cases. Required when the credential can reach every building. Example: 6650a1f2c4e8b90012ab34c1

List case types

requires authentication

The case types of the buildings where this credential may read cases, ordered by building, then each building's own display order. Every type is returned at once: this collection is not paginated.

Example request:
curl --request GET \
    --get "https://keystone.stg.mybos.com/api/v1/case-types?buildings=6650a1f2c4e8b90012ab34c1" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Accept: application/json"
const url = new URL(
    "https://keystone.stg.mybos.com/api/v1/case-types"
);

const params = {
    "buildings": "6650a1f2c4e8b90012ab34c1",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "data": [
        {
            "type": "case-types",
            "id": "6650a2b5c4e8b90012ab3601",
            "attributes": {
                "name": "Plumbing"
            },
            "relationships": {
                "building": {
                    "data": {
                        "type": "buildings",
                        "id": "6650a1f2c4e8b90012ab34c1"
                    }
                }
            }
        },
        {
            "type": "case-types",
            "id": "6650a2b5c4e8b90012ab3602",
            "attributes": {
                "name": "Electrical"
            },
            "relationships": {
                "building": {
                    "data": {
                        "type": "buildings",
                        "id": "6650a1f2c4e8b90012ab34c1"
                    }
                }
            }
        }
    ],
    "meta": {
        "total": 2,
        "scope": {
            "origin": "defaulted"
        }
    },
    "links": {
        "self": "/api/v1/case-types",
        "first": "/api/v1/case-types",
        "next": null,
        "prev": null
    }
}
 

Example response (400, Paging, sorting or filtering was requested):


{
    "errors": [
        {
            "status": "400",
            "code": "invalid_query",
            "title": "Bad Request",
            "detail": "This collection is not paginated: every option is returned at once.",
            "source": {
                "parameter": "page"
            }
        }
    ]
}
 

Example response (401, Missing, invalid or expired token):


{
    "errors": [
        {
            "status": "401",
            "code": "invalid_token",
            "title": "Unauthenticated"
        }
    ]
}
 

Example response (403, A named building is not accessible):


{
    "errors": [
        {
            "status": "403",
            "code": "building_not_accessible",
            "title": "Forbidden",
            "detail": "One or more requested buildings are not accessible with this credential.",
            "meta": {
                "buildings": [
                    "6650a1f2c4e8b90012ab34c2"
                ]
            }
        }
    ]
}
 

Example response (403, Cases may not be read in a named building):


{
    "errors": [
        {
            "status": "403",
            "code": "forbidden",
            "title": "Forbidden",
            "detail": "This credential is not permitted to read cases in one or more of the requested buildings."
        }
    ]
}
 

Example response (422, The credential can reach every building and named none):


{
    "errors": [
        {
            "status": "422",
            "code": "validation_failed",
            "title": "Unprocessable",
            "detail": "Name the buildings whose options to list: this credential can reach every building.",
            "source": {
                "parameter": "buildings"
            }
        }
    ]
}
 

Request      

GET api/v1/case-types

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Accept        

Example: application/json

Query Parameters

buildings   string  optional    

Comma-separated building identifiers, from GET /api/v1/buildings. Omit to cover every building where this credential may read cases. Required when the credential can reach every building. Example: 6650a1f2c4e8b90012ab34c1

List case priorities

requires authentication

The case priorities of the buildings where this credential may read cases, ordered by building, then each building's own display order. Every priority is returned at once: this collection is not paginated.

Example request:
curl --request GET \
    --get "https://keystone.stg.mybos.com/api/v1/case-priorities?buildings=6650a1f2c4e8b90012ab34c1" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Accept: application/json"
const url = new URL(
    "https://keystone.stg.mybos.com/api/v1/case-priorities"
);

const params = {
    "buildings": "6650a1f2c4e8b90012ab34c1",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "data": [
        {
            "type": "case-priorities",
            "id": "6650a2c9c4e8b90012ab3701",
            "attributes": {
                "name": "Urgent"
            },
            "relationships": {
                "building": {
                    "data": {
                        "type": "buildings",
                        "id": "6650a1f2c4e8b90012ab34c1"
                    }
                }
            }
        },
        {
            "type": "case-priorities",
            "id": "6650a2c9c4e8b90012ab3702",
            "attributes": {
                "name": "Low"
            },
            "relationships": {
                "building": {
                    "data": {
                        "type": "buildings",
                        "id": "6650a1f2c4e8b90012ab34c1"
                    }
                }
            }
        }
    ],
    "meta": {
        "total": 2,
        "scope": {
            "origin": "defaulted"
        }
    },
    "links": {
        "self": "/api/v1/case-priorities",
        "first": "/api/v1/case-priorities",
        "next": null,
        "prev": null
    }
}
 

Example response (400, Paging, sorting or filtering was requested):


{
    "errors": [
        {
            "status": "400",
            "code": "invalid_query",
            "title": "Bad Request",
            "detail": "This collection is not paginated: every option is returned at once.",
            "source": {
                "parameter": "page"
            }
        }
    ]
}
 

Example response (401, Missing, invalid or expired token):


{
    "errors": [
        {
            "status": "401",
            "code": "invalid_token",
            "title": "Unauthenticated"
        }
    ]
}
 

Example response (403, A named building is not accessible):


{
    "errors": [
        {
            "status": "403",
            "code": "building_not_accessible",
            "title": "Forbidden",
            "detail": "One or more requested buildings are not accessible with this credential.",
            "meta": {
                "buildings": [
                    "6650a1f2c4e8b90012ab34c2"
                ]
            }
        }
    ]
}
 

Example response (403, Cases may not be read in a named building):


{
    "errors": [
        {
            "status": "403",
            "code": "forbidden",
            "title": "Forbidden",
            "detail": "This credential is not permitted to read cases in one or more of the requested buildings."
        }
    ]
}
 

Example response (422, The credential can reach every building and named none):


{
    "errors": [
        {
            "status": "422",
            "code": "validation_failed",
            "title": "Unprocessable",
            "detail": "Name the buildings whose options to list: this credential can reach every building.",
            "source": {
                "parameter": "buildings"
            }
        }
    ]
}
 

Request      

GET api/v1/case-priorities

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Accept        

Example: application/json

Query Parameters

buildings   string  optional    

Comma-separated building identifiers, from GET /api/v1/buildings. Omit to cover every building where this credential may read cases. Required when the credential can reach every building. Example: 6650a1f2c4e8b90012ab34c1

Service

Service status

Whether the service is up and can reach its data. Returns no customer data, so it needs no token.

Example request:
curl --request GET \
    --get "https://keystone.stg.mybos.com/api/v1/status" \
    --header "Accept: application/json"
const url = new URL(
    "https://keystone.stg.mybos.com/api/v1/status"
);

const headers = {
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200, Fully available):


{
    "app": true,
    "database": true
}
 

Example response (500, Up, but its data is unreachable):


{
    "app": true,
    "database": false
}
 

Request      

GET api/v1/status

Headers

Accept        

Example: application/json