Kolibri Kolibri
  • AppXchange ⇩
    • Documentation intro 
    • Kolibri core API 
    • Widgets 
    • Swagger
      • Getting started 
      • Swagger 
      • Example: Generating a PHP client 
    • Kolibri VOIP API 
    • Workflows (example flows) 
    • Quick start guides
      • Quick start guides 
      • Quick start guide PHP 
  • Sitelink 
  • Mediapartner 
  • Contact 
  • Webhooks 

Table of Contents

WebHooks

Some AppXchange partners want to be notified over HTTP when events occur, such as . A WebHook is a useful feature to achieve this. Each time an event occurs Kolibri POSTS a message using HTTP(S) to your server, using a pre-configured URI. The AppXchange partner must provide the URI so it can be configured in our platform.

Message envelope

Message envelopeMessages send by Kolibri are JSON objects wrapped inside an envelope. To process messages you need to process all items (“batches”) inside the “items” array, take each message inside the “data.messages” array and process the string in the “data” property of the message as a JSON object.
{
   "items": [
	  {
		 "webhookId": "d28HJw",
		 "source": "channel.message",
		 "serial": "e91URQe6QA0gwm05496618:2",
		 "timestamp": 1479301189860,
		 "name": "channel.message",
		 "data": {
			"channelId": "foob",
			"site": "eu-central-1-A",
			"messages": [
			   {
				  "id": "9qaOH1C4tO:2:0",
				  "name": "foo1",
				  "connectionId": "9qaOH1C4tO",
				  "timestamp": 1479301189856,
				  "data": "<json message 1>"
			   },
			   {
				  "id": "8q5aOH3C44O:5:5",
				  "name": "foo2",
				  "connectionId": "9qaOH1C4tO",
				  "timestamp": 1479301189859,
				  "data": "<json message 2>"
			   }
			]
		 }
	  },
	  {
		 "webhookId": "d28HJx",
		 "source": "channel.message",
		 "serial": "e91URQe6QA0gwm05496618:4",
		 "timestamp": 1479301189866,
		 "name": "channel.message",
		 "data": {
			"channelId": "foob",
			"site": "eu-central-1-A",
			"messages": [
			   {
				  "id": "9qaOH1C4t9:2:0",
				  "name": "foo3",
				  "connectionId": "9qaOH1C4tO",
				  "timestamp": 1479301189877,
				  "data": "<json message 3>"
			   }
			]
		 }
	  }
   ]
}
For your reference a description of each property in the message container is given in the table below. We advise you to not use any of these properties, except from the JSON array “messages”, since in the near future properties might be added or removed from the container, except “items.data.messages”.
Name Description
webhookId An internal unique ID for the configured WebHook.
source The source for the WebHook, namely “channel.message”.
timestamp A timestamp represented as milliseconds since epoch for the published message.
data An object containing the attributes defined below in JSONPath format data.*.
data.channelId Name of the channel that the presence event belongs to.
data.site An internal site identifier indicating the datacenter from which the message was published.
data.messages An array of messages with fields for each message described below.
data.messages.id Unique ID assigned by Kolibri to this message
data.messages.name A string representing the event name for the published message, see the publish method
data.messages.connectionId The public unique identifier for the publisher’s connection. Find out more about connectionId
data.messages.timestamp The time in milliseconds since the epoch when this message was received by Kolibri.
data.messages.data An utf-8 encoded string representing a JSON object containing the message to process by the AppXchange partner.
 

Message format

The data inside the data.messages.data has the following format:
id string
timeStamp string
realEstateAgencyId string
personId string
mailAccountId string
appClientKey string
category* AnnouncementCategorystring x-enumNames: List [ “Entity”, “Notification”, “EmailAccountChange”, “EmailPersonalChange”, “EventCenterChange”, “Voip”, “Authorization” ]Enum: Array [ 7 ]
entityDetails EntityDetails{
entityType* RootEntityTypestring x-enumNames: List [ “Unknown”, “AccountSettings”, “Agendaitem”, “AgendaItemCategory”, “AppClient”, “AppClientConsent”, “AssignmentRequest”, “BusinessPartner”, “Cadastre”, “CommunicationLog”, “CommunicationLogBlob”, “CompanyListing”, “CompanySettings”, “ContactCompany”, “ContactPerson”, “DocumentSession”, “DocumentTemplate”, “DossierItem”, “Employee”, “FinancialAdministration”, “Invoice”, “MediaContract”, “MediaPartner”, “MemoTextItem”, “Message”, “ObjectAssignment”, “ObjectTypeAssignment”, “Office”, “ProjectAssignment”, “Publication”, “RealEstateAgency”, “RelationGroup”, “SearchAssignment”, “Task”, “Testimonial”, “AcquisitionAssignment”, “AcquisitionObjectAssignment”, “Bid” ]Enum: Array [ 38 ]
entityId string
event* RootEntityEventstring x-enumNames: List [ “Unknown”, “Add”, “Update”, “Delete” ]Enum: Array [ 4 ]
dateTimeCreated string
createdBy string
dateTimeModified string
modifiedBy string
parenEntityId string
parentEntityType RootEntityTypestring x-enumNames: List [ “Unknown”, “AccountSettings”, “Agendaitem”, “AgendaItemCategory”, “AppClient”, “AppClientConsent”, “AssignmentRequest”, “BusinessPartner”, “Cadastre”, “CommunicationLog”, “CommunicationLogBlob”, “CompanyListing”, “CompanySettings”, “ContactCompany”, “ContactPerson”, “DocumentSession”, “DocumentTemplate”, “DossierItem”, “Employee”, “FinancialAdministration”, “Invoice”, “MediaContract”, “MediaPartner”, “MemoTextItem”, “Message”, “ObjectAssignment”, “ObjectTypeAssignment”, “Office”, “ProjectAssignment”, “Publication”, “RealEstateAgency”, “RelationGroup”, “SearchAssignment”, “Task”, “Testimonial”, “AcquisitionAssignment”, “AcquisitionObjectAssignment”, “Bid” ]Enum: Array [ 38 ]
}
notificationDetails NotificationDetails{
dateTimeCreated string
personId string
entityType* RootEntityTypestring x-enumNames: List [ “Unknown”, “AccountSettings”, “Agendaitem”, “AgendaItemCategory”, “AppClient”, “AppClientConsent”, “AssignmentRequest”, “BusinessPartner”, “Cadastre”, “CommunicationLog”, “CommunicationLogBlob”, “CompanyListing”, “CompanySettings”, “ContactCompany”, “ContactPerson”, “DocumentSession”, “DocumentTemplate”, “DossierItem”, “Employee”, “FinancialAdministration”, “Invoice”, “MediaContract”, “MediaPartner”, “MemoTextItem”, “Message”, “ObjectAssignment”, “ObjectTypeAssignment”, “Office”, “ProjectAssignment”, “Publication”, “RealEstateAgency”, “RelationGroup”, “SearchAssignment”, “Task”, “Testimonial”, “AcquisitionAssignment”, “AcquisitionObjectAssignment”, “Bid” ]Enum: Array [ 38 ]
entityId string
message string
event* NotificationEventstring x-enumNames: List [ “Unknown”, “AccountCreated”, “AccountDeleted”, “BankwarrantyExpired”, “Birthday”, “BrochureDownloaded”, “ContactMeRequest”, “NvmOauthTokenExpired”, “OfferListing”, “PublicationFailed”, “PublicationSucceeded”, “RentedFromExpired”, “RentedUntilExpired”, “ReservationExpired”, “Searchprofile”, “SoldExpired”, “SystemMessage”, “TransferExpired”, “Valuation”, “Viewing”, “ViewingRequest”, “SendMailFailed”, “SendMailSuccess”, “TaskReminder”, “AgendaItemReminder”, “PersonalMessage”, “RentedExpired”, “RentReservationExpired”, “IncomingBidExpired”, “OutgoingBidExpired”, “SentEmailOpened” ]Enum: Array [ 31 ]
}
emailChangeDetails EmailChangeDetails{
category* EmailChangeCategorystring x-enumNames: List [ “Message”, “Folder”, “Draft”, “Account”, “MessageOpened”, “MessageForwarded”, “MessageReplied”, “MessageLinkClicked”, “AccountConnected”, “AccountRunning”, “AccountStopped”, “AccountInvalidCredentials”, “AccountError”, “FolderUnreadCount”, “SendingMessageFailure”, “SendingMessageSuccess”, “Unknown” ]Enum: Array [ 17 ]
actionType* EmailChangeActionTypestring x-enumNames: List [ “Create”, “Modify”, “Delete”, “Unknown” ]Enum: [ Create, Modify, Delete, Unknown ]
emailMessage EmailChangeEmailMessage{
id string
subject string
emailAddresses [EmailChangeEmailAddress{
name string
email string
type* EmailChangeEmailAddressTypestring x-enumNames: List [ “From”, “To”, “Bcc”, “Cc”, “Unknown” ]Enum: [ From, To, Bcc, Cc, Unknown ]
}]
folder EmailChangeFolder{
id string
displayName string
type* EmailChangeFolderTypestring x-enumNames: List [ “Inbox”, “All”, “Trash”, “Archive”, “Drafts”, “Sent”, “Spam”, “Important”, “UserCreated”, “Unknown” ]Enum: [ Inbox, All, Trash, Archive, Drafts, Sent, Spam, Important, UserCreated, Unknown ]
}
}
emailDraft EmailChangeEmailMessage{
id string
subject string
emailAddresses [EmailChangeEmailAddress{
name string
email string
type* EmailChangeEmailAddressTypestring x-enumNames: List [ “From”, “To”, “Bcc”, “Cc”, “Unknown” ]Enum: Array [ 5 ]
}]
folder EmailChangeFolder{
id string
displayName string
type* EmailChangeFolderTypestring x-enumNames: List [ “Inbox”, “All”, “Trash”, “Archive”, “Drafts”, “Sent”, “Spam”, “Important”, “UserCreated”, “Unknown” ]Enum: Array [ 10 ]
}
}
emailFolder EmailChangeEmailFolder{
id string
displayName string
type* EmailChangeFolderTypestring x-enumNames: List [ “Inbox”, “All”, “Trash”, “Archive”, “Drafts”, “Sent”, “Spam”, “Important”, “UserCreated”, “Unknown” ]Enum: Array [ 10 ]
unreadCount integer($int32)
}
objectId string
messageType* EmailChangeMessageTypestring x-enumNames: List [ “MailPersonalChange”, “MailAccountChange” ]Enum: Array [ 2 ]
}
eventCenterChangeDetails EventCenterChangeDetails{
entityAction* RootEntityEventstring x-enumNames: List [ “Unknown”, “Add”, “Update”, “Delete” ]Enum: Array [ 4 ]
entityType* EventCenterEntityTypestring x-enumNames: List [ “Unknown”, “Event”, “EventNotification” ]Enum: Array [ 3 ]
entityIds [string($guid)]
}
voipDetails VoipDetails{
phoneNumber string
conversationId* string($guid)
direction* VoipConversationDirectionstring x-enumNames: List [ “Outgoing”, “Incoming”, “Unknown” ]Enum: Array [ 3 ]
status* VoipConversationStatusstring x-enumNames: List [ “Ringing”, “Answered”, “Disconnected”, “Transferred”, “Unknown” ]Enum: Array [ 5 ]
conversationEmployeeId string($guid)
}
authorizationDetails AuthorizationDetails{
companyId* string($guid)
employeeId string($guid)
entity* AuthorizationEntitystring x-enumNames: List [ “Consent”, “EmployeeSettings”, “CompanySettings”, “Unknown” ]Enum: Array [ 4 ]
action* AuthorizationActionstring x-enumNames: List [ “Added”, “Deleted”, “Updated”, “Unknown” ]Enum: Array [ 4 ]
appClientKey string
}

Request authenticity

WebHook messages send by Kolibri are signed allowing the AppXchange partner to verify the authenticity of each request. Kolibri signs the data with a private pre-shared key (PSK) and include an HTTP header with the signature in every HTTP post request issued to your server. Customers who receive WebHooks can then use the private pre-shared key (PSK) to verify the authenticity of the WebHook data. In order to verify the signature, you need to do the following: • start with the webhook request body. This will be a JSON string encoded with content-encoding utf-8; • calculate the HMAC of that request body with algorithm SHA-256 and the private pre-shared key (PSK); • encode the resulting HMAC using RFC 3548 base 64; • compare that result with the signature value indicated in the Signature header. Validating the authenticity of messages coming from Kolibri is an optional feature and is not required. But we do encourage you to do validate the message signature. We also encourage customers to use a secure HTTPS URL for their WebHooks endpoint. This will ensure that requests cannot be intercepted and all communication with your servers is secured with TLS. In order to prevent overloading your servers with requests, Kolibri batches WebHook data and issues one POST request with JSON data in the body of the request to your servers. WebHook requests are typically published at most once per second per configured WebHook. Webhook requests are made with a default timeout of 15s. If the request fails or times out, Kolibri retries the request with exponential backoff (base delay 1s, backoff factor sqrt(2), up to a max of 60s).    

Get started using widgets

When creating a widget in Kolibri, you need to contact us. You need to provide the following data:

  • Display name: This is the title that is shown above the widget in Kolibri (see the screenshots above for an example)
  • Callback url: This is the URL Kolibri uses to request you for the HTML to display in the Iframe. More about the callback URL in the Callback chapter.
  • Height in pixels: This is especially important for widgets in the sidebar and the dashboard (not behind a button). The height is the maximum height your widget will be. This height will be reserved in the Kolibri web application for your widget. If your widget is higher than the specified height, parts of the widget will not be visible. For widgets behind a button this is less relevant, because it is possible to scroll horizontally in these widgets.
  • Location: where should the widget be placed in the Kolibri web application (see the locations above)
  • Entities: which entities should the widget be displayed for on the specified location (see the locations above for all entity options available for a location).

Callback URL

As stated before, you are notified a widget is required by Kolibri using a callback URL you specify. This callback URL contains 4 sections. The information they contain and what you need to do with those sections is now explained using the following example URL: https://yourcallbackurl.com?entityId={value}&realEstateAgencyId={value}&entityType={value}&validUntil={value}&signature={value}=&keyVersion={value}

  • Callback URL base: This is the URL you have specified to us to use as a callback URL.
  • Payload: this part contains the data about the entity where the widget should be displayed. It consists of the following properties:
  • EntityId: the unique identifier for the entity in the Kolibri System. This is a GUID value.
  • RealEstateAgencyId: the unique identifier of the real estate agency the entity belongs to. This is a GUID value.
  • EntityType: the type of the entity the widget is meant for. This is an enum value, with the following options: ContactPerson, ContactCompany, Task, AgendaItem, ObjectAssignment, ProjectAssignment, ObjectTypeAssignment, SearchAssignment, Invoice, DynamicDocument and Dashboard.
  • ValidUntil: this is an epoch date (in minutes, indicating a UTC date) that indicates until when this URL should be allowed.
  • Signature: Every widget callback contains a signature. This signature is a hash calculated over the payload (the query string from “entity=” until “validUntil={valid}” (note, the question mark is not included!). You are responsible for validating this hash. The hash is calculated using the private key of asymmetric encryption RSA and the hash method SHA512.
  • keyVersion: to make it possible to roll keys, we add a key version to the request, indicating which private key is used to calculate the signature hash. When we provide you with the public key, we also include the version.

Optional: EmployeeId

It is possible to add the EmployeeId to the callback URL. This way, your application can know which employee has requested the widget. You can request this extra parameter at development@kolibri.software. Do keep in mind, if you have multiple widgets, all callback URL’s will include this new employeeId. When the employeeId is added, the callback URL will look like this:
https://yourcallbackurl.com?entityId={value}&realEstateAgencyId={value}&entityType={value}&validUntil={value}&employeeId={value}&signature={value}=&keyVersion={value}
 
So, an extra parameter is added to the request, after the valid until parameter.

Security

In the previous chapter the signature and key version are mentioned. You are responsible for validating the request is a valid request. If it is not a valid request, a 401 status code must be returned. There are 2 ways to validate a request. The primary way, is using an endpoint to validate the request. The URL for this endpoint is https://sandbox-authorization.kolibri24.com/v1/ validateSignature for the sandbox environment and  https://authorization.kolibri24.com/v1/ validateSignature for the production environment. To use this endpoint, you have to make a POST request (authorization tokens are not required) with the following JSON request object:

  • key is the same app client key you use the validate against the Kolibri Identity Server.
  • The PayloadData is an object containing the data from the payload in the request (see the previous chapter). Note that employeeId is optional in the payloadData. This is only required if your Callback URL includes the employeeId.
  • The signature is the signature string you obtained from the request (see previous chapter). Please note, some libraries remove the + characters from the signature and replace them with a space. The + characters must be provided when validating a signature.
  • The keyVersion is also obtained from the request (see previous chapter)

The Content-Type header must be set to: application/json.

If the request is correctly made, a response in the response body will be returned that looks like this:

isValid indicates if the signature is correct and the validUntil is still valid. If the response isValid is false, or the http status code does not indicate success (Bad request, Not found, Internal server Error etc), you are required to return a 401 Unauthorized. If it is valid (200 and isValid true), you may return the widget HTML.

On the live environment we require that the widget returns a 401 if the request is invalid. We test periodically if these requirements are met, and if not, the widget will be taken offline. Then, it can only be set online if het requirements are met (401 for invalid requests and a 200 with the widgets HTML if the request is correct). The sandbox environment does not enforce this. Meaning, you can test you widgets in Kolibri without having implemented the signature check.

If you want to test if your application successfully does these security checks in the sandbox environment, we can test this. Contact us and we will check it.

When your widget requires a more secure way of validating the request (not using our API to validate) you can contact us. We will provide you the public key that can be used to validate the signature on your side.

X-Frame-Options

In the response headers for the widget, the x-frame-options must be set so that the widget can be displayed in an Iframe in the Kolibri24.com domain. The response header should look like this: