RealEstateCore and P8S ServiceObject - Alarm, Alert and Work Order model that aggregates different types of events and activities into a single set of APIs with publish–subscribe pattern.
ServiceObject could be produced from different applications or users and depending on type of event ServiceObject could be consumed by multiple 3-party services.
There are 4 entities in the application
ServiceObject is the main actor. ServiceObject could be produced directly via API or by configured alert based on sensor observation stream. Read more about ProptechOS Alerts
Routes provide the ability to specify which ServiceObjects should be dispatched by selected dispatchers and under what conditions (when it is created, modified or deleted). Therefore, the dispatcher itself is configured once with required/sensitive information, and then routing is responsible for leveraging how many ServiceObjects should be dispatched. Routing can be updated multiple times without the need to re-configure dispatcher information.
Dispatchers introduce an abstraction layer on top of the existing ServiceObject stream that allows to dispatch items to 3-party systems: Email, SMS, EstateLogs, Webhook etc. Dispatcher aggregates information about how to dispatch specific ServiceObject and stores any sensitive information: API keys, credentials, etc., so applications that produce ServiceObject don't need to know nor provide this information during ServiceObject creation.
Some dispatchers are provided by ProptechOS as a service: Email, SMS, so clients don't need to have their Email/SMS integration services. Other dispatchers will require sensitive information provided by clients like EstateLogs service credentials, those will be stored securely and used only during the dispatching process.
ServiceObject API develops integration with different dispatcher types and the list of supported types will grow in the future. Clients that want to create an instance of a dispatcher have to submit a request with required information depending on the type of the dispatcher and then set up routing for ServiceObjects that will instruct the dispatcher which ServiceObjects should be dispatched by them. One ServiceObject could be dispatched by multiple dispatchers and a single dispatcher can dispatch multiple ServiceObjects.
Dispatch Log contains the information about the ServiceObject dispatching. Whenever ServiceObject is processed by Router, each dispatcher creates a log entry with dispatch status and details. This information can be used as debug information to analyze unhealthy behavior of 3-party service.
Every ServiceObject goes into a dispatching queue after creation, modification or deletion, where routing configuration is taken into account. Router gathers all available routes and tests each ServiceObject in the queue by the route filter specified during route creation.
If there is a match, the router puts the ServiceObject into a dedicated dispatching queue, therefore malfunctioning dispatcher won't affect other dispatchers and each dispatcher will have their own dispatching time, retry on failure and all dispatchers will be processing items in parallel.
- ServiceObject is created, modified or deleted and put into the routing queue.
- Router picks up a ServiceObject and tests for existing route filters.
- ServiceObject put into the specific dispatcher queue(s).
- Dedicated Dispatcher picks up ServiceObject and dispatches based on the provided information.
Please see Open API Specification (Swagger) docs for details and to try it out. (Note that if you are running a dedicated instance of ProptechOS, your API and your OAS will have a separate proptechos.com subdomain).
Authentication in ProptechOS uses OAuth 2.0 protocol. It can be separated into two categories:
- implicit (interactive) authentication - for applications accessing the API on behalf of user, like web apps and UIs
- client_credentials (deamon application) authentication - for applications working without user interaction. See more in the Authentication section
You need to have ProptechOS account in order to make authorized requests to this API.
- Click "Authorize" button in top right corner
- Leave "client_id" field empty and click the "select all" scopes link at the bottom
- Click "Authorize"
Some dispatchers are provided by ProptechOS as a service: Email, SMS, so clients don't need to have their Email/SMS integration services.
You can get list of available dispatchers by calling [GET] /api/dispatchers
endpoint which should give use such response:
You can test API with one of existing SMS or Email dispatcher, so you can go directly to Working with Routes section of this document.
id
of the dispatcher is specified in routes to allow reuse of the same dispatcher in multiple routing scenarios.
configuration
of the dispatcher always has a dispatcherType
to force
validity of configuration properties depending on dispatcher type.
Dispatcher sensitive data specified in configuration (like API keys, credentials) is always encrypted before it passes to storage devices and is only decrypted on short-time period right before using it to authorize to 3-party services. So you can be sure that your credentials are never exposed to other services in a raw representation.
Custom dispatcher is required when you do an integration with your API
service. The most common use-case would be the Webhook integration. Everytime when ServiceObject is created, modified or deleted and route is matched for this item, Webhook dispatcher will try to send [POST]
request to the specified endpoint with ServiceObject in a request body. The "X-Event-Type"
http request header contains information about the event that was fired on ServiceObject, can contain such values: Created
, Modified
or Deleted
.
A retry policy is the most basic type of exception and error handling. If an initial request timed out or failed (any request that results in a 429 or 5xx response), this policy defines whether the action should retry. By default, all actions retry 4 additional times over 20-second intervals. So if the first request receives a 500 Internal Server Error response, the workflow engine pauses for 20 seconds, and attempts the request again. If after all retries, the response is still an exception or failure, the workflow continues and marks the action status as Failed.
You can always check the dispatch log to get more information about dispatch status and captured responses from webhook URL.
In order to create Webhook dispatcher you need to have API service that has publically accessible https
URL like: https://api.service.com/serviceobject-listener
. In addition to URL we encourage you to have request header based authorization established for your endpoint so you can avoid unintended/unauthorized requests to your webhook URL.
In Webhook Dispatcher configuration you can specify in headers
property a list of header that will be sent to your webhook URL, so authorization could be done in multiple ways:
-
Basic
{ "configuration": { "headers": { "Authorization": "Basic dGVzdDphY2NvdW50" }, "dispatcherType": "Webhook" } }
-
API key
{ "configuration": { "headers": { "X-API-KEY": "P9&E?n$=LKh@-DnL" }, "dispatcherType": "Webhook" } }
- Choose an available "Webhook" example from
[POST] /api/dispatchers
endpoint. - Provide dispatcher user-friendly name and configuration:
uri
andheaders
.
Example:
{
"name": "My Webhook dispatcher",
"configuration": {
"uri": "https://api.service.com/serviceobject-listener",
"headers": {
"Authorization": "Basic dGVzdDphY2NvdW50"
},
"dispatcherType": "Webhook"
}
}
Routes provide ability to filter an ServiceObject creation, modification and deletion stream and dispatch with one or more pre-configured dispatchers. The dispatchers
property in the Route body is a Map
where Key
is a Dispatcher identifier and Value
is an object with additional to a Dispatcher data. Any object must contain the dispatcherType
property to force
validity of configuration properties depending of dispatcher type. The eventTypes
property contains ServiceObject lifecycle events, ServiceObject will be dispatched when any of the defined events occur. Available event types: Created
(default), Modified
, Deleted
.
Routing filter helps to precisely configure which ServiceObjects should be dispatched, for that need clients have the ability to build an OData filter query that would be executed against each new ServiceObject and if it matches the filter then the ServiceObject is routed to specified dispatchers. More details on available filter operators.
-
Match all ServiceObjects
true
-
Filter by string property
serviceStatus eq 'Acknowledged'
title eq 'Title value'
contains(title, 'phrase') and not contains(title, 'another phrase')
-
Filter by collection property
aliases/any(alias: alias eq 'http://.../001')
producedByDevices/any(device: device eq 'http://.../001') and relatedTo/any(space: space eq 'http://.../001')
-
Filter by tags
tags/any(tag: tag/name eq 'tag name' and tag/value eq 'tag value')
Route can have multiple dispatcher specified inside dispatchers
property.
If you want to have Email
and SMS
dispatching, simply add another dispatcher by providing it's id
and required configuration.
Email
dispatcher example:
{
"name": "Send Email",
"filter": "true", // ServiceObject filter
"eventTypes": [ // ServiceObject lifecycle event filter
"Created",
"Modified",
"Deleted"
],
"dispatchers": {
"5a126627-d640-456b-8d0d-3c99bc965c32": { // Email dispatcher id
"emails": [
"email@com" // target emails
],
"emailTemplate": "Default", // predefined template
"dispatcherType": "Email"
}
}
}
Email
and SMS
dispatcher example:
{
"name": "Send Email and SMS",
"filter": "aliases/any(alias: alias eq 'http://.../001')", // ServiceObject filter
"eventTypes": [ // ServiceObject lifecycle event filter
"Created",
"Modified",
"Deleted"
],
"dispatchers": {
"5a126627-d640-456b-8d0d-3c99bc965c32": { // Email dispatcher id
"emails": [
"email@com" // target emails
],
"emailTemplate": "Default", // predefined template
"dispatcherType": "Email"
},
"246c6117-603c-4ab2-a4d3-19c65889ded4": { // SMS dispatcher id
"phoneNumbers": [
"+10000000000" // target phones
],
"messageTemplate": "Title: {{title}}", // SMS template
"dispatcherType": "SMS"
}
}
}
- Choose an available
Email
example from[POST] /api/routes
endpoint. - Fill in
emails
property with list of emails you want ServiceObject to be sent to. - Choose
emailTemplate
from predefined list ofDefault
orAlert
- Choose an available
SMS
example from[POST] /api/routes
endpoint. - Fill in
phoneNumbers
property with list of phone numbers you want ServiceObject to be sent to. - Provide
messageTemplate
of SMS you want to send.
Template allows you to provide variables like: {{title}}
, {{serviceType}}
, {{tags.name}}
which will be substituted with ServiceObject real values before sending. You can see details on how to build property path with JSON path.
Warning:
Message exceeding 160 characters in length will be truncated. If provided variable is missing in ServiceObject, then {{variable}} text won't be substituted.
SMS template example:
{
"messageTemplate": "ServiceObject with a title: {{serviceObject.title}} has been {{eventType}}.",
"dispatcherType": "SMS"
}
ServiceObject example:
{
"serviceObject": {
"title": "Temperature Alert",
"tags": {
"threshold": "25°C",
}
},
"eventType": "Modified"
}
Result SMS text: ServiceObject with a title: Temperature Alert has been Modified
- Choose an available
Webhook
example from[POST] /api/routes
endpoint. - Replace existing
id
key insidedispatchers
property withWebhook
dispatcherid
, you have recently created.
Webhook
dispatcher example:
{
"name": "Call endpoint",
"filter": "tags/any(tag: tag/name eq 'Alert'", // ServiceObject filter
"eventTypes": [ // ServiceObject lifecycle event filter
"Created",
"Modified",
"Deleted"
],
"dispatchers": {
"<your-dispatcher-id>": { // Custom Webhook dispatcher id
"dispatcherType": "Webhook"
}
}
}
You can get list of available ServiceObjects by calling [GET] /api/serviceobject
endpoint. This endpoint contains extended filtering by optional query parameters, sorting, paging and response model shaping.
Filtering of ServiceObjects could be done via such optional query parameters:
serviceObjectIds
- Comma-separated list of ServiceObjects UUID identifiers
Example: a6c33fee-a408-4cba-97c0-659742f337c0, 0ae31072-26c4-49c4-879b-a93face20d4f
aliases
- Comma-separated list of ServiceObjects aliases
Example: https://ns.proptechos.com/bim/QWERTY, https://ns.proptechos.com/bim/12345
-
title
- phrase that ServiceObject title should contain -
createdByAgent
- UUID identifier of the Agent that created the ServiceObject -
createdAfter
- The UTC timestamp after which the ServiceObject was created
Example: 2020-12-01T00:00:00Z
-
updatedByAgent
- UUID identifier of the Agent that last updated the ServiceObject -
updatedAfter
- The UTC timestamp after which the ServiceObject was last updated
Example: 2020-12-01T00:00:00Z
-
acknowledgedByAgent
- UUID identifier of the Agent that last acknowledged the ServiceObject -
acknowledgedAfter
- The UTC timestamp after which the ServiceObject was last acknowledged
Example: 2020-12-01T00:00:00Z
-
closedByAgent
- UUID identifier of the Agent that closed the ServiceObject -
closedAfter
- The UTC timestamp after which the ServiceObject was closed
Example: 2020-12-01T00:00:00Z
serviceStatus
- Service status of the ServiceObject
Available values : UnAcknowledged, Acknowledged, Closed
severity
- Severity of the ServiceObject. See REC QuantityKind for details
Available values : AlarmMinor, AlarmMajor, AlarmSevere
serviceStatus
- Service type of the ServiceObject
Available values : WorkOrder, ErrorReport, Alert, Notification
producedByDevices
- Comma-separated list of Devices URIs that produced the ServiceObject
Example: https://proptechos.com/api/device/a6c33fee-a408-4cba-97c0-659742f337c0, https://proptechos.com/api/sensor/a6c33fee-a408-4cba-97c0-659742f337c0
relatedTo
- Comma-separated list of Twin URIs related to this ServiceObject
Example: https://proptechos.com/api/realestatecomponent/a6c33fee-a408-4cba-97c0-659742f337c0, https://proptechos.com/api/room/a6c33fee-a408-4cba-97c0-659742f337c0
tags
- Comma-separated list of Tags of the ServiceObject. Note: If you specify more than one Tag the ServiceObject should contain all these Tags to match the filter
Example: category=cleaning, needscleaning=windows
To bring more flexibility in ServiceObject filtering expressions you can use OData filter queries as described in ServiceObject filtering inside Routes. To test your filter expressions you can use [GET] /api/serviceobject
endpoint with provided $filter
query parameter which contains your OData filter query.
You can control on how many items you get in response by providing
-
page
- Zero-based page index -
pageSize
- The size of the page to be returned
Note:
For some cases you want to know how many items are matching your filter criteria, so for this case you can build a lightweight response by providing pageSize=1
which will return only 1 item in response but you'll be able to read count
property in response too, which gives you total count of items available.
In order to sort ServiceObjects in response you can use $orderBy
query parameter which contains a comma-separated list of the ServiceObject properties you want to order by. In addition you can specify sort order between asc
- ascending order, desc
- descending order for multiple provided properties. If you dont specify sort order, then asc
order is used by default.
$orderBy
- Comma-separated list of the ServiceObject properties to order by
Example: serviceType, severity desc, serviceStatus asc
At some circumstances you don't want to get all properties of ServiceObject in a response which may lead to extensive amount of network traffic produced if querying larger amounts of data in one page. In this case you can use $select
query parameter to specify comma-separated list of the ServiceObject properties to return in a response, therefore only those properties will be populated in ServiceObject response model and other properties will be ignored.
$select
- Comma-separated list of the ServiceObject properties to return in a response
Example: id, title, serviceType, severity, serviceStatus
ServiceObjects Response:
{
"count": 100,
"items": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"title": "string",
"serviceStatus": "UnAcknowledged",
"severity": "AlarmMinor",
"serviceType": "WorkOrder",
}
]
}
You can create ServiceObject by submitting [POST] /api/serviceobject
request. You have to provide required fields to complete the request:
-
title
- Human-readable title of the ServiceObject -
relatedTo
- Must always contain at least one Building URI related to this ServiceObject
Example: https://proptechos.com/api/realestatecomponent/a6c33fee-a408-4cba-97c0-659742f337c0
List of optional parameters you can provide during creation:
-
severity
- Severity of the ServiceObject. Available valuesAlarmMinor, AlarmMajor, AlarmSevere
. Default value -AlarmMinor
-
serviceType
- Type of service associated with this ServiceObject. Available valuesWorkOrder, ErrorReport, Alert, Notification
. Default value -WorkOrder
-
createdAt
- The timestamp indicates when the ServiceObject was created. If not specified - the current UTC time will be set. Providing this value might be handy when you want to store actual time of event rather than time when ServiceObject was created for the event -
producedByDevices
- List of Devices URIs that produced this ServiceObject. Should beNULL
if created by Agent.
Example: https://proptechos.com/api/device/a6c33fee-a408-4cba-97c0-659742f337c0
aliases
- List of custom defined URIs that could uniquely identify this ServiceObject. Alias should be based on existingAlias Namespace
inProptechOS
. You can create theAlias Namespace
inProptechOS
with providedbaseUrl
and all provided aliases here will be matched to this namespace bybaseUrl
part of URI.
Example: https://external-service.com/api/alarm/a6c33fee-a408-4cba-97c0-659742f337c0
Alias Namespace in ProptechOS:
{
"id": "caa00b29-3fa5-4cda-9d85-3a6ac95bdca2",
"class": "AliasNamespace",
"baseURL": "https://external-service.com/api" // aliases begins with this URL part
},
tags
- Map (key, value) of custom defined tags/properties associated to this ServiceObject
{
"tags": {
"customSeverify": "10",
"threshold": "25°C",
"description": "Alert was triggered programmatically"
}
},
id
- UUID of the ServiceObject to be created. If not specified - an autogenerated UUID will be used. At some circumstances you might need to pre-define theid
of aServiceObject
before creation.
You can update existing ServiceObject by sumbitting [PUT] /api/serviceobject
request. In addition to required fields that you specify during creation id, title, relatedTo
, you have to specify:
eTag
- Indicates ServiceObject's version.
ETag (Entity Tag) is a versioning approach that attaches eTag
(version number) for existing ServiceObject which you should provide back during update.
Provided version is compared with the stored version in the repository.
This approach is guarding against potential data corruption happened due to sumiltaneous updates performed by different agents.
Version mismatch indicates that ServiceObject was updated during the time when ServiceObject was read and submitted back with updates. Such request will be rejected and you have to read the latest version of the ServiceObject and perform update again.
For simplitity, we introduced dedicated endpoints to update ServiceObject serviceStatus
via:
-
/api/serviceobject/{id}/status/{status}
- to updateserviceStatus
of ServiceObject by it'sid
-
/api/serviceobject/status/{status}
- to updateserviceStatus
of ServiceObjects in bulk
Such endpoints do not require eTag
to be provided.
Whenever a Dispatcher executes it's job (sends SMS, email or creates a work order in EstateLogs, etc.) a Dispatch Log with the current timestamp is saved to the ServiceObject database.
Dispatch logger has a specific behavior when the Dispatcher type is Webhook. During operation, the Dispatch Log record is saved with the status InProgress. Then the application makes an API call to the endpoint specified in the Dispatcher configuration. This can take some time while the application waits for the response. Once the response is received the dispatch log record is updated with the correct status (Succeeded
, Failed
) and response message.