COMAPI

Suggest Edits

The One API Overview

 

Comapi offers one all encompassing messaging API which we refer to as the "One" API.
This API lets you send messaging to your customers across all supported channels simply, using a RESTful service and JSON data.

In addition to giving you multi channel messaging support through one simple API, you can optionally use the Branch features to intelligently message your users using the channels you specify in your preference order, therefore saving costs and improving users experiences.

Calling the "One" API

End Points

The following endpoints (URLs) are available for the "One" API for HTTP POSTing to.

Which method to use?

Use the batch submission if you have more than one message to send at a time, otherwise use the single submission.

Single Message Submission

The endpoint for the web service is: https://api.comapi.com/apispaces/apiSpaceId/messages

Send a single message request object using JSON e.g.

{
  "body": "Sending is easy with Comapi",
  "to": {
    "phoneNumber": "447123123123"
  },
  "rules": [
    "sms"
  ]
}

You can find more details about the end point here, but please read this section first so that you are familiar with how the API works.

Batch Message Submission

The endpoint for the web service is: https://api.comapi.com/apispaces/apiSpaceId/messages/batch

Send a batch of message request objects using a JSON array e.g.

[
  {
    "body": "This is message 1",
    "to": {
      "profileId": "dave@acme.com",
      "phoneNumber": "447123123123"
    },
    "rules": [
      "fbMessenger",
      "sms"
    ]
  },
  {
    "body": "This is message 2",
    "to": {
      "profileId": "bob@acme.com",
      "phoneNumber": "447234234234"
    },
    "rules": [
      "fbMessenger",
      "sms"
    ]
  }
]

You can find more details about the end point here, but please read this section first so that you are familiar with how the API works.

Content Type

The content type for the HTTP requests should be set to application/json using the Content-Type HTTP header:

Content-Type: application/json

Security

Secure communications

The "One" API is only accessible via secure communications using 2048 bit TLS encryption.

Granular authorisation

The authorisation to invoke methods on the API is provided by claims in JWT access tokens that can be created by sufficiently privileged users in the Comapi Portal. Tokens may be revoked at any time, and can be assigned permissions individually to allow least privilege best practices to be easily implemented.

See our Quick Start guide for more details on how to setup a security token.

Authorising web service calls

To use your access token to authorise calls to Comapi web services you should ensure that your token is included in the Authorization HTTP header as a Bearer for example:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI3NjYzZGU4OC02MTRlLTQ1NTYtYmNiYi1mMDJlZjk4OWFlN2UiLCJpc3MiOiJodHRwczovL2FwaS5jb21hcGkuY29tL2FjY2Vzc3Rva2VucyIsImF1ZCI6Imh0dHBzOi8vYXBpLmNvbWFwaS5jb20iLCJhY2NvdW50SWQiOjM5Njk0LCJhcGlTcGFjZUlkIjoiNTMzNjQxOTgtM2YzZi00NzIzLWFiOGYtNzA2ODBjMTExM2IxIiwicGVybWlzc2lvbnMiOlsiY29udjpycHUiLCJjb252OnJwciIsImNvbnY6cmEiLCJjb252OndwdSIsImNvbnY6d3ByIiwiY29udjpkcHUiLCJjb252OmRwciIsImNvbnY6ZGEiLCJjb252bXNnOnIiLCJjb252bcgHtyIoLCJjaGFuOnIiLCJjaGFuOnciLCJjaGFuOmQiLCJwcm9mOnJhIiwicHJvZjpybyIsInByb2Y6d2EiLCJwcm9mOndvIiwicHJvZjpkYSIsInByb2Y6ZG8iLCJtc2c6c21zOnMiLCJtc2c6ZmJtc2c6cyIsIm1zZzphcHBtc2c6cyIsIm1zZzpjdXN0b206cyIsIm1zZzpyIiwiYXBpczpybyJdLCJzdWIiOiI3NjYzZGU4OC02MTRlLTQ1NTYtYmNiYi1mMDJlZjk4OWFlN2UiLCJwcm9maWxlSWQiOiJDb21hcGlfU3VwcG9ydCIsIm5hbWUiOiJNZXlhLVN1cHBvcnQiLCJpYXQiOjE0ODcxODUxNzB9.vRx3nQyeg66eZLeKZ03R1Npcayul_XdipvoObBF_M2I
POST /apispaces/53364198-3f3f-4723-ab8f-70680c1113b1/messages HTTP/1.1
Host: api.comapi.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI3NjYzZGU4OC02MTRlLTQ1NTYtYmNiYi1mMDJlZjk4OWFlN2UiLCJpc3MiOiJodHRwczovL2FwaS5jb21hcGkuY29tL2FjY2Vzc3Rva2VucyIsImF1ZCI6Imh0dHBzOi8vYXBpLmNvbWFwaS5jb20iLCJhY2NvdW50SWQiOjM5Njk0LCJhcGlTcGFjZUlkIjoiNTMzNjQxOTgtM2YzZi00NzIzLWFiOGYtNzA2ODBjMTExM2IxIiwicGVybWlzc2lvbnMiOlsiY29udjpycHUiLCJjb252OnJwciIsImNvbnY6cmEiLCJjb252OndwdSIsImNvbnY6d3ByIiwiY29udjpkcHUiLCJjb252OmRwciIsImNvbnY6ZGEiLCJjb252bXNnOnIiLCJjb252bcgHtyIoLCJjaGFuOnIiLCJjaGFuOnciLCJjaGFuOmQiLCJwcm9mOnJhIiwicHJvZjpybyIsInByb2Y6d2EiLCJwcm9mOndvIiwicHJvZjpkYSIsInByb2Y6ZG8iLCJtc2c6c21zOnMiLCJtc2c6ZmJtc2c6cyIsIm1zZzphcHBtc2c6cyIsIm1zZzpjdXN0b206cyIsIm1zZzpyIiwiYXBpczpybyJdLCJzdWIiOiI3NjYzZGU4OC02MTRlLTQ1NTYtYmNiYi1mMDJlZjk4OWFlN2UiLCJwcm9maWxlSWQiOiJDb21hcGlfU3VwcG9ydCIsIm5hbWUiOiJNZXlhLVN1cHBvcnQiLCJpYXQiOjE0ODcxODUxNzB9.vRx3nQyeg66eZLeKZ03R1Npcayul_XdipvoObBF_M2I
Content-Type: application/json

Need a access token?

See our quick start guide for a guide to creating access tokens in the Comapi portal

Addressing your messages

The Comapi "One" API makes reaching your customers easy by allowing you to pass multiple ways of identifying your target customer in the to block of the request, plus it will automatically use relevant information from a customers Comapi profile in order to send via a specified channel.

e.g. If you pass a profileId only in the to block and wanted to send via Facebook Comapi would automatically look at the customers profile to see if they have a fbMessengerId and use it.

Profiles

Comapi can store contact profiles for you to make it easier to send across multiple channels as you can retain important details such as phone numbers, Facebook Ids, emails etc... and then have Comapi automatically utilise them when required to send via a channel such as Facebook.

To ensure Comapi creates or updates a profile record for a contact ensure you include a profileId in the to section of your requests. The profileId value can be any unique identifier for the contact, so if your system uses email to identify customers pass that, or customer numbers etc...

{
  "body": "The phoneNumber will be stored against the profileId",
  "to": {
    "profileId": "dave.smith@acme.com",
    "phoneNumber": "447123123123"
  },
  "rules": [
    "sms"
  ]
}
{
  "body": "This will still work even though no phoneNumber is being passed!",
  "to": {
    "profileId": "dave.smith@acme.com"
  },
  "rules": [
    "sms"
  ]
}

Profiles are useful for the Facebook Messenger channel

You need to use profiles when using the Facebook Send to Messenger plugin, as we use the profileId to save the unique Facebook Messenger Id Facebook sends to Comapi when a customer clicks on the Send to Messenger button. To send to this customer using Facebook Messenger simply ensure you pass the profileId in the to section. Find out more in the Facebook channel details.

Simple message sending across channels

The Comapi "One" API automatically creates a compatible message for each channel based on the text content held in the body property, therefore removing the need to specify a custom message body for each channel. This makes sending simple text based messages simple.

You can optionally specify a title property, which will be used to describe the message on channels that support the concept.

Merge Fields

In both the title and body properties you can optionally use merge fields to merge in properties from the Comapi profile you are addressing the message to. To do this simply add {{ profile property name }} anywhere in the properties e.g.

{
  "body": "Hello {{profile.name}}, thanks for signing up today.",
  "title": "{{profile.name}} welcome",
  "to": {
    "phoneNumber": "447123123123"
  },
  "rules": [
    "sms"
  ]
}

If you are creating a new profile with the send, then the merge field will allow the merging of the data in the to property, and if no property value can be found a blank string will replace the token.

How do I find my profile property names?

You can find your profile property names by calling retrieving you profile schema by calling this web service

Watch out for length restricitions

Some channels may have maximum message length restrictions. If your message exceeds then it will be truncated. Use a customBody to provide alternative content for these channels

Custom Messages

The "One" API enables you to optionally define a more specific message body for a channel, allowing you to utilise all of the features supported by that channel. If you don't define a customBody for a channel Comapi will automatically create a message body using the text in the body property.

See each channels section for more information on what content to populate the customBody property with e.g. Facebook's custom body

Some examples of custom body content are:

{
  "body": "Test from Comapi",
  "to": {
    "profileId": "**YOUR USER PROFILE ID**"
  },
  "customBody": {
    "fbMessenger": {
      "attachment": {
        "type": "image",
        "payload": {
          "url": "https://scontent.xx.fbcdn.net/v/t1.0-1/p200x200/17156020_1871286216424427_1662368582524349363_n.jpg?oh=22685c22a19fc2e28e69634e6a920972&oe=592FD3D1"
        }
      }
    }
  },
  "rules": [
    "fbMessenger"
  ]
}
{
  "body": "Test from Comapi",
  "to": {
    "phoneNumber": "447123123123"
  },
  "customBody": {
    "sms": {
      "from": "Comapi",
      "to": "447123123123",
      "body": "Hello, this is a test message",
      "deliverAfter": "2017-03-01T12:57:00.543Z",
      "clientRef": "124546"
    }
  },
  "rules": [
    "sms"
  ]
}

We make it simple

If you only want to define a customBody for some of the channels you are targeting Comapi will automatically create a message body for the other channels based on the text in the body property.

Merge Fields

In custom bodies you can optionally use merge fields to merge in properties from the Comapi profile you are addressing the message to. To do this simply add {{ profile property name }} anywhere in the properties.

If you are creating a new profile with the send, then the merge field will allow the merging of the data in the to property, and if no property value can be found a blank string will replace the token.

In addition to the profile merge fields you can reference the {{body}} and {{title}} properties of the request to avoid duplication. e.g.

{
  "body": "Hi {{profile.name}}, this body text can be merged in the customBody!",
  "to": {
    "profileId": "joe.blogs@acme.com"
  },
  "customBody": {
    "fbMessenger": {
      "text": "{{body}}"
    }
  },
  "rules": [
    "fbMessenger",
    "sms"
  ]
}

Channel Selection

You can easily send a message to a single channel by including a rules array with a single channel listed. For example:

"rules": [
    "sms"
  ]
{
  "body": "Test from Comapi",
  "to": {
    "phoneNumber": "447123123123"
  },
  "rules": [
    "sms"
  ]
}

Please see our Branch section for more information about how you instruct Comapi to use multiple channels.

Supported channels are:

SMS

Send and receive SMS globally, with optional delivery receipts

Get started with the docs here

Facebook Messenger

Send messages via Facebook Messenger and reach an audience of over 1 billion monthly active users.

Get started with the docs here

App Messaging

App messaging allows you to build secure messaging and chat services into your native and web applications. Use App messaging for P2P chat, B2C chat or A2P messaging for alerts reminders and notifications.

Get started with the docs here

Inbox

Inbox is a complete platform for engaging and communicating to your customers via any mobile or native application. Inbox gives you the power to send rich HTML content and push notifications into apps.

Get started with the docs here

Custom Channels

If we don’t have it covered or you want to use existing suppliers you can use our custom channel to call your existing providers API or call any other communication channel. Total flexibility without the hassle of changing.

Get started with the docs here

Additional Options

Setting Expiry

For time sensitive messages you can specify a expiry date and time where Comapi won't deliver you message after. Where a channel supports message expiry the expiry time will be passed on automatically.

To set an expiry set the following property:

Property
Type
Description

expiresOn

date-time

Date/Time (in UTC, ISO 8601 format)

"expiresOn": "2017-03-16T17:05:02Z"
{
  "body": "Test from Comapi",
  "to": {
    "phoneNumber": "447123123123"
  },
  "expiresOn": "2017-03-16T17:05:02Z",
  "rules": [
    "sms"
  ]
}

Others...

These other properties may be of use:

Property
Type
Description

title

string

The title to use to describe the message (if supported by the channel)

conversationId

string

Optional conversationId, used to correlate messages within Comapi and on any external channels, if supported

metadata

object

Custom metadata relating to the message that will be echoed back to you on any webhook events related to the message. Can be used to correlate data, or aid webhook processing efficency

channelOptions

object

Allows easy configuration of popular options for a channel without the need for a full customBody. See each channels details for further details.

Message Send Response

The response from the API changes depending on whether you sent a single message or a batch of message, please refer to the appropriate section below:

Single Submission Response Details

Successful Submission Response

Upon a successful send to the "One" API you will receive a HTTP 201 and a JSON body in the following format:

Property
Type
Description

messageId

string

Unique id for this message

sentOn

string

Date/Time (in UTC, ISO 8601 format) that the message sent was accepted

status

string

One of:

  • processing
  • failed
  • sent

statusDetails

object

Specific details related to the current status.

_createdOn

string

Date/Time (in UTC, ISO 8601 format) that the message was created

_createdBy

string

The profile id that created the message

_updatedOn

string

Date/Time (in UTC, ISO 8601 format) that the message was last updated

_updatedBy

string

The profile id that last updated the message

{
  "_createdOn": "2017-03-17T14:22:40.633Z",
  "_updatedOn": "2017-03-17T14:22:40.633Z",
  "_createdBy": "access:3dd1558a-9db3-406a-8460-cbe5100a7628",
  "_updatedBy": "access:3dd1558a-9db3-406a-8460-cbe5100a7628",
  "to": {
    "phoneNumber": "447123123123"
  },
  "senderProfileId": "M0rsy",
  "body": "I love Comapi",
  "rules": [
    "sms"
  ],
  "sentOn": "2017-03-17T14:22:40.627Z",
  "channelOptions": {
    "sms": {
      "from": "Comapi",
      "allowUnicode": false
    }
  },
  "status": "processing",
  "statusDetails": {
    "channelId": "sms"
  },
  "messageId": "d8e296bb-97c4-4495-b98f-1e32e0b11628"
}

Failed Submission

If there are issues with the submitted request a HTTP 400 will be returned with the body describing the validation errors.

Getting the final status for a message

Once submitted Comapi will process your message and try to deliver it down one of the channels specified. It is still possible the message can fail at this stage if no channels accept the message. To find the final status of a message you can query it using the Get Status method, or register a Webhook to have data pushed to your systems.

Full API Specification

Please see the full API specification for further details about sending messages with the "One" API

Batch Submission Response Details

Successful Submission Response

Upon a successful send to the "One" API you will receive a HTTP 202 and a JSON body. The JSON will be an array of objects in the following format, one for each message submitted in the batch:

Property
Type
Description

index

int

The index of the message send request in the original batch submission this response is for

messageId

string

The unique identifier for the message. This id can be used to request status details and correlate inbounds and receipts to the original message

Whats happens after sending a batch of messages

The messages will be processed as fast as possible after submission but it may take a few seconds before this process is finished and the message if available for status updates. You can get updates on your messages statuses asynchronously using our webhooks system.

[
  {
    "index": 0,
    "messageId": "58bde0da-b380-4cb2-8dd1-ca74fc7a0cda"
  },
  {
    "index": 1,
    "messageId": "0aee96a8-09c9-4192-bf6d-c5f20a323956"
  }
]

Failed Submission

If there are issues with the submitted request a HTTP 400 will be returned with the body describing the validation errors.

Full API specification

Please see the full API specification for further details about sending messages with the "One" API

Receiving Inbound Messages and Receipts

All feedback from channels including receipts and inbound messages are converted to a canonical set of events. This means that you only have to implement one webhook in order to receive information about all channels and future channels supported by Comapi!

To find out more about webhooks and the events that can be forwarded to your systems please see the following section: Webhooks

 

This section covers the channels the Comapi "One" API supports:

Trial mode restrictions

If your account is still in the free trial mode then SMS sending is restricted to only that mobile number you used when signing up for your free account. Sending to any other number will cause the SMS message to fail.

Channel Identifier

In Comapi this channel can be referenced using: sms

This identifier can be used in the rules array, and customBody sections.

Sending an SMS

Sending an SMS via the "One" API is very simple all you required to so is ensure that your channel rules include the sms channel and you specify either a phoneNumber in international format or a profileId with a phone number in the to section of your request, and provide a body message e.g.

{
  "body": "Sending is easy with Comapi",
  "to": {
    "phoneNumber": "447123123123"
  },
  "rules": [
    "sms"
  ]
}

Message Length

The maximum length of SMS messages supported is 2000 characters.

Localiser & Stitching

In the basic SMS send example above you will notice we have not defined what phone number or identifier the SMS is sent from, so how can this work? The answer is that Comapi will automatically assign a suitable from phone number for the destination country from a pool of numbers. If your message is sent from a pool number, that number is then stitched to the destination phone number, and will be used for all future SMS communications within a 60 day period - keeping the individual user experience consistent.

Channel Options

The following additional channel options can be used to control the SMS channels most common options. To use the channel options create a object with your options in the requests channelOptions section in the sms property:

Property
Type
Description

from

string

The phone number, alpha identifier, or short code you are sending from

allowUnicode

boolean

This toggles whether having Unicode characters in the message body will be allowed or not

Please read why Unicode is important below before using this setting

{
  "body": "Sending SMS with Comapi is easy!",
  "to": {
    "phoneNumber": "447123123123"
  },
  "channelOptions": {
  	"sms": {
  		"from": "Comapi",
  		"allowUnicode": true
  	}
  },
  "rules": [
    "sms"
  ]
}

Possible from values

You can send SMS from the following types of originator:

Type
Example
Description

Mobile number

447123123123

A mobile number in international format. These can be rented for your desired country from Comapi

Short Code

60006

A short numeric identifier that requires companies to register for. These can be rented for your desired country from Comapi

Alpha

COMAPI

String value between 2-11 characters of 0-9, a-z, A-Z, hyphen, underscore, full-stop and space

Why is Unicode important with SMS

SMS by default is limited to a small character set called the GSM character set. If you body contains characters outside of the GSM character set it will require the allowUnicode channel option to be enabled if you want to be able to send the message.

Unicode SMS messages can cost up to 3 times more!

Unicode messages require over double the amount of data to be sent over mobile networks and therefore the numbers of characters you can fit into a SMS segment is severely affected i.e. A message using 160 characters which only uses the GSM character set will cost 1 SMS, whereas if it has a Unicode character the same message will cost 3 SMS.

Please see our knowledge base article on how message lengths are calculated for SMS.

Custom Body

The Comapi "One" API automatically creates a text based SMS message if you only define the body property when sending a message, but you can specify advanced options if you use the customBody property and define a sms object within it using the details below:

Property
Type
Description

to*

string

The phone number to send to. This should be in international format (e.g. 447123123456), or if countryIsoCode is specified, in local format (e.g. 07123123456)

from*

string

A string containing the from value. See the possible options above

countryIsoCode

string

An optional country in ISO 3166-1 alpha-3 format. If specified, the to field can contain a number in local format, e.g. GBR would allow 07123123456

body*

string

The message text

deliverAfter

datetime

A date time specifying when we should try to deliver the message. This must be a UTC time in ISO 8601 format, e.g. 2015-10-21T13:28:06.419Z

validUntil

datetime

A date time specifying how long we should try to deliver the message for. This must be a UTC time in ISO 8601 format, e.g. 2015-10-21T13:28:06.419Z

filterSetName

string

If you have enabled Intelligent Filtering on your account, in the portal, you can specify which filter set is applied to this message.

rejectUnicode

boolean

If set to true, any messages containing Unicode characters will not be sent.

Please read why Unicode is important below before using this setting. If not sure we recommend it is set to true

Examples of sends using SMS custom bodies are:

{
  "body": "Test from Comapi",
  "to": {
    "profileId": "dave@acme.com"
  },
  "customBody": {
    "sms": {
      "from": "Acme Corp",
      "to": "447123123123",
      "body": "Hello, this is a message using a SMS customBody",
      "deliverAfter": "2017-03-01T12:57:00.543Z",
      "rejectUnicode": "true"
    }
  },
  "rules": [
    "sms"
  ]
}

Cannot use a customBody and channelOptions together

It's not possible to define a customBody and channelOptions together in the same request as Comapi doesn't know which settings to use, so either use the universal send with channelOptions or define everything using a customBody

Receipts and Inbounds

To receive feedback or inbound messages from SMS sends please see the following:

Inbound SMS

Allows you to receive SMS messages sent from phones to your Comapi hosted mobile numbers, short codes or keywords. Messages are delivered to a URL of your choosing using Comapi's webhook system. See the Inbound event in the Message Events section for more details.

Receipts

If you need to to know the status of messages you've sent using one of our APIs, you can request that delivery receipts are forwarded to a URL of your choosing using Comapi's webhook system. See the events in the Message Events section for more details on the receipt events you can receive.

You can receive the following types of receipts:

  • Sent
  • Delivered
  • Expired
  • Failed

Click-through Forwarding

If your system needs to know whether URLs inserted into SMS messages have been clicked, check how long the clicker took between receiving the message and clicking the URL or discover details about the clicker's operating system, you can have details forwarded to a URL of your choosing.

Check out our quick starts...

Check out our Sending SMS quick start for simple code example of how to send SMS with Comapi

Suggest Edits

Facebook

Using Facebook with Comapi

 

Channel Identifier

In Comapi this channel can be referenced using: fbMessenger

This identifier can be used in the rules array, and customBody sections.

Enabling the Facebook Channel

See our Facebook channel setup guide for more information on how to setup Comapi for Facebook Messenger sending.

Obtaining a Facebook Messenger Id

There are two pieces of data that Facebook Messenger uses to match a target customer for a message and they are:

  • Facebook Messenger Id - A unique numeric identifier for a Facebook user and your Facebook page combination
  • Mobile Phone Number - Use a mobile phone number in international format as long as you have enabled Customer Matching on your Facebook page

Not all Facebook users enter a mobile number

Phone number matching for Facebook Messenger can only work if the target Facebook user has entered their mobile number into their Facebook profile

To guarantee being able to send a message to a customer via Facebook Messenger you must obtain a unique Facebook Messenger Id for them and your page combination. This can be achieved in the following ways:
1) The customer elects to receive messages using the Facebook Send to Messenger plugin from a web page
2) The customer sends your Facebook page a direct message via Messenger
3) The customer clicks on the Message Us Facebook plugin and sends a message

Using the Facebook Web Plugins

Facebook provides web plugins you can easily add to your web site at appropriate points to invite customers to allow you to interact via Facebook Messenger with them. The plugins are:

  • Message Us - takes the person directly to Messenger and allows them to initiate a conversation with you
  • Send to Messenger - allows you to initiate a conversation with them in Messenger by providing a Facebook Messenger id for the customer via a webhook call
  • Checkbox Plugin - Not currently supported
You can find out more about the Facebook web plugins <a href="https://developers.facebook.com/docs/messenger-platform/plugin-reference" target="_new">here</a>

You can find out more about the Facebook web plugins here

In order to use the Send to Messenger plugin with Comapi you can use one of the following approaches:
1) Pass the profile id of the user securely with the Send to Messenger data and let Comapi update the customer profile with their Facebook Messenger Id in the fbMessengerId attribute
2) Capture and record the Facebook Messenger Id yourselves for use with the Comapi "One" API

Using the Send to Messenger web plugin with Comapi

To use the Facebook Send to Messenger plugin with Comapi we recommend doing the following:

Create secure meta data to use with the plugin

In order for Comapi to be able to update the correct user profile with the Facebook messenger Id received we need you to attach some secure meta data to the Facebook plugin call. To generate this data is a simple web service call as follows:

1) Ensure you have setup your Facebook page with Comapi as detailed in the Facebook Messenger channel setup guide
2) From your server side call the Facebook Meta Data Service passing the profileId you want to attach the Facebook Messenger Id to, this is usually the logged user id. This service generates the secure meta data Comapi can recognise.
3) Ensure that the secure meta data created in step 2 is passed into the Send to Messenger web control in the data-ref property e.g:

<div class="fb-send-to-messenger" 
  messenger_app_id="336037380081042" 
  page_id="PAGE_ID" 
  data-ref="**Add your secure meta data from step 1 here**" 
  color="blue" 
  size="standard">
</div> 

4) Ensure that the Send to Messenger control is implemented in your web site according to the instructions. Note Comapi will handle the Opt-in callback from Facebook for you, so no need to do this, and the messenger_app_id is set to 336037380081042

How to find your Facebook pages page_id

There are many ways to do this you can find mentioned on the internet but the easiest is to use this website: https://findmyfbid.com

No `Send to Messenger` control rendered

The Facebook Send to Messenger web plugin only renders if it is happy with the parameters passed to it. Things to check are:

  • messenger_app_id is set to Comapi's app id 336037380081042
  • Your Facebook page has been configured in the Comapi portal as the Facebook channel
  • Your Facebook page is published
  • You have implemented the Facebook web plugin code correctly

Capturing the Facebook messenger Id yourself

Please see the following Facebook documentation about the Facebook web plugins for further instructions on how to achieve this, but you will be required to create a Facebook application and a web hook to receive the opt in data from Facebook.

Where can I find a customer Facebook Id after they have opted in?

Comapi will automatically receive Facebook Ids as your users contact your Facebook page or click on Send to messenger widgets and store their Facebook Id in the customers Comapi profile in the fbMessengerId attribute.

If Comapi cannot find any secure meta data with a Facebook opt-in or inbound message it will automatically create a profile for the user, so you can message them. If it does find secure meta data it will decode this and store the Facebook Id and information against the profile specified in the meta data.

In addition to storing the Facebook Id on the profile Comapi will automatically retrieve the following details from Facebook:

  • First name
  • Last name
  • Profile picture
  • Locale
  • Timezone
  • Gender
  • Whether they have payments enabled

This additional information is held in the facebook section of the users profile.

Sending a Facebook Messenger message

The Comapi One API allows you to address customers on Facebook using one or more of the following:

  • Customer Profile Id - This is the easiest option, Comapi will automatically store the customer Facebook Id against their profile when they opt in to messages using the Send to Messenger widget or send an inbound message, and then use it when necessary
  • Phone Number Matching - If you have enabled Customer Matching on your Facebook page simply pass a phone number in international format
  • Facebook Messenger Id - If you have already captured the customers Facebook Messenger Id for your Facebook page you can use this

Comapi will always use a Facebook Messenger Id in preference to the phone number or profile if included in the API call.

To test your channel you can call the Comapi "One" API targeting the Facebook channel. To do this easily you could use a tool such as Postman or create the code in the language of your choice using the API reference docs.

The following request JSON can be used to perform a send with text content:

{
  "to": {
    "profileId": "dave@acme.com"
  },
  "body": "My customer message via Facebook Messenger using a profile id",
  "rules": [
    "fbMessenger"
  ]
}
{
  "to": {
    "phoneNumber": "447123123123"
  },
  "body": "My customer message via Facebook Messenger using phone number matching",
  "rules": [
    "fbMessenger"
  ]
}
{
  "to": {
    "fbMessengerId": "11112223333444444"
  },
  "body": "My customer message via Facebook Messenger using explicit fbMessengerId",
  "rules": [
    "fbMessenger"
  ]
}
{
  "to": {
    "fbMessengerId": "11112223333444444",
    "mobileNumber": "441234123123"
  },
  "body": "My customer message via Facebook Messenger",
  "rules": [
    "fbMessenger",
    "sms"
  ]
}

Custom Body

Facebook Messenger is capable of sending many types of messages including:

The Comapi "One" API automatically creates a text based Facebook message if you only define the body property when sending a message, but you can send any of the Facebook message body types if you use the customBody property and define a fbMessenger object within it that complies with the Facebook Graph API's message object as defined in the Facebook docs. Essentially you can pass any Facebook message type you desire if you define this property, or let Comapi automatically create you a basic text message.

Examples of sends using Facebook custom bodies are:

{
  "body": "Test from Comapi",
  "to": {
    "profileId": "**YOUR USER PROFILE ID**"
  },
  "customBody": {
    "fbMessenger": {
      "attachment": {
        "type": "image",
        "payload": {
          "url": "https://scontent.xx.fbcdn.net/v/t1.0-1/p200x200/17156020_1871286216424427_1662368582524349363_n.jpg?oh=22685c22a19fc2e28e69634e6a920972&oe=592FD3D1"
        }
      }
    }
  },
  "rules": [
    "fbMessenger"
  ]
}
{
  "body": "Test from Comapi",
  "to": {
    "profileId": "**YOUR USER PROFILE ID**"
  },
  "customBody": {
    "fbMessenger": {
    "text":"Pick a color:",
      "quick_replies":[
        {
          "content_type":"text",
          "title":"Red",
          "payload":"DEVELOPER_DEFINED_PAYLOAD_FOR_PICKING_RED"
        },
        {
          "content_type":"text",
          "title":"Green",
          "payload":"DEVELOPER_DEFINED_PAYLOAD_FOR_PICKING_GREEN"
        }
      ]
    }
  },
  "rules": [
    "fbMessenger"
  ]
}
{
  "body": "Test from Comapi",
  "to": {
    "profileId": "**YOUR USER PROFILE ID**"
  },
  "customBody": {
    "fbMessenger": {
      "attachment": {
        "type": "template",
        "payload": {
          "template_type": "receipt",
          "recipient_name": "Stephane Crozatier",
          "order_number": "12345678902",
          "currency": "USD",
          "payment_method": "Visa 2345",
          "order_url": "http://petersapparel.parseapp.com/order?order_id=123456",
          "timestamp": "1428444852",
          "elements": [
            {
              "title": "Classic White T-Shirt",
              "subtitle": "100% Soft and Luxurious Cotton",
              "quantity": 2,
              "price": 50,
              "currency": "USD",
              "image_url": "http://clipart-library.com/images/rcjKk9yni.png"
            },
            {
              "title": "Classic Gray T-Shirt",
              "subtitle": "100% Soft and Luxurious Cotton",
              "quantity": 1,
              "price": 25,
              "currency": "USD",
              "image_url": "http://clipart-library.com/images/yTkAjXk9c.png"
            }
          ],
          "address": {
            "street_1": "1 Hacker Way",
            "street_2": "",
            "city": "Menlo Park",
            "postal_code": "94025",
            "state": "CA",
            "country": "US"
          },
          "summary": {
            "subtotal": 75,
            "shipping_cost": 4.95,
            "total_tax": 6.19,
            "total_cost": 56.14
          },
          "adjustments": [
            {
              "name": "New Customer Discount",
              "amount": 20
            },
            {
              "name": "$10 Off Coupon",
              "amount": 10
            }
          ]
        }
      }
    }
  },
  "rules": [
    "fbMessenger"
  ]
}

Receipts and Inbounds

To receive feedback or inbound messages from Facebook sends please see the following:

Inbound Messages

Allows you to receive Facebook Messenger messages sent from phones or Facebook to your Facebook page. Messages are delivered to a URL of your choosing using Comapi's webhook system. See the Inbound event in the Message Events section for more details.

Receipts

If you need to to know the status of messages you've sent using one of our "One" API, you can request that delivery receipts are forwarded to a URL of your choosing using Comapi's webhook system. See the events in the Message Events section for more details on the receipt events you can receive.

You can receive the following types of receipts:

  • Sent
  • Delivered
  • Read
  • Failed

Check out our quick starts...

Check out our Using Facebook quick start to see a simple code example of how to use Facebook with Comapi

Suggest Edits

App Messaging

 

Channel Identifier

In Comapi this channel can be referenced using: appMessaging

This identifier can be used in the rules array, and customBody sections.

Sending messages

Sending in-app messages via App Messaging is simple using the Comapi "One" API; simply ensure that in your rules or ruleSet you include the appMessaging channel, and follow the guidance below:

Addressing your messages

To target who a App Messaging message is delivered to include a profileId in the to section of the request. For more information on profile Ids see the The One API Overview.

Conversations

Messages are grouped together by a conversationId into conversations. If you don't specify a conversationId on a send a unique one will automatically be assigned therefore starting a new conversation. If you reference an existing conversationId then the new message will be appended to that conversation.

{
  "body": "App Messaging send from Comapi",
  "to": {
    "profileId": "dave@acme.com"
  },
  "rules": [
    "appMessaging"
  ]
}
{
  "body": "App Messaging send from Comapi",
  "to": {
    "profileId": "dave@acme.com"
  },
  "conversationId": "dave@acme.com:Acme Support",
  "rules": [
    "appMessaging"
  ]
}

Channel Options

The following additional channel options can be used to control the App Messaging channels most common options. To use the channel options create a object with your options in the requests channelOptions section in the appMessaging property:

Property
Type
Description

from

from object

The profile Id the message is being sent from, if not set the profile Id from the access token is used

from object

Property
Type
Description

profileId*

string

The profile Id the message is being sent from, if not set the profile Id from the access token is used

name

string

The name to display to the user

{
  "body": "How can we help you today?",
  "to": {
    "profileId": "dave@acme.com"
  },
  "channelOptions": {
    "appMessaging": {
      "from": 
      {
      	"profileId": "Acme-Support",
      	"name": "Support"
      }
    }
  },
  "conversationId": "dave@acme.com:Acme Support",
  "rules": [
    "appMessaging"
  ]
}

Custom Body

The Comapi "One" API automatically creates a text based App Messaging message if you only define the body property when sending a message, but you can specify advanced options if you use the customBody property and define a appMessaging object within it using the details below:

Property
Type
Description

from

from object

Who the message is being sent from

metadata

object

Any JSON meta data you want to send with the message, useful for driving custom experiences

parts*

array of messagePart

The parts that form your message i.e. the content

alert

messageAlert object

Details of the alert to be displayed on receipt of the message

from object

Property
Type
Description

profileId*

string

The profile Id the message is being sent from, if not set the profile Id from the access token is used

name

string

The name to display to the user

messagePart object

Property
Type
Description

data*

string

String representationof the message part, for non text MIME types use base64

type

string

The MIME type (if applicable) of the part

name

string

A name for the part

url

string

A URL associated with the message part

size

integer

Size of the message part in bytes

messageAlert object

Property
Type
Description

title

string

Title for the notification to send to all platforms. Note that this is ignored if any details are set in platforms

body

string

Body for the notification to send to all platforms. Note that this is ignored if any details are set in platforms

platforms

messageAlertPlatforms object

The platform specific details for the alert. If specified, these are set verbatim and alert.title / alert.body values are ignored

messageAlertPlatforms

Property
Type
Description

profileId*

string

The profile Id the message is being sent from, if not set the profile Id from the access token is used

name

string

The name to display to the user

messageAlertApns

Property
Type
Description

badge

integer

The value to display on the application badge

sound

object

The soundfile to play on the device when the notification arrived

alert

string

The alert to display on the device when the notification arrives

payload

object

The payload to send to the device

messageAlertFcm

Property
Type
Description

collapse_key

object

The collapse key to send to FCM

data

object

The payload to send to the device

notification

messageAlertFcmNotification object

Details of the notification to display

messageAlertFcmNotification

Property
Type
Description

title

string

The title to display

body

string

The body to display

icon

string

The icon to display

Examples of sends using SMS custom bodies are:

{
  "body": "App Messaging send from Comapi",
  "to": {
    "profileId": "dave@acme.com"
  },
  "conversationId": "dave@acme.com:Acme Support",
  "customBody": {
    "appMessaging": {
      "from": "Acme Corp",
      "alert": {
        "title": "Acme Support Update",
        "body": "You have a new message on your support ticket, click here to read it"
      },
      "parts": [
        {
          "data": "Your item has arrived and your account has been credited $50.00"
        }
      ]
    }
  },
  "rules": [
    "appMessaging"
  ]
}

Cannot use a **customBody** and **channelOptions** together

It's not possible to define a customBody and channelOptions together in the same request as Comapi doesn't know which settings to use, so either use the universal send with channelOptions or define everything using a customBody

Receipts and Inbounds

To receive feedback or inbound messages from App Messaging sends please see the following:

Inbound Messages

Allows you to receive App Messaging messages sent. Messages are delivered to a URL of your choosing using Comapi's webhook system. See the Inbound event in the Message Events section for more details.

Receipts

If you need to to know the status of messages you've sent using one of our "One" API, you can request that delivery receipts are forwarded to a URL of your choosing using Comapi's webhook system. See the events in the Message Events section for more details on the receipt events you can receive.

You can receive the following types of receipts:

  • Sent
  • Delivered
  • Read
  • Failed
Suggest Edits

Custom Channels

 

Channel Identifier

In Comapi custom channels can be referenced using there custom channel ids that are shown in the Custom Channels tab of the Branch settings for your API Space (see below highlighted in yellow)

This identifier can be used in the rules array, and customBody sections.

Sending messages

Sending custom channel messages is simple using the Comapi "One" API; simply ensure that in your rules or ruleSet you include the custom channel id, and follow the guidance below:

Addressing your messages

To target who a custom channel message is delivered to ensure you include either the field referred to in the custom channels Id field name property or a profileId with this information in the to section of the request. For more information on profile Ids see the The One API Overview.

Title and Body

The title and body fields of the request will be used to create the custom channel request, so ensure you specify these.

{
  "body": "A message via custom channels",
  "title": "A email from Comapi",
  "to": {
    "email": "info@comapi.com"
  },
  "rules": [
    "custom_email"
  ]
}

Channel Options

The channel options section is not used with custom channels.

Custom Body

You can specify a custom body for a custom channel by adding a property to the customBody section with the same name as your custom channel id. The value must be a string so if passing JSON please ensure you escape it to create a string.

If specified this will be used instead of your defined body template for the custom channel, but you must still provide a to section identifier, we suggest you provide a profileId, which is simply your unique identifier for your user.

{
  "to": {
    "profileId": "info@comapi.com"
  },
  "customBody": {
	"custom_email": "{  \"personalizations\": [    {      \"to\": [        {          \"email\": \"info@comapi.com\"        }      ]    }  ],  \"from\": {    \"email\": \"m0rsy@comapi.com\"  },  \"subject\": \"Custom bodies with custom channels\",  \"content\": [    {      \"type\": \"text\/html\",      \"value\": \"Hi from Comapi\"    }  ]}"
  },
  "rules": [
    "custom_email"
  ]
}

Receipts and Inbounds

You can create two way custom channel integrations with Comapi, but you will be responsible in creating the simple adaptor layer between the 3rd party systems feedback mechanisms and Comapi.

Receiving data from custom channels

Receiving data from custom channels

Your adaption layer will need to receive the data from the 3rd party system however they specify, such as a web page, and then call on to Comapi to record the data.

Comapi provides two web methods to allow receipts and inbound messages to be recorded in the system, so analytics can be recorded and receipts and inbound messages sent via our webhooks to your integrations. The web methods are:

Receipts

To record receipt values for a custom channel for a message you need to call the Messages web services status update method
with the Comapi messageId and status.

Where can I find the messageId?

Hopefully the 3rd party channel you are integrating will allow meta data or a message reference to be added to the request. If so you can use the {{messageId}} token when setting up the custom channels body template to map the Comapi message id in. This will them often be echoed on any inbound data.

See our Custom Channels section for more info.

Inbound Messages

To record inbound messages from your 3rd party channel you need to call the Messages services inbound method.

You will need to populate the from and to objects with the identifiers used by the channel, and if possible provide a text only version of the message in the body property. The channel Data object is used to record the raw unaltered body provided by the channel, so it is available for display or processing later.

Coming soon for the moment you can use a Custom Channel to send via Inbox's Integration API

Suggest Edits

Branch

Using Branch to find the best channel to contact your customer

 

Branch works in conjunction with The "One" API and lets you set rules to prioritise the order of channels in which a message to a customer can be sent. Branch will work through these channels in priority order trying to find the best channel in order to send your message automatically.

To find out more see the Branch overview in the Get Started section.

How to use Branch

To use Branch features with a message send is very simple, you just specify more than one channel to send to in your preference order, which can be done explicitly in the JSON or you can refer to a Branch rule set to allow easy tweaking to the Branch rules without a code change later. To find out more about Branch rule set setup see the Branch channel setup guide.

Explicit Rules

To use Branch with explicitly set channels is very simple, simply include more than one channel id in the rules array in your message send in the priority order required. For example:

"rules": [
  "fbMessenger",
  "sms"
  ]
{
  "body": "Branch send from Comapi",
  "to": {
    "profileId": "dave@acme.com",
    "phoneNumber": "447123123123"
  },
  "rules": [
    "fbMessenger",
    "sms"
  ]
}

Configured Rules

To use a reference to a Branch rule set simply replace the usual rules section with a ruleSet property with it value set to the rule set id you want to use.

"ruleSet": "Facebook-First"
{
  "body": "Branch send from Comapi",
  "to": {
    "profileId": "dave@acme.com",
    "phoneNumber": "447123123123"
  },
  "ruleSet": "Facebook-First"
}

Don't confuse Branch

Ensure you specify either a rules or ruleSet section but not both!

 

Comapi can forward you events in real-time to a web page of your choosing so that you integrate your own systems, for processing or analytics. You can choose what events you are interested in, so you only receive the information you want and need.

Creating a web page to receive data

Receiving events

In order to setup a webhook registration you will need to create a web page that the event data will be posted to. The web page will have JSON data sent to it via HTTPS POST and will be expected to return an appropriate HTTP status code such as: HTTP 200 - OK response if it successfully accepts the posted data; the expected HTTP status codes are:

HTTP Status Code
Type
Description

200, 201, 2XX

OK

Data accepted

401

Unauthorized

Issue authenticating the sender, or HMAC not valid

400

Bad Request

Could not process the data sent; this will not be retried

Any other

-

Failed to accept data, this will be retried

Retry Policy

If a failure occurs accepting data or your server cannot be contacted Comapi will use a gradual back off retry schema for up to 24 hours and then move the event to dead queue. This system will ensure glitches and downtime do not result in data loss, but it will be delayed.

Processing the events

Each forwarded event is sent individually, and therefore at peak times your system must be able to accept many parallel calls. Your system will be permitted 10 seconds to respond to a forwarded event before Comapi will consider the server unresponsive and the event send in error.

Event message structure

Each event will be sent in a standard enveloper message with a payload that is the actual event data. The event payloads are documented in the sub sections following this page, such as App Messaging - Message Events .

The envelope format is:

Property
Type
Description

required
eventId

string

The unique identifier for this event

required
accountId

int

The Comapi account id the event is associated with

required
apiSpaceId

string

The API Space Id the event is from

required
name

string

The name/type of the event being received

required
payload

object

The specific event data (see this sections pages for more details)

required
revision

int

The revision number for the event which increments with each event

required
etag

string

The ETag entity hash, commonly used for detecting change

required
timestamp

date time

The UTC time the event occurred in ISO 8601 format

The following is an example event you could receive:

{
  "eventId": "ca58832d-d67a-412e-9b28-e51b675ea142",
  "accountId": 123,
  "apiSpaceId": "c124df6e-4352-4b26-a32a-c3032bea7a01",
  "name": "message.sent",
  "payload": {
    "id": "ec7e182f-4d87-4135-b989-b26ab8d74f05",
    "details": {
      "channel": "sms",
      "additionalInfo": {
        "to": "441231123123",
        "successful": true
      },
      "channelStatus": {
        "sms": {
          "status": "sent",
          "details": {
            "to": "441231123123",
            "successful": true
          },
          "updatedOn": "2017-04-11T08:19:48.106Z"
        }
      }
    },
    "updatedOn": "2017-04-11T08:19:48.106Z"
  },
  "revision": 2,
  "etag": "\"2e-PNWSn3HlxaIB/CYz7LaR1XhMvDE\"",
  "timestamp": "2017-04-11T08:19:48.494Z"
}
{
  "eventId": "e8b015b0-dd11-4f2d-a4b8-52c20739e16b",
  "accountId": 123,
  "apiSpaceId": "c124cf6e-4352-4b26-a71a-c3032bea7a01",
  "name": "profile.create",
  "payload": {
    "id": "Bob Smith"
  },
  "revision": 0,
  "etag": "\"15-w27xhc3Oc4ZW/h3hpKppJQ88/rU\"",
  "timestamp": "2017-04-10T14:52:29.484Z"
}

Testing made simple

You can test receiving events simply by creating a request bin page for free, setting this as the URL for your webhook.

Security

The following processing and guidance should be followed in order to ensure your webhook remains secure:

HTTPS Recommended

Comapi will forward data to either HTTP or HTTPS URLs, but we recommend using HTTPS connections to ensure data privacy. Please ensure your web page can accept HTTPS requests, and uses a certificate from a public certificate authority as we cannot accept self signed certificates.

Authenticating and verifying data

Comapi uses the HMAC hashes to both authenticate and ensure the data has not been altered in transit.

Comapi will use the secret you configure on your webhook settings to create a hash of the event data (HTTP body) using the HMAC SHA-1 algorithm and then store this in the requests HTTP headers as X-Comapi-Signature. When you receive data via your webhook page your must create a hash of the received request body using UTF-8 encoding with the SHA-1 algorithm and your secret, and then compare it against the received hash from the X-Comapi-Signature HTTP header. If they match you can trust the data, otherwise you should reject the data and return a HTTP 401.

The HMAC is not Base64 encoded!

Some out of the box HMAC SHA-1 algorithms return the HMAC result Base64 encoded, please note Comapi does not Base64 encode the HMAC result!

Revision processing

The Revision property will increment for each message type, therefore giving you an indication of relative order. The sequence may not be contiguous, but will always increment as more events are generated.

In some circumstances the Revision property value can be used to recognise when it is safe to discard data, such as when you receive a message status update with a lower revision id than the last one you processed for a message.

We do not guarantee events in the correct sequence

Due to the nature of large distributed systems we cannot guarantee the order events will be forwarded in exactly the chronological order they occurred in, and therefore the provide a Revision property to help you sequence the events when you receive them.

Setting up to receive data from the webhook

Configure the forwarding

To setup or modify a webhook registration go to the Comapi Portal and open the Platform Configuration -> Integration section, and the Webhooks tab shown below:

Setting up a webhook

Setting up a webhook

Adding a new webhook registration

  • If the new webhook form is not displayed click the green plus button to open it
  • Fill in the Name field with a meaningful name for your webhook e.g. Analytics feed
  • Add your URL to your webhook page to the Target URL field
  • Add a secret to be used to hash your events, so that you can verify we sent the data and it hasn't been tampered with (See the security section above)
  • Choose your events to receive
  • Scroll down and click Save to create the webhook registration

Check out the quick starts....

See our Receiving Data using Webhooks quick start for some example code for creating a webhook reception page.

Suggest Edits

One API - Message Events

 

Sent

Examples

{
  "eventId": "0dceaf0d-93ea-4314-a6f8-b0cffe69799a",
  "accountId": 1234,
  "apiSpaceId": "c924cf6e-4352-4b26-a71a-c2032bea7a01",
  "name": "message.sent",
  "payload": {
    "details": {
      "channel": "sms",
      "additionalInfo": {
        "to": "447123123123",
        "successful": true,
        "clientRef": "e9f154c8-4011-493f-bb73-09cfcf8a1411"
      },
      "channelStatus": {
        "sms": {
          "status": "sent",
          "details": {
            "to": "447123123123",
            "successful": true,
            "clientRef": "e9f154c8-4011-493f-bb73-09cfcf8a1411"
          },
          "updatedOn": "2017-08-31T09:26:14.798Z"
        }
      }
    },
    "updatedOn": "2017-08-31T09:26:14.799Z",
    "metadata": {
      "data": "My correlation data"
    },
    "id": "e9f154c8-4011-493f-bb73-09cfcf8a1411"
  },
  "revision": 2,
  "etag": "\"2e-PLB2C6w/yVdPjUhuRiZtlvt8k0Q\"",
  "timestamp": "2017-08-31T09:26:14.604Z"
}
{
  "eventId": "487b9258-aee0-48da-b410-b253823fda10",
  "accountId": 1234,
  "apiSpaceId": "fa42e8af-5fc4-40d4-a38c-034535c0a385",
  "name": "message.sent",
  "payload": {
    "details": {
      "channel": "fbMessenger",
      "additionalInfo": {
        "fbMessengerId": "1924625277845988",
        "messageId": "mid.$cABAnV7v5AVdiiyqOdVcWeVGDyz-6"
      },
      "channelStatus": {
        "fbMessenger": {
          "status": "sent",
          "details": {
            "fbMessengerId": "1924625277645988",
            "messageId": "mid.$cABAnV7v5AVdiiyqOdVcWeVGDyz-6"
          },
          "updatedOn": "2017-05-30T15:06:56.934Z"
        }
      }
    },
    "updatedOn": "2017-05-30T15:06:56.934Z",
    "id": "79b156e6-c314-4114-ac6a-be8eab624951"
  },
  "revision": 2,
  "etag": "\"2e-Zb4yVYmVGmr9WRNuY47SF8Vd21I\"",
  "timestamp": "2017-05-30T15:06:57.592Z"
}

Delivered

Examples

{
  "eventId": "2b612e8c-b88d-485c-8e98-81939f879500",
  "accountId": 1234,
  "apiSpaceId": "c224cf6e-4352-4b26-a71a-c9032bea7a01",
  "name": "message.delivered",
  "payload": {
    "details": {
      "channel": "sms",
      "channelStatus": {
        "tranid": "946245251",
        "messageguid": "9fd260a5-52a7-4d84-bc08-10e04d07d7b7",
        "statusid": "1",
        "recipient": "447123123123",
        "statusdescription": "Messages delivered to handset",
        "datetime": "2017-08-31T10:28:00.143",
        "clientref": "e9f154c8-4011-493f-bb73-09cfcf8a1411",
        "submissiontime": "2017-08-31T10:26:14.317",
        "apispaceid": "c224cf6e-4352-4b26-a71a-c9032bea7a01",
        "statustimeutc": "2017-08-31T09:27:21.000Z"
      }
    },
    "metadata": {
      "data": "My correlation data"
    },
    "id": "e9f154c8-4011-493f-bb73-09cfcf8a1411"
  },
  "revision": 3,
  "etag": "\"2e-oazq69nFeiKkl5WM78YH7xo/8xU\"",
  "timestamp": "2017-08-31T09:28:03.074Z"
}
{
  "eventId": "77775ef0-60f9-4ec1-8fa5-cfc3990e1f94",
  "accountId": 1234,
  "apiSpaceId": "fb42e8af-5fc3-40d4-a38c-034535c0a385",
  "name": "message.delivered",
  "payload": {
    "details": {
      "channel": "fbMessenger",
      "channelStatus": {
        "sender": {
          "id": "1124625277645988"
        },
        "recipient": {
          "id": "138143536705220"
        },
        "timestamp": 1496248541996,
        "delivery": {
          "watermark": 1496248541613,
          "seq": 0
        }
      }
    },
    "updatedOn": "2017-05-31T16:35:41.613Z",
    "id": "0bc1e71e-28f4-40f4-853a-7112fd28f627"
  },
  "revision": 3,
  "etag": "\"2e-5L1Qw6Yi1fL1KMW7AH2amYIvNSI\"",
  "timestamp": "2017-05-31T16:35:43.078Z"
}

Read

Examples

{
  "eventId": "4fb53a11-48a0-46e6-af4c-946556f03445",
  "accountId": 1234,
  "apiSpaceId": "fb42e8af-5fc3-40d4-a38c-034535c0a385",
  "name": "message.read",
  "payload": {
    "details": {
      "channel": "fbMessenger",
      "channelStatus": {
        "sender": {
          "id": "1124625277645988"
        },
        "recipient": {
          "id": "138143536705220"
        },
        "timestamp": 1496248566710,
        "read": {
          "watermark": 1496248541613,
          "seq": 0
        }
      }
    },
    "updatedOn": "2017-05-31T16:35:41.613Z",
    "id": "0bc1e71e-28f4-40f4-853a-7112fd28f627"
  },
  "revision": 4,
  "etag": "\"2e-konny6DS4WEpVq5z2UDXb98kYQw\"",
  "timestamp": "2017-05-31T16:36:07.407Z"
}

Expired

Examples

{
  "eventId": "c88f35a8-5fe7-4069-9151-f4dcf8ee89b3",
  "accountId": 1234,
  "apiSpaceId": "fb42e8af-5fc3-40d4-a38c-034535c0a385",
  "name": "message.expired",
  "payload": {
    "details": {
      "channel": "sms",
      "channelStatus": {}
    },
    "updatedOn": "2017-06-01T10:45:05.685Z",
    "id": "a3c0c65c-6199-43e1-ba43-e7184a583e86"
  },
  "revision": 3,
  "etag": "\"2e-gTE0IKqsj+rNG0tojkpW/WZezTc\"",
  "timestamp": "2017-06-01T16:07:19.777Z"
}

Failed

Examples

{
  "eventId": "9f53e6b5-c060-49b2-9cac-65b49ee66dc7",
  "accountId": 1234,
  "apiSpaceId": "fb42e8af-5fc3-40d4-a38c-034535c0a385",
  "name": "message.failed",
  "payload": {
    "details": {
      "reason": "The message failed because it could not be sent to any of the configured channels.",
      "channelStatus": {
        "sms": {
          "status": "failed",
          "details": {
            "reason": "Dynmark SMS Platform returned a status of unsuccessful: {\"to\":\"447990766636\",\"successful\":false,\"clientRef\":\"1a18a255-43f2-492c-942a-b3d1443d3ddd\",\"validationFailures\":[{\"failureCode\":\"InsufficientCredit\",\"details\":\"The user has insufficient remaining credits. 1 required\"}]}"
          },
          "updatedOn": "2017-05-30T16:20:44.820Z"
        }
      }
    },
    "updatedOn": "2017-05-30T16:20:44.822Z",
    "id": "1a18a255-43f2-492c-942a-b3d1443d3ddd"
  },
  "revision": 2,
  "etag": "\"2e-86a/ddHPsy2gvKIhUjhClVmwdps\"",
  "timestamp": "2017-05-30T16:20:45.267Z"
}
{
  "eventId": "d8536261-ee1d-4b88-8f2e-89d9ea35dd1b",
  "accountId": 1234,
  "apiSpaceId": "c224cf6e-4352-4b26-a71a-c9032bea7a01",
  "name": "message.failed",
  "payload": {
    "details": {
      "reason": "The message failed because it could not be sent to any of the configured channels.",
      "channelStatus": {
        "fbMessenger": {
          "status": "failed",
          "details": {
            "reason": "connect ETIMEDOUT 31.13.71.1:443"
          },
          "updatedOn": "2017-08-31T11:13:38.238Z"
        }
      }
    },
    "updatedOn": "2017-08-31T11:13:38.239Z",
    "id": "2a680295-581f-41a7-8825-c6d488e87d07"
  },
  "revision": 2,
  "etag": "\"2e-X5EBTbPlzb3vKVB3mCNjthHMD2E\"",
  "timestamp": "2017-08-31T11:13:37.895Z"
}

Inbound

Examples

{
  "eventId": "0028ebc3-c0e9-4924-9b44-9135c7990f57",
  "accountId": 1234,
  "apiSpaceId": "c224cf6e-4352-4b26-a71a-c9032bea7a01",
  "name": "message.inbound",
  "payload": {
    "from": {
      "phoneNumber": "447234234234",
      "profileId": "sms_447234234234"
    },
    "channel": "sms",
    "to": {
      "phoneNumber": "447123123123"
    },
    "body": "Hello",
    "channelData": {
      "messageId": "2014753436",
      "keyword": "",
      "clientRef": "",
      "receivedOn": "2017-08-31T11:57:54.000Z"
    },
    "id": "4980e512-1a11-4ac3-ad9b-f8a23905432b",
    "receivedOn": "2017-08-31T11:57:55.735Z"
  },
  "revision": 0,
  "etag": "\"2e-egLNsnrQHYwEYFdCSTKKvFzExjM\"",
  "timestamp": "2017-08-31T11:57:55.738Z"
}
{
  "eventId": "020b8e91-a116-47c6-b7e9-dab878e5761a",
  "accountId": 1234,
  "apiSpaceId": "fb42e8af-5fc3-40d4-a38c-034535c0a385",
  "name": "message.inbound",
  "payload": {
    "from": {
      "fbMessengerId": "1924625277645988",
      "profileId": "joe.blogs@acme.com"
    },
    "channel": "fbMessenger",
    "body": "Hi there",
    "channelData": {
      "pageId": "118143536805220",
      "body": {
        "sender": {
          "id": "1924625277645988"
        },
        "recipient": {
          "id": "118143536805220"
        },
        "timestamp": 1496157103192,
        "message": {
          "mid": "mid.$cAAAnV7v5AVdiiy7sWFcWemjwmFUt",
          "seq": 611,
          "text": "Hi there"
        }
      }
    },
    "id": "8e14bc40-cf93-44bf-be7b-d5dfa77cb53e",
    "receivedOn": "2017-05-30T15:11:44.451Z"
  },
  "revision": 0,
  "etag": "\"2e-gLoREj4XI35uB44EZ2pYIeiIhls\"",
  "timestamp": "2017-05-30T15:11:45.169Z"
}
{
  "eventId": "bb7529b7-6f42-48ae-9c7c-f4787f0309c1",
  "accountId": 1234,
  "apiSpaceId": "fb42e8af-5fc3-40d4-a38c-034535c0a385",
  "name": "message.inbound",
  "payload": {
    "from": {
      "fbMessengerId": "1924625277645988",
      "profileId": "joe.blogs@acme.com"
    },
    "channel": "fbMessenger",
    "channelData": {
      "pageId": "118143536805220",
      "body": {
        "sender": {
          "id": "1924625277645988"
        },
        "recipient": {
          "id": "118143536805220"
        },
        "timestamp": 1496157323290,
        "message": {
          "mid": "mid.$cAAAnV7v5AVdiizJIGlcWez6SN4a6",
          "seq": 613,
          "attachments": [
            {
              "type": "image",
              "payload": {
                "url": "https://scontent.xx.fbcdn.net/v/t35.0-12/18836412_972877662815683_1343824608_o.png?_nc_ad=z-m&oh=bb64923a45848c937f15cbb415bf7942&oe=592F8F05"
              }
            }
          ]
        }
      }
    },
    "id": "2e779cb8-681d-440f-b48a-c0e6061cecc0",
    "receivedOn": "2017-05-30T15:15:32.120Z"
  },
  "revision": 0,
  "etag": "\"2e-JpSAWx+NJaPfrFzaDVTp863rgZ4\"",
  "timestamp": "2017-05-30T15:15:32.873Z"
}
Suggest Edits

One API - Profile Events

 

Create

{
    "eventId": "ef21e48a-44ab-45a5-b0df-d69efefd84f4",
    "accountId": 1234,
    "apiSpaceId": "fe11e8af-5fc3-40d4-a38c-034535c0a385",
    "name": "profile.create",
    "payload": {
        "id": "joe.blogs@acme.com",
        "fbMessengerId": "1924625477645988",
        "facebook": {
            "first_name": "Joe",
            "last_name": "Blogs",
            "profile_pic": "https://scontent.xx.fbcdn.net/v/t31.0-1/18491707_966210440149072_5125726667986939281_o.jpg?oh=1d2e9d127908108d583816b4a2d3029f&oe=59B76722",
            "locale": "en_GB",
            "timezone": 1,
            "gender": "male",
            "is_payment_enabled": true,
            "lastRefreshedOn": "2017-05-30T14:56:46.161Z"
        }
    },
    "revision": 0,
    "etag": "\"1c-FQbvdiKOAWc7ELR9RkHnLLjOFA0\"",
    "timestamp": "2017-05-30T14:56:47.293Z"
}

Update

{
    "eventId": "e625d046-80b9-430d-bfeb-fda6ba5194ed",
    "accountId": 1234,
    "apiSpaceId": "fb42e8af-5fc3-40d4-a38c-034535c0a385",
    "name": "profile.update",
    "payload": {
        "id": "joe.blogs@acme.com",
        "name": "Joe Blogs",
        "email": "joe.blogs@acme.com"
    },
    "revision": 1,
    "etag": "\"1a-lyBG7sifukcsukcmXL87nb41J0U\"",
    "timestamp": "2017-05-31T15:26:58.199Z"
}

Delete

{
    "eventId": "8e1503da-952b-4946-86c4-5c51218f39ed",
    "accountId": 1234,
    "apiSpaceId": "fb42e8af-5fc3-40d4-a38c-034535c0a385",
    "name": "profile.delete",
    "payload": {
        "date": "2017-05-31T15:31:13.311Z",
        "by": "access:91c59b6a-5ff9-4a97-84a7-1b1ca4a0fe82",
        "id": "joe.blogs@acme.com"
    },
    "revision": 2,
    "etag": "\"1a-XK1WYN45Lurpx25Rz5OFoDxSl1s\"",
    "timestamp": "2017-05-31T15:31:13.479Z"
}

Undelete

{
    "eventId": "99d27031-7802-4e3d-9980-322027f869f3",
    "accountId": 1234,
    "apiSpaceId": "fb42e8af-5fc3-40d4-a38c-034535c0a385",
    "name": "profile.undelete",
    "payload": {
        "id": "joe.blogs@acme.com"
    },
    "revision": 3,
    "etag": "\"1a-iSV8wt9PDMKB7YICuT/LmQtrSys\"",
    "timestamp": "2017-05-31T15:34:25.066Z"
}
Suggest Edits

Facebook Events

 

Opt In

{
    "eventId": "29d0141d-e2a2-4732-bcdc-c6b402813d76",
    "payload": {
        "fbMessengerId": "1924625277645988",
        "profileId": "joe.blogs@acme.com",
        "stateData": {
            "profileId": "joe.blogs@acme.com"
        },
        "ref": "cd0756b9177796787257ca5ca7409040296a23fac233e486c9df0a0ad4fa3f17c4204157729fe702fe31f9fd6de524c2cRp2jzdJ2yOoxOr6/KnOsnXKGsDmdpaka+k6jGfJsl/nsgDZIdOY54FAOA7nzlZWtC7/9KMMfZBA9atPllXkbvzeOtTchKRHNo+SjWeUopRU=",
        "rawData": {
            "recipient": {
                "id": "178143536105220"
            },
            "timestamp": 1496156204158,
            "sender": {
                "id": "1924625277645988"
            },
            "optin": {
                "ref": "cd0756b9177796787257ca5ca7409040296a23fac233e486c9df0a0ad4fa3f17c4204157729fe702fe31f9fd6de524c2cRp2jzdJ2yOoxOr6/KnOsnXKGsDmdpaka+k6jGfJsl/nsgDZIdOY54FAOA7nzlZWtC7/9KMMfZBA9atPllXkbvzeOtTchKRHNo+SjWeUopRU="
            }
        },
        "pageId": "123143536805220"
    },
    "accountId": 1234,
    "apiSpaceId": "f342e8af-5fc3-41d4-a38c-034535c0a385",
    "name": "facebook.optin",
    "timestamp": "2017-05-30T14:56:44.600Z"
}
Suggest Edits

App Messaging - Conversation Events

 

Create

{
    "eventId": "84a9ba5b-5833-49eb-8289-8856f28501fc",
    "accountId": 1234,
    "apiSpaceId": "fb42e8af-5fc3-40d4-a38c-034535c0a385",
    "name": "conversation.create",
    "payload": {
        "id": "acme_conv_1",
        "name": "Support request",
        "description": "My first support request",
        "roles": {
            "owner": {
                "canSend": true,
                "canAddParticipants": true,
                "canRemoveParticipants": true
            },
            "participant": {
                "canSend": true,
                "canAddParticipants": true,
                "canRemoveParticipants": true
            }
        },
        "isPublic": false,
        "participants": [
            {
                "profileId": "joe.blogs@acme.com",
                "role": "owner"
            },
            {
                "profileId": "Support",
                "role": "participant"
            }
        ]
    },
    "revision": 0,
    "etag": "\"1a-cOQdwU9OQDXnvOqWHfjQ72Izt78\"",
    "timestamp": "2017-05-30T16:32:27.074Z"
}

Delete

{
    "eventId": "23bbf631-e682-4737-8819-b3160aa1dc92",
    "accountId": 1234,
    "apiSpaceId": "fb42e8af-5fc3-40d4-a38c-034535c0a385",
    "name": "conversation.delete",
    "payload": {
        "date": "2017-06-01T10:50:49.150Z",
        "by": "access:91c59b6a-5ff9-4a97-84a7-1b1ca4a0fe82",
        "id": "acme_conv_1"
    },
    "revision": 1,
    "etag": "\"1a-+9k1CMDpFHoLo2uBF5HqIq0ZLhw\"",
    "timestamp": "2017-06-01T10:50:49.154Z"
}

Participant Added

{
    "eventId": "bf99d289-0ba9-4da6-b2b1-c342fa4a8619",
    "payload": {
        "profileId": "joe.blogs@acme.com",
        "role": "owner",
        "id": "acme_conv_1"
    },
    "accountId": 1234,
    "apiSpaceId": "fb42e8af-5fc3-40d4-a38c-034535c0a385",
    "name": "conversation.participantAdded",
    "timestamp": "2017-05-30T16:32:27.031Z"
}

Participant Deleted

Participant Updated

Undelete

{
    "eventId": "19679cb9-0a2d-4bbd-a3b5-56c1f3243b81",
    "accountId": 1234,
    "apiSpaceId": "fb42e8af-5fc3-40d4-a38c-034535c0a385",
    "name": "conversation.undelete",
    "payload": {
        "id": "acme_conv_1",
        "name": "Support request",
        "description": "My first support request",
        "roles": {
            "owner": {
                "canSend": true,
                "canAddParticipants": true,
                "canRemoveParticipants": true
            },
            "participant": {
                "canSend": true,
                "canAddParticipants": true,
                "canRemoveParticipants": true
            }
        },
        "isPublic": false,
        "participants": [
            {
                "profileId": "joe.blogs@acme.com",
                "role": "owner"
            },
            {
                "profileId": "Support",
                "role": "participant"
            }
        ]
    },
    "revision": 2,
    "etag": "\"1a-N0pT35pVlNSVjeOrrTS7Y5ZfBMs\"",
    "timestamp": "2017-06-01T10:51:31.628Z"
}

Update

Suggest Edits

App Messaging - Message Events

 

Delivered

{
    "eventId": "c34a5952-9a74-4b91-91df-6a63f64c7875",
    "accountId": 1234,
    "apiSpaceId": "fb42e8af-5fc3-40d4-a38c-034535c0a385",
    "name": "conversationMessage.delivered",
    "payload": {
        "conversationId": "e215cfce-4e4c-41ad-a396-1253e4df76f7",
        "scope": "a2p",
        "direction": "inbound",
        "isPublicConversation": false,
        "profileId": "dave",
        "timestamp": "2017-05-31T16:06:21.566Z",
        "id": "8a05572f-a102-4b0c-9944-ec30caef1d6f",
        "conversationEventId": 5
    },
    "revision": 5,
    "timestamp": "2017-05-31T16:06:22.003Z"
}

Read

{
    "eventId": "5f214933-3add-4f55-84e3-803d8772360e",
    "accountId": 1234,
    "apiSpaceId": "fb42e8af-5fc3-40d4-a38c-034535c0a385",
    "name": "conversationMessage.read",
    "payload": {
        "conversationId": "e215cfce-4e4c-41ad-a396-1253e4df76f7",
        "scope": "a2p",
        "direction": "inbound",
        "isPublicConversation": false,
        "profileId": "dave",
        "timestamp": "2017-06-01T10:04:47.006Z",
        "id": "f4cc0e85-c47c-410c-993c-d73032cb04fc",
        "conversationEventId": 31
    },
    "revision": 31,
    "timestamp": "2017-06-01T10:04:47.434Z"
}

Sent

{
    "eventId": "65a891c0-01ff-41de-bc21-6313a3c6abd4",
    "accountId": 1234,
    "apiSpaceId": "fb42e8af-5fc3-40d4-a38c-034535c0a385",
    "name": "conversationMessage.sent",
    "payload": {
        "metadata": {},
        "context": {
            "from": {
                "id": "dave"
            },
            "conversationId": "e215cfce-4e4c-41ad-a396-1253e4df76f7",
            "sentBy": "session:dave",
            "sentOn": "2017-05-31T16:30:35.206Z",
            "scope": "a2p",
            "direction": "inbound"
        },
        "parts": [
            {
                "data": "dhdhdhdjdsjs",
                "size": 12,
                "type": "text/plain"
            }
        ],
        "id": "61320b34-0ca2-4a9f-8338-de33407450ba",
        "conversationEventId": 6
    },
    "revision": 6,
    "timestamp": "2017-05-31T16:30:35.208Z"
}
Suggest Edits

App Messaging - Session Events

 

Started

{
    "eventId": "fca90836-a688-43d2-8532-58d5e3e8cad2",
    "accountId": 1234,
    "apiSpaceId": "fb42e8af-5fc3-40d4-a38c-034535c0a385",
    "name": "session.sessionStarted",
    "payload": {
        "authenticationId": "f90fb6de-af55-4ecd-8005-04f17e82deba",
        "deviceId": "909b21ec-3214-4aa5-b128-bb124eda7225",
        "platform": "javascript",
        "platformVersion": "58",
        "sdkType": "native",
        "sdkVersion": "1.0.1.2",
        "sourceIp": "10.255.0.3",
        "startedOn": "2017-05-31T16:06:19.589Z",
        "expiresOn": "2017-05-31T16:16:19.589Z",
        "profileId": "joe.blogs@acme.com",
        "id": "f90fb6de-af55-4ecd-8005-04f17e82deba"
    },
    "revision": 1,
    "etag": "\"2e-/5mDxQJgm8S7AWKMI4itMec7Hdw\"",
    "timestamp": "2017-05-31T16:06:19.638Z"
}
Suggest Edits

App Messaging Overview

 

What is App Messaging

App Messaging allows you to build secure messaging and chat services into your native and web applications. Use App Messaging for P2P chat, B2C chat or A2P messaging for alerts reminders and notifications. Create user profiles, start conversations and send any type of message. We have done the heavy lifting for you, so you don’t carry the cost of development or platform support.

Profiles

Comapi automatically creates a profile for each individual user using any unique identifier you want, allowing you to pass profile and user information/data to the platform. Profile information can then be used by other Comapi services to segment and target users. You can also add, update and get the profile's information using the our APIs.

Start Conversations

Conversations are like subscriptions to a channel. You can start a conversation with one user, groups of users or any defined target. You can then use permissions to set access rights and manage conversations. Conversations are stored on our platform, enabling synchronisation across any of a customer's devices. This provides a continuous customer experience within your application. You have total control over how you want your conversations to be managed.

Send Messages

Send any type of message. Comapi supports all usual formats (text, image, video, html). Messages can also be any other MIME type, data payload or native operating system services such as location. Send multi-part messages to make rendering of rich content easy, and easily automate processes by sending data along with the message.

 

The following sections will take you through using the Comapi Android SDK

There is also a repository containing some sample apps here ...

Suggest Edits

Installing the SDK

 
compile 'com.comapi:foundation:1.0.2'
<dependency>
    <groupId>com.comapi</groupId>
    <artifactId>foundation</artifactId>
    <version>1.0.2</version>
</dependency>

Then

  • initialise SDK with API Space id and authentication token provider
  • start session when you obtain token from your authentication provider
  • listen for realtime events, and push messages
  • call message and profile services
Suggest Edits

Initialise

 

To initialise the SDK, you will need a few pre-requisites. A configured API Space and an authentication provider that can generate a JWT that matches the auth scheme configured for your API Space. Set both of them when building configuration object. This is the only required configuration setup. To use the SDK you will also need to provide couple event listeners.

ComapiConfig config = new ComapiConfig()
   .apiSpaceId("<API_SPACE_ID>")
   .authenticator(new ChallengeHandler());

e.g. of the authentication challenge handler (needs to extend ComapiAuthenticator class)

public class ChallengeHandler extends ComapiAuthenticator {
    @Override
    public void onAuthenticationChallenge(AuthClient authClient, 
       ChallengeOptions options) {
          authClient.authenticateWithToken(/*get token using options.getNonce()*/));
    }
}

onCreate in Application

The initialisation needs to be performed in onCreate method of Android Application class.

You can use APIs in two versions reactive and with callbacks. Callback version initialise through Comapi class and the access to APIs is through ComapiClient. For reactive java alternative use RxComapi and RxComapiClient classes.

public class MyApplication extends Application {

    @Override
    public void onCreate()
    {
        super.onCreate();
      
        Comapi.initialiseShared(this, config, new Callback<ComapiClient>() {
            @Override
            public void success(ComapiClient client) {
               //Use client instance to communicate with services
            }

            @Override
            public void error(Throwable t) {
               //Error
            }
        });
    }
}
public class MyApplication extends Application {

    @Override
    public void onCreate()
    {
        super.onCreate();
      
        RxComapi.initialiseShared(this, config)
           .subscribeOn(Schedulers.io())
           .observeOn(AndroidSchedulers.mainThread())
           .subscribe(new Action1<RxComapiClient>() {
                    @Override
                    public void call(RxComapiClient client) {
                       //Use client instance to communicate with services
                    }
                }, new Action1<Throwable>() {
                    @Override
                    public void call(Throwable t) {
                        //Error
                    }
                });
    }
}

Both of the above calls will create singleton client instances that you can obtain through

Comapi.getShared(); 
// OR
RxComapi.getShared();

If you don't want SDK to keep the reference to the client instance use

Comapi.initialise(...);
// OR
RxComapi.initialise(...);

instead and store the client yourself.

Add listeners

When your app is in the foreground it keeps socket connection open and listens for live update of your profile and conversations for which you are a participant. Register for the incoming events with the ComapiConfig object and pass it to registration method.

ComapiConfig config = new ComapiConfig()
   .apiSpaceId("<API_SPACE_ID>") //required
   .authenticator(new ChallengeHandler()) //required
   .pushMessageListener(/* extends PushMessageListener */)
   .messagingListener(/* extends MessagingListener */)
   .profileListener(/* extends ProfileListener */)
   .stateListener(/* extends StateListener */);

Advanced options

Logging

You can set the logging level separately for internal file and console output and network calls to OFF, FATAL, ERROR, WARNING, INFO, DEBUG. By default they are set to warning.

ComapiConfig config = new ComapiConfig().logConfig(
new LogConfig()
   .setFileLevel(LogLevel.DEBUG)
   .setConsoleLevel(LogLevel.DEBUG)
   .setNetworkLevel(LogLevel.DEBUG));

You can also set custom limit for the size of internal log files

new ComapiConfig().logSizeLimitKilobytes(limit);

Proxy

If your test app connects through a proxy provide the Uri like this

new ComapiConfig().apiConfiguration(new APIConfig()
       .proxy("http://xx.xxx.xx.xxx:xxxx"));
Suggest Edits

Start session

 

Initialisation through Comapi (RxComapi) class gives you access to client object used for any further communication with the SDK. See initialisation for details.

In order to communicate with Comapi services you need to start session in SDK. This is needed only once per user log in, after that SDK will be re-authenticating session until you end session.

Start

To create or log in user to Comapi services use

client.service().session().startSession(new Callback<Session>(){/* implement */});
rxClient.service().session().startSession()
   .subscribe(new Action1<Session>(){/* implement */}, 
      new Action1<Throwable>(){/* implement */});

This will ask ComapiAuthenticator (provided when initialising) for a JWT token then SDK will create session server side for profile id obtained from the token. Any subsequent call to services will use same authentication details.

Stop

To close currently opened session use

client.service().session().endSession(
   new Callback<ComapiResult<Void>>(){/* implement */});
rxClient.service().session().endSession()
   .subscribe(new Action1<ComapiResult<Void>>(){/* implement */},
      new Action1<Throwable>(){/* implement */});
Suggest Edits

Client APIs

 

You can use Comapi client instance obtained in Initialisation to access SDK APIs.

session

client.getSession().getProfileId();
client.getSession().isSuccessfullyCreated();

services

// Messaging related service calls
client.service().messaging();

// User profile related service calls
client.service().profile();

APIs

Depending on the client you initialised ComapiClient or RxComapiClient you will have access to callbacks or reactive APIs. Reactive version returns Observables you need to subscribe to. For callback version pass callback as the last parameter and the request will be performed in the background delivering result in UI thread.

All service calls will return ComapiResult<T> objects that give access to

// True if {@link #getCode()} ()} is in the range [200..300).
result.isSuccessful()

// Response data
result.getResult()

// HTTP status message
result.getMessage()

// service call error details
result.getErrorBody()

// HTTP status code.
result.getCode()

// ETag describing version of the data.
result.getETag()

Logs

client.getLogs().subscribe(new Action1<String>() {
                    @Override
                    public void call(String logs) {
                       //Internal logs
                    }
                }, new Action1<Throwable>() {
                    @Override
                    public void call(Throwable t) {
                        //Error
                    }
                });

SDK state

client.getState();

You can find the list of codes in GlobalState class.

Suggest Edits

Listen to events

 

Realtime events are delivered to the SDK via a websocket. These events can be subscribed to via the following methods

ComapiConfig config = new ComapiConfig()
   .messagingListener(listener /* extends MessagingListener */)
   .profileListener(listener /* extends ProfileListener */);
client.addListener(listener /* extends MessagingListener */);
client.addListener(listener /* extends ProfileListener */);
client.removeListener(listener /* extends MessagingListener */);
client.removeListener(listener /* extends ProfileListener */);

MessagingListener and ProfileListener are abstract with empty implementation of onEventName methods. Override some or all of them to receive socket events from foundation SDK. Real time events are available only when the application is in foreground (visible to the user). If app is put to the background use FCM to communicate messages to the user. Update the internal state after app is put back to foreground using services APIs.

Available events

Event Name
Description

ProfileUpdateEvent

Sent when a user's profile is updated.

MessageSentEvent

Sent when a new message appeared in a conversation. This event will also be delivered to the message sender.

MessageDeliveredEvent

Sent when one of participants updated the message status to 'delivered'.

MessageReadEvent

Sent when one of participants updated the message status to 'read'.

ParticipantAddedEvent

Sent when a participant is added to a conversation. When a conversation is created, this event will also fire with the owner's profileId.

ParticipantUpdatedEvent

Sent when a participant role is updated in a conversation.

ParticipantRemovedEvent

Sent when a participant is removed from a conversation.

ConversationDeleteEvent

Sent when a conversation is deleted.

ConversationUpdateEvent

Sent when a conversation details were updated.

ConversationUndeleteEvent

Sent when a conversation is restored.

ParticipantTypingEvent (from v1.0.2)

Sent when 'isTyping' method has been called informing conversation participants that the user started typing a new message

ParticipantTypingOffEvent (from v1.0.2)

Sent when 'isTyping' method has been called informing conversation participants that the user stopped typing a new message

// Event unique identifier.
getEventId()

// Event name/type.
getName()

In addition specific event type

// Profile unique identifier.
getProfileId();

//Time when the update event was published.
getPublishedOn();

//Revision of the profile details on the server.
getRevision();

//Gets profileId of an user that performed this update.
getCreatedBy();

//Gets profile update details.
getPayload();
// Message unique identifier
getMessageId();

// Unique, monotonically increasing number of event for this conversation
getConversationEventId()

// Custom message metadata (sent when client sends a message)
getMetadata();

// Message sender
getFromWhom();

// Message sender defined internally on server (shouldn't be visible inside the app)
getSentBy();

// When the message was sent
getSentOn();

// Conversation unique identifier
getConversationId();

// Parts of the message with data, type, name and size 
getParts();

// Alert definitions with FCM and APNS push platforms
getAlert();
// Message unique identifier
getMessageId();

// Conversation unique identifier
getConversationId();

// Profile unique identifier
getProfileId();

// When the message was marked as delivered
getTimestamp();

// Unique, monotonically increasing number of event for this conversation
getConversationEventId();
// Same as MessageDeliveredEvent
// Conversation unique identifier
getConversationId();

// Profile unique identifier
getProfileId();

// Role definition for this participant in this conversation
getRole();
// Same as ParticipantAddedEvent
// Same as ParticipantAddedEvent
// Conversation unique identifier
getConversationId() 

// Conversation name
getConversationName();

// Conversation decription
getDescription();

// Role definitions for 'owner' and 'participant'
getRoles();
// Conversation unique identifier
getConversationId();

// When the conversation was deleted
getDeletedOn();
// Conversation unique identifier
getConversationId();

// Conversation details, same as ConversationUpdateEvent
getConversation();
// Conversation unique identifier
getConversationId();

// Get profile id of an participant who started typing a new message
getProfileId();
// Conversation unique identifier
getConversationId();

// Get profile id of an participant who stopped typing a new message
getProfileId();
Suggest Edits

Push messages

 

Comapi SDK is automatically registering you to receive push messages through FCM.

There are few places where the messages can be obtained: PushMessageListener registered when initialising SDK, system tray, Intent used to open Launcher Activity.

App state
Notification
Data
Both

Foreground

PushMessageListener

PushMessageListener

PushMessageListener

Background

System tray

PushMessageListener

Notification: system tray; Data: in extras of the intent.

PushMessageListeners simply redirects messages from internally registered FirebaseMessagingService (see Firebase)

Suggest Edits

Messaging service

 
service = client.service().messaging();

Depending on the client you initialised ComapiClient or RxComapiClient you will have access to callbacks or reactive profile APIs. Reactive version returns Observables you need to subscribe to. For callback version pass callback as the last parameter and the request will be performed in the background delivering result in UI thread.

data consistency

In order to manage concurrent updates of a conversation from many devices/websites use ETag. When obtaining conversation details from Comapi services you can find an ETag in ComapiResult object. It describes what version of server data you got. When you want to update this data pass the same ETag with the new details. If the server data will change before your next update the services will not allow such modification until you obtain the recent version of the data and pass correct ETag with the next update. This way you can keep consistency of the data across many devices.

messaging service API

create conversation

ConversationCreate conversation = ConversationCreate.builder()
   // Unique conversation identifier.
   .setId("1234")
   // Description
   .setDescription("This is my first conversation")
   // Name 
   .setName("Awesome chat")
   // Is this conversation visible to users not being participants.
   .setPublic(false)
   /* Sets what permissions 'owner' and 'participant' will have in this conversation
      You can set if they will can: add new participants, remove participants, send messages.
      By default 'owner' and 'participant' can send messages, can add participants, cannot remove participants.
      Here we added CanRemoveParticipants permission for the 'owner'. */
   .setRoles(new Roles(Role.builder().setCanRemoveParticipants().build(), new Role()))
   .build();

Then pass this object to Comapi services

service.createConversation(conversation,
   new Callback<ComapiResult<ConversationDetails>>(){/* implement */});
rxService.createConversation(conversation)
   .subscribe(new Action1<ComapiResult<ConversationDetails>>(){/* implement */},
      new Action1<Throwable>(){/* implement */});

update conversation

ConversationUpdate update = ConversationUpdate.builder()
                .setDescription("New description")
                .setName("Different name")
                .build();

Then pass this object to Comapi services

service.updateConversation(conversationId, update, eTag,
   new Callback<ComapiResult<ConversationDetails>>(){/* implement */});
rxService.updateConversation(conversationId, update, eTag)
   .subscribe(new Action1<ComapiResult<ConversationDetails>>(){/* implement */},
      new Action1<Throwable>(){/* implement */});

delete conversation

service.deleteConversation(conversationId, eTag,
   new Callback<ComapiResult<Void>>(){/* implement */});
rxService.deleteConversation(conversationId, eTag)
   .subscribe(new Action1<ComapiResult<Void>>(){/* implement */},
      new Action1<Throwable>(){/* implement */});

get convesartion

To obtain conversation details call

service.getConversation(conversationId,
  new Callback<ComapiResult<ConversationDetails>>(){/* implement */});
rxService.getConversation(conversationId)
   .subscribe(new Action1<ComapiResult<ConversationDetails>>(){/* implement */},
      new Action1<Throwable>(){/* implement */});

query conversations

You can also obtain the list of all conversation with a Scope.PUBLIC or Scope.PARTICIPANT:

service.getConversations(scope,
   new Callback<ComapiResult<List<ConversationDetails>>>(){/* implement */});
rxService.getConversations(scope)
   .subscribe(new Action1<ComapiResult<List<ConversationDetails>>>(){/* implement */},
      new Action1<Throwable>(){/* implement */});

remove conversation participants

Provide list of profile ids to be removed from conversation.

service.removeParticipants(conversationId, ids,
   new Callback<ComapiResult<Void>>(){/* implement */});
rxService.removeParticipants(conversationId, ids)
   .subscribe(new Action1<ComapiResult<Void>>(){/* implement */},
      new Action1<Throwable>(){/* implement */});

query conversation participants

Query all participants for particular conversation

 service.getParticipants(conversationId, 
   new Callback<ComapiResult<List<Participant>>>(){/* implement */});
rxService.getParticipants(conversationId)
   .subscribe(new Action1<ComapiResult<List<Participant>>>(){/* implement */},
      new Action1<Throwable>(){/* implement */});

add participants to conversation

Add new participants to conversation

service.addParticipants(conversationId, participants,
   new Callback<ComapiResult<Void>>(){/* implement */});
rxService.addParticipants(conversationId, participants)
   .subscribe(new Action1<ComapiResult<Void>>(){/* implement */},
      new Action1<Throwable>(){/* implement */});

send message in conversation

service.sendMessage(conversationId, body,
   new Callback<ComapiResult<MessageSentResponse>>(){/* implement */});
rxService.sendMessage(conversationId, body)
   .subscribe(new Action1<ComapiResult<MessageSentResponse>>(){/* implement */},
      new Action1<Throwable>(){/* implement */});

You can update the message status later using conversationId from ComapiResult result.

The more advanced way of constructing a new message is through

Map<String, Object> data = new HashMap<>();
data.put("key","value");

Map<String, Object> fcm = new HashMap<>();
fcm.put("data", data);
fcm.put("notification", "{ \"title\":\"Message\", \"body\":\"Hi!\" } ");

Map<String, Object> apns = new HashMap<>();
apns.put("alert", "Hi!");

Part part = Part.builder()
               .setData("Hi")
               .setName("body")
               .setSize("Hi".length())
               .setType("text/plain")
               .build();

MessageToSend message = MessageToSend.builder()
   .setAlert(fcm, apns)
   .setMetadata(data)
   .addPart(part)
   .build();

In this way you can set details of FCM/APNS notifications to be sent to participants devices, some custom metadata, add more message parts with e.g. Base64 encoded images etc.

Then call

service.sendMessage(conversationId, message, 
   new Callback<ComapiResult<MessageSentResponse>>(){/* implement */});
rxService.sendMessage(conversationId, message)
   .subscribe(new Action1<ComapiResult<MessageSentResponse>>(){/* implement */}, 
      new Action1<Throwable>(){/* implement */});

You can update the message status later using conversationId from ComapiResult result.

update message status

MessageStatusUpdate update = MessageStatusUpdate.builder()
   .addMessageId("id")
   .setStatus(MessageStatus.delivered)
   .build();

List<MessageStatusUpdate> updates = new ArrayList<>();
updates.add();

Then pass the list with 'delivered' and 'read' statuses to the service

service.updateMessageStatus(conversationId, updates,
   new Callback<ComapiResult<Void>>(){/* implement */});
rxService.updateMessageStatus(conversationId, updates)
   .subscribe(new Action1<ComapiResult<Void>>(){/* implement */},
      new Action1<Throwable>(){/* implement */});

query events

Every action in a conversation like sending a message, updating a message adding and removing participants etc. can be obtained providing conversation event id to start from (and going forward) and an upper limit of events you are interested in.

 service.queryEvents(conversationId, from, limit,
   new Callback<ComapiResult<EventsQueryResponse>>(){/* implement */});

rxService.queryEvents(conversationId, from, limit)
   .subscribe(new Action1<ComapiResult<EventsQueryResponse>>(){/* implement */},
      new Action1<Throwable>(){/* implement */});

In response you will get

// Message events
getMessageSent();

// Message delivered to one of the participants events
getMessageDelivered();

// Message read by one of the participants events
getMessageRead();

// Conversation details updated events
getConversationUpdate();

// Conversation deleted events
getConversationDelete();

// Conversation restored events
getConversationUnDelete();

// Participant added to conversation events
getParticipantAdded();

// Participant roles in conversation updated events
getParticipantUpdate();

// Participant removed from conversation events
getParticipantRemoved();

//Total number of events in EventsQueryResponse
getCombinedSize();

See socket events for details.

query messages

List of messages in a conversation can be obtained providing conversation event id to start from (and going backwards) and an upper limit of messages you are interested in. This interface allows you to get messages from the end of the conversation one page at a time. This will allow you to implement the classic pull down interface to load more messages.

service.queryMessages(conversationId, from, limit, 
   new Callback<ComapiResult<MessagesQueryResponse>>(){/* implement */});
rxService.queryMessages(conversationId, from, limit)
   .subscribe(new Action1<ComapiResult<MessagesQueryResponse>>(){/* implement */},
      new Action1<Throwable>(){/* implement */});

The result is a list of messages and orphaned events associated with them. The orphaned events are the events updating messages for conversation event id higher then 'from' - so for messages obtained e.g. in previous 'paging calls'. This events need to be applied to this newer messages.

// Messages conforming to the query.
getMessages();

/* Latest event id in this conversation that 
was taken into account constructing the query result. */
getLatestEventId();

/* Earliest event id in this conversation that 
was taken into account constructing the query result. */
getEarliestEventId();

// Events updating messages for conversation event id later/higher then 'from'.
getOrphanedEvents();

Getter messagesQueryResponse.getMessages() returns list of MessageReceived:

// Message unique identifier.
getMessageId();

/* Unique, monotonically increasing number of event 
of this message and conversation. */
getSentEventId();

// Message sender.
getFromWhom();

/* Message sender defined internally on server 
(shouldn't be visible inside the app). */
getSentBy();

// When the message was sent.
getSentOn();

// Conversation unique identifier
getConversationId();

// Key is the profile identifier the value is either 'delivered' or 'read'
getStatusUpdate();

Getter messagesQueryResponse.getOrphanedEvents() returns list of OrphanedEvent:

// Conversation event id.
getConversationEventId()

// Event name.
getName();

// Unique event identifier.
getEventId()

// Id of the updated message.
getMessageId();

// Id of the conversation for which message was updated.
getConversationId();

// Profile id of the user that changed the message status.
getProfileId();

// Gets time when the message status changed.
getTimestamp();

// True if this event is of type message delivered.
isEventTypeDelivered()

// True if this event is of type message read.
isEventTypeRead();

send user is typing event

(available from v1.0.2)

To send an information to conversation participants that the user started or finished typing a new message call

service.isTyping(conversationId, isTyping, 
   new Callback<ComapiResult<Void>>(){/* implement */});
rxService.isTyping(conversationId, isTyping)
   .subscribe(new Action1<ComapiResult<Void>>(){/* implement */},
              new Action1<Throwable>(){/* implement */});
Suggest Edits

Profile service

 
service = client.service().profile();

Depending on the client you initialised ComapiClient or RxComapiClient you will have access to callbacks or reactive profile APIs. Reactive version returns Observables you need to subscribe to. For callback version pass callback as the last parameter and the request will be performed in the background delivering result in UI thread.

data consistency

In order to manage concurrent updates of an profile from many devices/websites use ETag. When obtaining profile details from COMAPI services you can find an ETag in ComapiResult object. It describes what version of server data you got. When you want to update this data pass the same ETag with the new profile details. If the server data will change before your next update the services will not allow such modification until you obtain the recent version of the data and pass correct ETag with the next update. This way you can keep consistency of the data across many devices.

profile details

Get profile details of an user registered in the same API Space as the SDK

service.getProfile(profileId,
   new Callback<ComapiResult<Map<String, Object>>>(){/* implement */});
rxService.getProfile(profileId)
   .subscribe(new Action1<ComapiResult<Map<String, Object>>>(){/* implement */},
      new Action1<Throwable>(){/* implement */});

Result contains containing a string-object map of properties associated with a given user profile.

query profiles

Query profiles registered on API Space

service.queryProfiles(queryString,
   new Callback<ComapiResult<List<Map<String, Object>>>>(){/* implement */});
rxService.queryProfiles(queryString)
   .subscribe(new Action1<ComapiResult<List<Map<String, Object>>>>(){/* implement */},
      new Action1<Throwable>(){/* implement */});

In result you will get a string-object map with a list of profile properties, each associated with a user profile conforming to provided query.

See query syntax. You can use QueryBuilder helper class to construct valid query string.

e.g.

String queryString = new QueryBuilder().addExists("email").build();

and queryProfiles will look for all profiles containing a field named 'email'.

update profile

Update your user profile providing custom map of values that should be associated with it.

service.updateProfile(profileDetailsMap, eTag,
   new Callback<ComapiResult<List<Map<String, Object>>>>(){/* implement */});
rxService.updateProfile(profileDetailsMap, eTag)
   .subscribe(new Action1<ComapiResult<List<Map<String, Object>>>>(){/* implement */},
      new Action1<Throwable>(){/* implement */});

result contains updated details.

The following sections will take you through using the Comapi iOS SDK

There is also a repository containing some sample apps here ...

Suggest Edits

Installing the SDK

 

To integrate Comapi Foundation into your Xcode project using CocoaPods, specify it in your Podfile:

use_frameworks!

pod 'ComapiFoundation'

Then, run the following command:

$ pod install

After that just insert in the Swift file:

import ComapiFoundation

and you are good to go.

Suggest Edits

Initialise

 

To initialise the Comapi SDK, you will need a few pre-requisites listed below:

  • A configured API Space
  • An authentication provider that can generate a JWT that matches the auth scheme configured for your Api Space.
  • The generated JWT must include the provided nonce as a claim in the generated JWT
import ComapiFoundation

let config = ComapiConfig(apiSpaceId: "<API_SPACE_ID>",
              						authenticationDelegate: ChallengeHandler())
let client = Comapi.initialise(config: config)
                          

Where <API_SPACE_ID> is your configured ApiSpaceID obtained from Comapi.

For convenience use of shared instance of ComapiClient, please use initialisation

Comapi.initialiseSharedInstance(config: config)

ChallengeHandler is just a class which implements following method:

class ChallengeHandler: AuthenticationDelegate {
    
    func client(_ client: ComapiClient,
                didReceiveAuthenticationChallenge authenticationChallenge: AuthenticationChallenge,
                completion continueWithToken: @escaping (String?) -> Void) {
/*
* 		Here goes your custom method to obtain a token with a nonce
* 	  call `continueWithToken` passing token as a parameter
*/
          getToken(authenticationChallenge.nonce) { token in
                  continueWithToken(token)
          }
		}
}

After initialisation you can call the singleton object from anywhere in your code:

Comapi.shared()

Add event listener

When your app is in the foreground it keeps socket connection open and listens for live update of your profile and conversations for which you are a participant. Register the delegate for the incoming events with the ComapiClient object. The delegate should conform to the EventListener protocol.

client.addEventDelegate(self)

To receive the event implement following method from EventListener protocol:

func client(client: ComapiClient, didReceiveEvent event: Event) {
        switch event {
          case .conversation: //handle `conversation` event
          case .conversationMessage: // handle `conversationMessage` event
          case .profile: // handle `profile` event
          default: return
        }
    }

Please note that you can register multiple listeners in your code, to handle events in different parts of your application. The events are broadcasted to all registered delegates.

To remove event delegate call:

client.removeEventDelegate(self)

passing the current subscribed delegate as a parameter.

Advanced options

You can set up the logging level for the SDK using the log configuration:

Log.consoleDestination.minimumLevel = .error
Log.fileDestination.minimumLevel = .info

Following log levels are defined in LogLevel swift file :

  • verbose
  • debug
  • info
  • warning
  • error
Suggest Edits

Client APIs

 

You can use Comapi client instance obtained in Initialisation to access SDK APIs.

Session

client.isSessionSuccesfullyCreated

Service

Accessing services is done by calling methods from the proper group:

client.services.profile
client.services.messaging
client.services.session

All service calls will return Result<T, Error: Swift.Error> Swift enumeration that gives access to response data. To properly handle it you can use following code in the completion block:

client.services.messaging.getMessages(forConversationID: conversation.id, limit: 0, from: nil) {
                (result) in

                switch result {
                	case .success(let value):
                   print(value.messages ?? "")

                	case .failure(let error):
                    print("error: ",error.localizedDescription)
                    /* handle error here */
                }
Suggest Edits

Listen to events

 

Add event listener

When your app is in the foreground it keeps socket connection open and listens for live update of your profile and conversations for which you are a participant. Register the delegate for the incoming events with the ComapiClient object. The delegate should conform to the EventListener protocol.

client.addEventDelegate(self)

To receive the event implement following method from EventListener protocol:

func client(client: ComapiClient, didReceiveEvent event: Event) {
        switch event {
          case .conversation: //handle `conversation` event
          case .conversationMessage: // handle `conversationMessage` event
          case .profile: // handle `profile` event
          default: return
        }
    }

Please note that you can register multiple listeners in your code, to handle events in different parts of your application. The events are broadcasted to all registered delegates.

client.removeEventDelegate(self)

Available events

Event type
Event subtype

profile

update

Sent when a user's profile is updated.

conversation

participantAdded

Sent when a participant is added to a conversation. When a conversation is created, this event will also fire with the owner's profileId.

conversation

participantUpdated

Sent when a participant role is updated in a conversation.

conversation

participantRemoved

Sent when a participant is removed from a conversation.

conversation

delete

Sent when a conversation is deleted.

conversation

update

Sent when a conversation details were updated.

conversation

undelete

Sent when a conversation is restored.

conversation

create

Sent when new conversation was created.

conversation

participantTyping

Sent when one of the participants is typing new message

conversation

participantTypingOff

Sent when the other participant stopped typing

conversationMessage

delivered

Sent when one of participants updated the message status to 'delivered'.

conversationMessage

read

Sent when one of participants updated the message status to 'read'.

conversationMessage

sent

Sent when a new message appeared in a conversation. This event will also be delivered to the message sender.

For all events you can access id, apiSpace and name properties.

For other events, there are other properties you can access, as follows:

profile.profileId

Conversation

Below are listed properties specific for the conversation event subtypes.

conversation.conversationId
conversation.payload.profileId
conversation.payload.role
conversation.conversationId
conversation.payload.profileId
conversation.payload.role
conversation.conversationId
conversation.payload.profileId
conversation.payload.role
conversation.conversationId
conversation.payload.date
conversation.profileId
conversation.payload.roles
conversation.payload.isPublic
conversation.payload.participants
conversation.conversationId
conversation.payload.description
conversation.payload.roles
conversation.profileId
conversation.payload.roles
conversation.payload.isPublic
conversation.payload.participants
conversation.account
conversation.payload.profileId
covenrsation.payload.conversationId
conversation.account
conversation.payload.profileId
covenrsation.payload.conversationId

ConversationMessage

conversationMessage.messageId
conversationMessage.conversationId
conversationMessage.profileId
conversationMessage.timestamp
conversationMessage.messageId
conversationMessage.conversationId
conversationMessage.profileId
conversationMessage.timestamp
conversationMessage.messageId
conversationMessage.metadata
conversationMessage.parts
conversationMessage.alert
Suggest Edits

Push messages

 

In order to use Comapi built-in support for the Push Notifications, you need to configure your app to support the PUSH notification, as you would normally do. To benefit from the Comapi Push Notifications you need to pass the deviceToken which your appliacation has registered with and call setPushToken method, e.g:

func application(_ application: UIApplication,
                     didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        if let tokenString = String(data: deviceToken, encoding: .utf8) {
            try? Comapi.shared().setPushToken(tokenString, completion: {_ in})
        
        }
                /* Your custom code here*/

    }
Suggest Edits

Start session

 

Start

Comapi starts new session when the authentication flow is finished. Providing proper ChallengeHandler and successfully initialising the ComapiClient starts new session automatically, no further customization is needed.

However, if you want to start session explicitly, you can use startSession method:

client.services.session.startSession(completion: { 
            //do any of the subsequent calls or any other action after initialisation
        }) { error in
            print(error.localizedDescription)
        }

Stop

You can end the session just by calling:

client.services.session.endSession { (result) in /* your completion code goes here */}
Suggest Edits

Messaging services

 

Messaging service API

Conversations

In order to properly create conversation, you need to set up correct attributes for the Owner and the participants. The whole procedure is listed in example below:

let ownerAttributes = RoleAttributes(canSend: true, canAddParticipants: true, canRemoveParticipants: true)
        let participantAttributes = RoleAttributes(canSend: true, canAddParticipants: false, canRemoveParticipants: false)

        let roles = Roles(owner: ownerAttributes , participant: participantAttributes )
        let participant = ConversationParticipant(id: "Avarage Joe", role: "owner")
        let newConversation = NewConversation(id:"{id}", name: "Awesome chat", conversationDescription: "My first conversation", roles: roles, isPublic: false, participants: [participant])

        self.client.services.messaging.createConversation(newConversation) { result in }

To query all conversation call the method:

client.services.messaging.getAllConversations { (result) in
      if let conversations = result.value {
      
      }
}

Or if you want just to query the specific one:

client.services.messaging.getConversation("{id}") { result in
            print("result", result)
        }

Updating and deleting is as easy as calling dedicated SDK methods:

let conversationUpdate = ConversationUpdate(id: "{id}", name: "Name", conversationDescription: "description", roles: roles, isPublic: true)

client.services.messaging.updateConversation("{id}", conversationUpdate) { result in
            print("result", result)
        }
client.services.messaging.deleteConversation("{id}") { result in
            print("result", result)
        }

Participants

client.services.messaging.getParticipants(forConversationId: "{id}", completion: { result in
        })
let participant = ConversationParticipant(id: "{id}", role: "participant")
        client.services.messaging.addParticipants(forConversationId: conversationId, participants: [participant], completion: { result in
        })
client.services.messaging.removeParticipants(forConversationId: "{id}", participants: [participant]], completion: { result in
            })

Messages

Querying messages is done with the method getMessages:

client.services.messaging.getMessages(forConversationID: "{id}", limit: 1, from: nil) { (result) in }

If you want to query messages from specific message id, set the id as from parameter.

To send a message you need to create SendableMessage struct and then call sendMessage method.

let text = "Hello!"
let metadata = MessageMetadata(myMessageID: UUID().uuidString)
let part = MessagePart(name: "",
                       type: "text/plain",
                        url: nil,
                       data: text,
                       size: text.utf8.count)
                           
let message = SendableMessage(metadata: metadata, parts: [part])

client.services.messaging.send(message: message, toConversationWithId: "{id}") { (result) in
      
    }
client.services.messaging.updateStatus(forMessagesWithIds: ["{ids_list}"], to: .read, inConversationWithId: "{id}", withTimestamp: Date()) { result in

        }

Events

client.services.messaging.queryEvents(forConversationID: "{id}", limit: 10) { result in

        }
Suggest Edits

Profile services

 
client.services.profile.getProfile("{profile_id}") { result in

}
client.services.profile.updateProfile("{profile_id}", attributes: attributes) { result in

}

You can query profiles using search criteria. Just specify the list of query elements and pass it as argument to queryProfile method

let query = [QueryBuilder.QueryElements( "name", .equal, "Han Solo")]

client.services.profile.queryProfiles(queryElements: query) { result in
}
client.services.profile.patchProfile("{id}", attributes: attributes) { result in
	
}

You can also use convenience methods to operate on current user profile:

client.services.profile.updateCurrentProfile(attributes: attributes) { _ in} 
client.services.profile.patchCurrentProfile(attributes: attributes) { _ in}
Suggest Edits

JavaScript

 

The following sections will take you through using the Comapi Javascript SDK

There is also a repository containing some sample apps here ...

Suggest Edits

Install the SDK

How to install the sdk into your app

 

Installation

Comapi SDK can be installed from either NPM or Bower depending on your intended usage.

If you are integration into a classical javascript project and you just want to include a script that exposes some global objects in your page, then use Bower.

If you are using a project that utilises ES6 modules i.e angular2, ionic2 etc., then use NPM.

NPM

Install SDK ...

npm install @comapi/sdk-js-foundation --save

Import into your code and access SDK methods ...


import { Foundation } from "@comapi/sdk-js-foundation"

class MyClass{
    public displayVersion(){
        console.log(`Comapi version: ${Foundation.version}`);
    }
}

Bower

Install package from bower ...

bower install comapi-sdk-js-foundation

Include the script somewhere ...

<script src="bower_components/comapi-sdk-js-foundation/dist/comapi-foundation.js"></script>

There is also a minified version comapi-foundation.min.js available.

For all subsequent classical snippets, I will assume that this script has been included

Access SDK methods ...

console.log("Comapi version: " + COMAPI.Foundation.version);

Use of ES6 Promises

ES6 Promises are extensively used within this SDK. Depending on what browsers you are targeting, you may need to include a poly-fill for this. Several of these are available online.

Suggest Edits

Initialise

 

SDK Initialisation

To initialise the SDK, you will need a few pre-requisites ...

1) A configured API space

2) An authentication provider that can generate a jwt that matches the auth scheme configured for your Api Space.

3) The generated JWT must include the provided nonce as a claim in the generated jwt

ES6

Here is a typescript sample using ES6 import syntax (available from the npm package)

// some app specific imports
import { AppSettings } from "../settings";
import { AuthService } from "./auth";

// Comapi class / interface imports
import { Foundation, ComapiConfig, IAuthChallengeOptions } from "@comapi/sdk-js-foundation"

export class ComapiService {

    public sdk: Foundation;

    private authChallenge(options: IAuthChallengeOptions, answerAuthenticationChallenge) {
        this._authService.getToken(options.nonce)
            .then((token) => {
                answerAuthenticationChallenge(token);
            });
    }

    constructor(private _authService: AuthService) { }

    /**
     * Public method to encapsulate up the initialisation of Comapi 
     */
    public initialise(): Promise<Foundation> {

        return new Promise((resolve, reject) => {

            if (this._authService.isAuthenticated()) {

                let comapiConfig = new ComapiConfig()
                    .withApiSpace(AppSettings.APP_SPACE_ID)
                    // Note the this pointer binding so I can access this._authService in the authChallenge calllback
                    .withAuthChallenge(this.authChallenge.bind(this));

                Foundation.initialise(comapiConfig)
                    .then((sdk) => {
                        this.sdk = sdk;
                        console.log("foundation interface created");
                        resolve(sdk);
                    })
                    .catch((error) => {
                        console.error("initialise failed", error);
                        reject(error);
                    });
            } else {
                reject("Not logged in");
            }
        });
    }
}

Classical

function authChallenge(options, answerAuthenticationChallenge) {

    authService.getToken(options.nonce)
        .then((token) => {
            answerAuthenticationChallenge(token);
        })
        .catch(error=>{
            answerAuthenticationChallenge(null);            
        });
}

var comapiConfig = new COMAPI.ComapiConfig()
    .withApiSpace(appConfig.apiSpaceId)
    .withAuthChallenge(authChallenge);

COMAPI.Foundation.initialise(comapiConfig)
    .then(function (sdk) {
        console.log("Foundation interface created", sdk);
    })
    .catch(function (error) {
        $log.error("paragonService: failed to initialise", error);
    });

Advanced options

The above examples initialised the SDK with minimal configuration. You can customise the sdk behaviour with the following optional settings

logRetentionTime

When the SDK uses indexedDB to persist logs, they are purged on SDK initialisation.
this value represents the number of hours to keep logs for - the default value is 24

logLevel

This parameter controls what level of logging to perform.

export enum LogLevels {
    None,
    Error,
    Warn,
    Debug
};

The default setting is to only log errors.

logPersistence

This parameter controls whether and where to persist log data.
The historic data is available via a call to Foundation.getLogs()

export enum LogPersistences {
    None,
    IndexedDB,
    LocalStorage
};

The default setting is to use local storage.
IndexedDB is more performant but may require a poly-fill.

Authentication

JWT

The Comapi Auth Challenge needs to generate and return a jwt via the answerAuthenticationChallenge method.

There are 4 pieces fo data that need to be specified in the Comapi portal for the ApiSpace auth settings

1) Issuer
2) Audience
3) Shared Secret
4) ID Claim

A cryptographic nonce is used as part of the authentication flow. This is passed into the authChallenge (options.nonce) and needs to be added as a claim in the generated jwt.

The below sample uses jsrsasign to dynamically create a client side jwt ...

function authChallenge (options, answerAuthenticationChallenge) {
    // Header
    var oHeader = { alg: 'HS256', typ: 'JWT' };
    // Payload
    var tNow = KJUR.jws.IntDate.get('now');
    var tEnd = KJUR.jws.IntDate.get('now + 1day');
    var oPayload = {
        sub: "john smith",
        nonce: options.nonce,
        iss: "https://my-issuer.com",
        aud: "https://my-audience.com",
        iat: tNow,
        exp: tEnd,
    };
    var sHeader = JSON.stringify(oHeader);
    var sPayload = JSON.stringify(oPayload);
    var sJWT = KJUR.jws.JWS.sign("HS256", sHeader, sPayload, "my shared secret");
    answerAuthenticationChallenge(sJWT);
}

This node express method uses the njwt package. and achieves the same as above but server - side

/**
 * @Params {string} req.body.username
 * @Params {string} req.body.password
 * @Params {string} req.body.nonce
 */
app.post('/authenticate', function (req, res, next) {

    // TODO: authenticate username & password ...

    var claims = {
        iss: "https://my-issuer.com",
        sub: req.body.username,
        nonce: req.body.nonce,
        aud: "https://my-audience.com"
    }

    var jwt = njwt.create(claims, "my shared secret");
    var token = jwt.compact();
    res.json({ jwt: token });
});

The following auth challenge could be used in conjunction with the above node endpoint ..

function authChallenge (options, answerAuthenticationChallenge) {

    $http.post("/authenticate", { 
            username: "johnSmith" 
            password: "Passw0rd!",
            nonce: options.nonce })
        .then(function (response) {
            answerAuthenticationChallenge(response.data.token);
        })
        .catch(function (error) {
            answerAuthenticationChallenge(null);
        });
}

Sessions

To call onto any of the Comapi services, a valid session is required. This is what the authChallenge is for. Whenever Comapi needs a session and it doesn't have a currently active one, it will run through the auth flow as part of session creation.

You can explicitly start a session or the SDK will create one on the fly the first time you call a method that requires one

To start a session manually as part of the initialisation flow, you can do the following ...

Foundation.initialise(comapiConfig)
    .then(sdk => {
        console.log("sdk initialised", sdk);        
        return sdk.startSession();
    })
    .then(sessionInfo => {
        console.log("session started", sessionInfo);
    })
    .catch(error => {
        console.error("Something went wrong", error);
    });
Suggest Edits

Create conversation

 

Creating a conversation

The first thing you probably want to do is create a conversation and add some participants.
I will assume you have an initialised SDK at this point.

To create a conversation, you should use the ConversationBuilder class in conjunction with the createConversation() method.

A unique ConversationId is required to create a conversation. This is up to the integrator to provide.
The ConversationBuilder interface will automatically create a guid for this when it is instantiated.
You can override this behaviour and specify your own id using the withId("myConversationId") method.

Note that the methods on this interface can be chained together. See the SDK docs for a complete list of options.

ES6

Here is an ES6 sample


import { ConversationBuilder } from "@comapi/sdk-js-foundation";

let conversation = new ConversationBuilder().withName("Support").withDescription("Support related chat").withUsers(["johnSmith", "joeBloggs"]);

sdk.services.appMessaging.createConversation(channelDetails)
    .then(conversationInfo => {
        console.log("conversation created", conversationInfo);
    })
    .catch(error => {
        fail("conversation creation failed: ", error);
    });

Classical

var conversation = new COMAPI.ConversationBuilder().withName("Support").withDescription("Support related chat").withUsers(["johnSmith", "joeBloggs"]);

sdk.services.appMessaging.createConversation(channelDetails)
    .then(function(conversationInfo) {
        console.log("conversation created", conversationInfo);
    })
    .catch(function(error) {
        fail("conversation creation failed: ", error);
    });

These samples are basically the same bar the import of ConversationBuilder which is available from the COMAPI global.

Adding participants to a conversation

Participants can be added when the conversation is created or on the fly at a later date. To add a participant, you need to specify 2 pieces of information :

1) profileId - the profileId of the user to add
2) role - the role of the participant ["member"|"owner"]

i.e.

    var participants = [
        {
            id: "johnSmith",
            role: "member",
        }, {
            id: "joeBloggs",
            role: "member",
        }
    ];

the addParticipantsToConversation() method takes a conversationId and an array of participant objects

return sdk.services.appMessaging.addParticipantsToConversation(conversationInfo.id, participants)
    .then(result => {
        console.log("addMembersToConversation() succeeded", result);
    })
    .catch(error => {
        console.error("addMembersToConversation() failed", error);
    });

Removing participants from a conversation

To delete a participant, you use the deleteParticipantsFromConversation() method. You can specify a list of users to remove.

sdk.services.appMessaging.deleteParticipantsFromConversation(conversationInfo.id, ["joeBloggs", "johnSmith"])
    .then(result => {
        console.log("deleteParticipantsFromConversation() succeeded", result);
    })
    .catch(error => {
        console.error("deleteParticipantsFromConversation() failed", error);
    });

Listing participants in a conversation

To query all the participants in a conversation, you use the getParticipantsInConversation method.
An array of participants is returned in the Promise result.

sdk.services.appMessaging.getParticipantsInConversation(conversationInfo.id)
    .then(result => {
        console.log("getParticipantsInConversation() succeeded", result);
    })
    .catch(error => {
        console.error("getParticipantsInConversation() failed", error);
    });

Deleting a conversation

To delete a conversation, you simply use the deleteConversation() method.

sdk.services.appMessaging.deleteConversation(conversationInfo.id)
    .then(result => {
        console.log("deleteConversation() succeeded", result);
    })
    .catch(error => {
        console.error("deleteConversation() failed", error);
    });

Conversation related events

All of these methods will generate events which can be handled in your app. There is a specific events section where I will cover this in more detail particularly in terms of event payloads. Events are especially useful when there is more than one user of your app, so all devices / users get notified of any conversation related changes.

Event Details
conversationDeleted The conversation has been deleted
conversationUndeleted The conversation has been un-deleted
conversationUpdated The conversation has been updated (name / description changed)
participantAdded A participant has been added to a conversation
participantRemoved A participant has been removed from a conversation
participantTyping A participant is typing in a conversation
Suggest Edits

Send message

 

Sending a message

Basic plain text message

The next thing that you are going to want to do is send a message to your newly created conversation.

You can use the MessageBuilder interface to build your message. This abstracts away the complexities of JSON formatting.

The simplest message that can be sent is plain text, so lets start with that ...

ES6

import { MessageBuilder } from "@comapi/sdk-js-foundation"

let message = new MessageBuilder().withText("Hello world");

sdk.services.appMessaging.sendMessageToConversation(channelDetails.id, message)
    .then(result => {
        console.log("sendMessageToConversation() succeeded", result);
    })
    .catch(error => {
        console.error("sendMessageToConversation() failed", result);
    });

Classic

var message = new COMAPI.MessageBuilder().withText("Hello world");

sdk.services.appMessaging.sendMessageToConversation(channelDetails.id, message)
    .then(function(result){
        console.log("sendMessageToConversation() succeeded", result);
    })
    .catch(function(error){
        console.error("sendMessageToConversation() failed", result);
    });

Message parts

A message in Comapi messaging consists of an array of message parts. In the above sample, there was a single part of type text/plain. You can have as many parts as you like and each part can be of the format of your choosing. The underlying structure of a message part is as follows (all the properties are optional - you can use as you see fit):

export interface IMessagePart {
    /**
     * The message part name
     */
    name?: string;
    /**
     * The message Part Type
     */
    type?: string;
    /**
     * The message URL
     */
    url?: string;
    /**
     * Te message data
     */
    data?: string;
    /**
     * The size of the data 
     */
    size?: number;
}

There are 2 helper methods available to allow more complex parts to be added:

withData

Here we will create a message with 2 parts, the first part being plain text, the second being an image attachment.
Note that the data type field is completely up to the integrator to use as they see fit. I have just adopted Mime types for these samples.


    // some data uri for an image ...
    let data = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="

    let message = new MessageBuilder()
        .withText("Check out this image ...")
        .withData("image/png", data);

withPart

If you woud like to manually create the parts yourself, then you can do the following


    let textPart = {
        data: "Check out this image ...",
        type: "text/plain",
        size: 24 // Note:  size is optional, use as required
    };

    let imagePart = {        
        data: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==",
        type: "image/png",
    };

    let message = new MessageBuilder()
        .withPart(textPart)
        .withPart(imagePart);

Push Notifications

If you would like to have a push notification sent with the message, you can specify a generic message for both FCM & APNS and further customise the platform specific payloads ...

Generic settings

    let message = new MessageBuilder()
        .withText("Hello world")
        .withPush("Hi there");

Platform Specific overrides


let apnsAlert = {
    badge: 1,
    sound: "ping.aiff",
    alert: "hello"
};

let fcmAlert = {
    notification: {
        body: ";-)",
        title: "hello"
    }
};    

let message = new MessageBuilder()
    .withText("Hello world")
    .withPush("Hi there")
    .withFcmAlert(fcmAlert)
    .withApnsAlert(apnsAlert);

Metadata

You can also send some metadata along with the message. This can be any object.

let message = new MessageBuilder()
    .withText("Hello world")
    .withMetadata({prop1: "val1", prop2: 2});

Events

Event Details
conversationMessage.sent A message has been sent to the conversation
conversationMessage.delivered A participant has marked a message as delivered
conversationMessage.read A participant has marked a message as read
Suggest Edits

Query conversations

 

Query a list of conversations

This is how you would query for list of ALL conversations that a user is a member of ...

sdk.foundation.services.appMessaging.getConversations()
    .then(conversations => {
        console.log("getConversations() succeeded", conversations);
    })
    .catch(error => {
        console.log("getConversations() failed", error);
    })

There are also some optional arguments to getConversations():

/**
 * Function to get all conversations (not any messages) the user is a participant in
 * @method ConversationManager#getConversations 
 * @param {ConversationScope} [scope] - the conversation scope ["public"|"participant"]
 * @param {string} [profileId] - The profileId to search with
 * @returns {Promise} 
 */

Query a specific conversation

To query the details of a specific conversation with a known conversationId, you can do the following ...

sdk.foundation.services.appMessaging.getConversation("CA29B56B-30D6-4217-9C99-577AA7525B92")
    .then(conversation => {
        console.log("getConversation() succeeded", conversations);
    })
    .catch(error => {
        console.log("getConversation() failed", error);
    })
Suggest Edits

Query conversation messages

 

Querying Messages

This interface allows you to get messages from the end of the conversation one page at a time. This will allow you to implement the classic pull down to load more interface.

This method uses a continuation token manage the paging. If you want to enumerate from the end, you don't specify a token. As part of the response, a continuation token is returned along with the messages. This is fed back in the next time we want to query for messages. If the wrong token is specified, the method will return an error and you wi have to go beck to the end of the list of messages.

Here is the first call to the api - I have specified a conversationId and a page size of 100 ...

//  Call the getMessages() api for the first time
sdk.services.appMessaging.getMessages("CA29B56B-30D6-4217-9C99-577AA7525B92", 100 )
    .then(response => {
        console.log("getMessages() succeeded", response);
        console.log("Here are your messages ...", response.messages);
        console.log("Here is your continuation token, store this and use the next time you call this method", response.continuationToken);        
    })
    .catch(response => {
        console.error("getMessages() failed", error);
    });

To query the previous page of messages, you need to feed in the continuation token that was returned the lst time you queried this conversation. The token is specific to this conversation only.

// Call the getMessages() api for the second time
// 
sdk.services.appMessaging.getMessages("CA29B56B-30D6-4217-9C99-577AA7525B92", 100, continuationToken )
    .then(response => {
        console.log("getMessages() succeeded", response);
        console.log("Here are your messages ...", response.messages);
        console.log("Here is an updated continuation token", response.continuationToken);
    })
    .catch(response => {
        console.error("getMessages() failed", error);
    });

You can keep doing this until you reach the beginning of the conversation ...

When the continuation token returned is <= 0, you have all the messages

Here is the definition of the response returned via a promise from the call to getMessages()

export interface IGetMessagesResponse {
    continuationToken?: number;
    earliestEventId?: number;
    latestEventId?: number;
    messages: IConversationMessage[];
}

The eventId's will be used later when we handle incoming events from the web-socket.

The foundation SDK doesn't deal with any kind of conversation persistence - that is up to you manage.
You may choose to just query messages from the end of the conversation every time and not bother persisting anything.

Applying realtime events to conversation Messages stored locally

Whether you choose to store your messages in some database, or just in-memory you will need to deal with new events that arrive.
These events will take the form of new messages and messages getting marked as delivered / read. I will deal with this in detail in the websocketEvents section.

Suggest Edits

Query events

 

Querying events

Conversations messages and their relative statuses are generated from an immutable event store. To build up a conversation, we play through the events and that is projected into an ordered list of messages. These events are available to the client either from the web-socket or from a call to sdk.services.appMessaging.getConversationEvents().

The live web-socket events are delivered to the app in realtime and need to be applied to the local conversation message store. If the client has any gaps, they can query a range of events using getConversationEvents(). The event payload is the same whether it is received from the web-socket of from this api method.

To query events, we can do the following ....

// retrieve up to 100 events from position 0
sdk.services.appMessaging.getConversationEvents("5D21F17C-B2EC-4622-848E-5A2A916953EA", 0, 100)
    .then(events => {
        console.log("getConversationEvents() succeeded", events);
    })
    .catch(error => {
        console.error("getConversationEvents() failed", error);
    });

Applying events

After you have initially loaded up a conversation, it becomes your responsibility to process the incoming web-socket events / query events from getConversationEvents() and update the messages accordingly.

There are 3 events that may need processing depending on whether you intend to mark messages as delivered / read.

conversationMessage.sent

This event signifies that a new message has been posted to the conversation. If this message wasn't sent by you, you should send a status update marking this message as delivered. You will also want to add this message to your local message store. You can identify the position to insert this message by looking at the sentEventid property on the messages in your message store and the conversationEventId property on the sent event. Messages should be ordered based on this sequence.

conversationMessage.read

This event signifies that a someone has marked a message as read.

conversationMessage.delivered

This event signifies that a someone has marked a message as delivered. Messages get automatically marked as delivered when you query messages with sdk.services.appMessaging.getMessages().

sdk.on("conversationMessageEvent", function (event) {

    // Check that we haven't processed this event already ....
    // when we call getMessages(), part of the response are 2 properties:
    // earliestEventId and latestEventId.
    // we need to maintain these and adjust when we query messages

    if(event.conversationEventId > earliestEventId ||
       event.conversationEventId < latestEventId){
        console.log("event already seen", event);
        return;
    }

    switch (event.name) {
        case "conversationMessage.sent":
        // add message to local conversation store 
        // also end a messageStatus update of delivered for this message
        // You can send another update of read when you display the message to the user
        break;

        case "conversationMessage.read":
        // update statusUpdates in locally stored message object (if found) to reflect message is read by event.payload.profileId
        break;

        case "conversationMessage.delivered":
        // update statusUpdates in locally stored message object (if found) to reflect message was delivered to event.payload.profileId
        break;
    }
});

StatusUpdates

Status updates are stored against a statusUpdates property on a message and is of the following structure. The status will either be delivered or read. if it is read, it implied to also be delivered.

{
    "alex": {
        "status": "read",
        "on": "2016-10-19T11:52:29.704Z"
    },
    "dave": {
        "status": "delivered",
        "on": "2016-10-19T11:00:29.704Z"
    },
}

IGetMessagesResponse

export interface IGetMessagesResponse {
    continuationToken?: number;
    earliestEventId?: number;
    latestEventId?: number;
    messages: IConversationMessage[];
}
Suggest Edits

Websocket events

 

Events

Realtime events are delivered to the SDK via a web-socket. These events can be subscribed to via the following methods ...

Subscribe to an event

sdk.on("profileUpdated", function (event) {
    console.log("profileUpdated", event);
});

Unsubscribe from an event

sdk.off("profileUpdated");

Complete list of events

Event Name Event Payload Description
conversationDeleted IConversationDeletedEventData Sent when a conversation is deleted
conversationUndeleted IConversationUndeletedEventData Sent when a conversation is undeleted
conversationUpdated IConversationUpdatedEventData Sent when a conversation is updated
participantAdded IParticipantAddedEventData Sent when a participant is added to a conversation. When a conversation is created, this event will also fire with the owner's profileId.
participantRemoved IParticipantRemovedEventData Sent when a participant is removed to a conversation. App needs to check whether the participant is the current user and locally remove the conversation from the UI.
participantTyping IParticipantTypingEventData Sent when a participant is typing in a conversation
profileUpdated IProfileUpdatedEvent Sent when a user's profile is updated
conversationMessageEvent IConversationMessageEvent This event is sent for all conversation message related activity. It encapsulates the sent, delivered and read events. It is defined like this so you can handle web-socket conversation message events and events requested from sdk.services.appMessaging.getConversationEvents() seamlessly.

IConversationDeletedEventData

export interface IConversationDeletedEventData {
    conversationId: string;
    createdBy: string;
    timestamp: string;
}

IConversationUndeletedEventData

export interface IConversationUndeletedEventData {
    conversationId: string;
    createdBy: string;
    timestamp: string;
}

IConversationUpdatedEventData

export interface IConversationUpdatedEventData {
    conversationId: string;
    createdBy: string;
    name: string;
    description: string;
    roles: IConversationRoles;
    isPublic: boolean;
    timestamp: string;
}

IParticipantAddedEventData

export interface IParticipantAddedEventData {
    conversationId: string;
    createdBy: string;
    profileId: string;
    role: string;
    timestamp: string;
}

IParticipantRemovedEventData

export interface IParticipantRemovedEventData {
    conversationId: string;
    createdBy: string;
    profileId: string;
    timestamp: string;
}

IParticipantTypingEventData

export interface IParticipantTypingEventData {
    conversationId: string;
    createdBy: string;
    profileId: string;
    timestamp: string;
}

IProfileUpdatedEvent

export interface IProfileUpdatedEvent {
    eTag: string;
    profile: any;
}

IConversationMessageEvent

This event encapsulates sent, delivered and read events.

export interface IConversationMessageEvent {
    eventId: string;
    // name field will be ["conversationMessage.sent" | "conversationMessage.read" | "conversationMessage.delivered"]
    name: string;
    conversationId: string;
    conversationEventId: number;
    // payload will differ based on event name ...
    payload: IMessageSentPayload | IMessageStatusUpdatePayload;
}

// payload for conversationMessage.sent
export interface IMessageSentPayload {
    messageId: string;
    metadata: any;
    context: any;
    parts: IMessagePart[];
    alert: IMessageAlert;
}

// payload for conversationMessage.delivered, conversationMessage.read
export interface IMessageStatusUpdatePayload {
    messageId: string;
    conversationId: string;
    profileId: string;
    timestamp: string;
}
Suggest Edits

Message status updates

 

Message status updates

Message status updates are sent by the application when a message is received or when the message has been read.
If you query for messages using sdk.services.appMessaging.getMessages(), they will be automatically marked as delivered.
It is the application's responsibility to mark New messages received from the web socket.
It is also the application's responsibility to mark the messages as read whenever this occurs. (This is optional functionality)

To send a message status update, you can use the MessageStatusBuilder interface.
You will also need the conversationId of the conversation that you you wish to send the status update to.

ES6 implementation of marking a message as delivered

import { MessageStatusBuilder } from "@comapi/sdk-js-foundation";

// Note I am using the version that takes a single messageId in this sample
let status = new MessageStatusBuilder().deliveredStatusUpdate("C984814D-B714-4DC8-8DFF-33C29082ACEA");

// we can send multiple updates of different types with this method, hence the array ...
sdk.services.appMessaging.sendMessageStatusUpdates(conversationId, [status]);

Classical implementation of marking a message as read

// Note I am using the version that takes a list of messageId's in this sample
var status = new COMAPI.MessageStatusBuilder().readStatusUpdates(["C984814D-B714-4DC8-8DFF-33C29082ACEA", "88E43FA4-9705-44F5-8DE6-4B9DD5E46DF3"]);

sdk.services.appMessaging.sendMessageStatusUpdates(conversationId, [status]);

See the API documentation for the full list of methods available. There are methods for single or multiple updates for both read and delivered statuses

Suggest Edits

Profile API

 

Profile API

When a user is created, a profile is also automatically generated.
The application can store user specific information in the profile.

Querying the user's profile

sdk.profile.getMyProfile()
    .then(function (profile) {
        console.log("getMyProfile() succeeded", profile);
    })
    .catch(function (error) {
        console.error("getMyProfile() failed", error);
    });

Updating the user's profile

sdk.services.profile.updateMyProfile(profile)
    .then(function (updatedProfile) {
        console.error("updateMyProfile() succeeded", updatedProfile);
    })
    .catch(function (error) {
        console.error("updateMyProfile() failed", error);
    });

eTags

The SDK internally uses eTags to prevent simultaneous updates of the profile from overwriting each other.

If you want to update a profile, you need to query it first, make the appropriate changes to it and then update it. When the profile is queried, an eTag is internally stored and passed back in the update. If they don't match, an error will be returned. This means that the state of the profile has changed since it was queried and what we tried to update is now stale.

Suggest Edits

Typescript

 

Typescript

This library was written in typescript, hence all entities that the SDK deals with have an associated interface.

Assuming you installed the SDK via npm, you can access all of the relevant interfaces as required.

Here is a simple example showing how you would go about importing an interface ...

import { 
    Foundation,
    IConversationMessage
} from "@comapi/sdk-js-foundation"

export class MessagesHelper {

    /**
     * Method to determine whether a message has been read or not by  user
     * @param {IConversationMessage} message - the message to check
     * @param {string} profileId - the profileId to check read status against 
     */
    public static isMessageRead(message: IConversationMessage, profileId: string):boolean {

        var isRead = false;

        // if the message was sent by this person then skip ...
        if (message.context.from.id !== profileId) {
            // are there status updates and is there one for this user
            if (message.statusUpdates && message.statusUpdates[profileId]) {
                // is it read ?
                if (message.statusUpdates[profileId].status === "read") {
                    isRead = true;
                }
            }
        } else {
            // the user who wrote the message obviously read it ...
            isRead = true;
        }

        return isRead;    
    }
}

If you look in node_modles/@comapi/foundation/src/interfaces.d.ts, you will see what interfaces are available.

 

Cordova Integration

This SDK can easily be incorporated into a cordova app.
There is no specific Comapi plugin as such as all the necessary functionality required to configure push notifictions are present in the core sdk. You just need to install the SDK into your app.

See the Installation section for details of how to do this.

For Ionic 2, follow the NPM guidelines.

For Ionic 1 or similar, follow the Bower guidelines.

You will however need a push notification plugin. The SDK implements push in a standard way so you can just install phonegap-plugin-push or use something similar.

Here are some snippets showing how to send the registrationId to Comapi

Retrieving the push registrationId is an asynchronous task that will be performed AFTER the cordova deviceready event has fired.
This needs to be passed to Comapi and can only be passed after the SDK is initialised and a valid session has been created.
There is potential for a race condition here so i will split the two tasks apart and save the registrationId to localStorage for the purposes of this snippet.

Getting the registrationId

This snippet is using the Push plugin from Ionic Native to retrieve the registrationId and setup a notification handler. There is no Comapi code in here.

import { Push } from 'ionic-native';

platform.ready().then(() => {

    let push = Push.init({
        ios: {
        alert: "true",
        badge: true,
        sound: 'false'
        }
    });

    push.on('registration', (data) => {
        console.log("got a registrationId", data.registrationId);
        localStorage.setItem("registrationId", data.registrationId);
    });

    push.on('notification', (data) => {
        console.log("got a pushNotification", data);
    });

}

Sending the registrationId to Comapi

This snippet shows how to send the registrationId to Comapi.
The assumption is that you have an initialised SDK and a valid session at this point. Note the Environment import.

import { Environment } from "@comapi/sdk-js-foundation";

// Put this somewhere appropriate in your app (at the end of you initialisation/login flow)

let registrationId = localStorage.getItem("registrationId");
// skip if registrationId hasn't been collected
if(registrationId){

    // There are separate methods to call depending on platform ...
    if (platform.is('ios')) {

        // You will need to create an APNS cert. in the apple developer portal.
        // Then you must upload it to your API space in the Comapi portal.
        // Can be a development or production cert, hence the environment parameter
        sdk.device.setAPNSPushDetails(">>> My Bundle Id <<<", Environment.development, registrationId)
        .then(result => {
            console.log("setAPNSPushDetails() succeeded", result);
        })
        .catch(error => {
            console.error("setAPNSPushDetails() failed", error);
        });

    }else if(platform.is('android')){

        sdk.device.setFCMPushDetails(">>> My Package Name <<<", registrationId)
        .then(result => {
            console.log("setFCMPushDetails() succeeded", result);
        })
        .catch(error => {
            console.error("setFCMPushDetails() failed", error);
        });

    }
}else{
    console.error("no registrationId ;-(");
}

Push Payloads

When you send a message, you can individually craft the platform specific payloads.
They will be received in the notification event handler shown in the first snippet.

Suggest Edits

SMS API Overview

 

Comapi has extensive SMS capabilities through the One API, this section details how to use these services.

SMS Services

Send SMS

Please see the Comapi "One" API section to find out how to send messages

Inbound SMS

Allows you to receive SMS messages sent from phones to your Comapi hosted mobile numbers, short codes or keywords. Messages are delivered to a URL of your choosing.

Receipts

If you need to to know the status of messages you've sent using one of our APIs, you can request that delivery receipts are forwarded to a URL of your choosing.

Click-through Forwarding

If your system needs to know whether URLs inserted into SMS messages have been clicked, check how long the clicker took between receiving the message and clicking the URL or discover details about the clicker's operating system, you can have details forwarded to a URL of your choosing.

We recommend using the One API for SMS messaging

You can use the One API to send outbound messages, and therefore easily utilise Branch and other capabilities without needing to change your integration

Suggest Edits

SMS Reference

 

The following pages contain useful reference information when working with our SMS APIs.

Suggest Edits

SMS Message Encoding

 

Most text messages will use the more space efficient 7 bit GSM character set as it covers most Latin based language characters. The GSM character set was extended to cover a few more commonly used characters and these 10 characters are known as the Extended GSM characters and include:

€ <FF> [ \ ] ^ { | } ~

Characters included in the Extended GSM character set will use 2 characters (7 bit GSM) instead of the usual 1 as they require an escape character prefix.

The GSM 7-bit character set is supported default by all GSM handsets and network elements, but characters in languages such as Arabic, Chinese, Korean, Japanese or Cyrillic alphabet languages (e.g. Russian, Serbian, Bulgarian, etc.) must be encoded using the 16-bit UTF-16 character encoding otherwise known as Unicode.

GSM character set

See here for more info on the GSM character set

Using SMS with different languages

See the following knowledge base article for more information on how SMS can be used to send messages in different languages

Note: If your message contains any characters that are not included in the GSM or Extended GSM character sets then the message will be encoded entirely as Unicode (UTF-16) and therefore each character will take 16 bits instead of 7 bits more than halving the amount of characters per SMS and potentially doubling your costs!

Suggest Edits

SMS Message Segmentation

 

An individual SMS is limited to 140 octets (140 octets = 140 * 8 bits = 1120 bits) of content data. SMSs can be encoded using a variety of character sets: GSM 7-bit (Default), and 16-bit UTF-16. Depending on which character set is selected, this leads to the maximum individual SMS sizes of 160 for 7-bit (GSM) encoding or 70 for 16-bit (Unicode) encoding, including spaces. When you step over these single message maximum lengths, the maximum character count changes since a small number of characters are now required to be used to associate the individual SMS segments together, so that these segments can then be recombined at the destination as one long message.

The SMS character limits for each encoding type are explained as follows:

7-bit encoded characters (Standard GSM)

1 SMS

160 characters

2 SMS

306 characters

3 SMS

459 characters

4 SMS

612 characters

UTF-16 (Unicode) encoding

1 SMS

70 characters

2 SMS

134 characters

3 SMS

201 characters

4 SMS

268 characters

Suggest Edits

SMS Message Statuses

 
Status Id
Enum
Description

1

Delivered

Delivered to handset

2

Expired

Not delivered during validity period

3

Sent

Sent

4

Rejected

Rejected by operator

5

InvalidNumber

Sent to discontinued/invalid number

6

FullSIM

Sent to handset with full SIM

7

RejectedSMSC

SMSC was not able to send

8

NoCredit

User with no credit

9

NoResponse

No response from Mobile Operator

10

Queued

Queued

11

Deleted

Deleted by user

12

Refused

Refused

13

Filtered

Filtered by Intelligent Filtering

Suggest Edits

Zapier

Learn how to use COMAPI SMS with Zapier

 

Comapi SMS can now be used with Zapier to quickly add SMS messaging, phone number validation and One Time Codes to any existing system using their powerful and easy to use system.

What can I do?

The following triggers are available on Zapier to start a Zap:

  • Receive an SMS
  • Receive a receipt for an SMS send

Let us know!

The Zapier triggers will only work for you once we have enabled polling support for your username. Please email us and let us know you want to use Zapier for inbounds and we'll enable you.

The following actions are available to add messaging to your Zapier automations:

  • Send a SMS
  • Send a One Time Code
  • Validate a One Time Code
  • Validate a mobile number

Getting Started

To get started please do the following:

  • Sign up to Zapier, its free to try!
  • Click here and accept the invite to use Comapi on Zapier
  • Add a new Zap
  • Pick a trigger source of your choosing e.g. Comapi, Trello, HubSpot etc..., adding a new card
  • Pick Comapi or another system for the action app
  • Choose your specific action e.g. Send SMS message
  • Fill out the fields
  • Test

That's it, please let use know how you get on.

Remember

Please ensure your account has been enabled for inbounds using the instructions above and your have a pending inbound SMS before testing.

Comapi SMS triggers being used in Zapier with Comapi actions

Comapi SMS triggers being used in Zapier with Comapi actions

Suggest Edits

About Inbox

Think inside the inbox and get real mobile engagement

 

Inbox is the new Donky

Comapi Inbox was formally known as Donky, so some aspects of the SDK still refer to this brand

Inside the Inbox

Inbox is a complete platform for engaging and communicating to your customers via any mobile or native application. Inbox gives you the power to send rich HTML content and push notifications into apps. Use Comapi’s inbox API’s or our campaign and lifecycle management tools to send your messages. Reach, inspire and manage your users. Inbox will help you to keep your mobile audience active with read rates of over 40%. Let’s transition your communication from just email to a truly mobile first experience together.

Click here for more information about Inbox and it great features, or if you want to get started checkout the pages in this section.

Find code samples and SDK source in Github below:

Suggest Edits

Registering Users

How to register users onto the Inbox network using the SDK's

 

In order to use any Inbox SDK modules, you must first initialise the Core Module, and register a user on the network. The most common ways to do this are:

  1. Register an anonymous user as soon as the app is opened or the website is launched for the first time, and update their details when they register / login.
  2. Register a known user after the a user has registered / logged in to your app or website

We generally recommend approach 1, which is to register anonymously as soon as the app / website is opened, and then update the users details when they register or login, therefore turning them from anonymous to known users.

Remember at any time after a user has been registered, you can also update the user's additional properties, or tags. For more information on doing that, see here.

1. Register an Anonymous user

Our recommended approach

This approach is the easiest to achieve and allows nurturing of users who haven't registered for you apps and websites using messages

Registering as an anonymous user

When the user first downloads your app or opens your website, the core SDK should be initialised without providing any details, this will automatically create an anonymous user on the network, with a guaranteed unique user id e.g. 22ruyhg. The logical flow for this pattern is shown below:

Registering an anonymous user logic flow

Registering an anonymous user logic flow

Use the following methods in the SDK to create this logic:

  • Checking whether a user is registered with Inbox, use IsRegistered
  • Registering an anonymous user, use Initialise

Target your anonymous users and convert them to known

You can send messages to your anonymous users in Inbox's Campaign tool to encourage them to register and increase their value to you. You can easily find this audience by filtering users on the IsAnonymous property equalling true.

Updating the user to become Known

So you have an anonymous registration as you do not know who the user of a device is yet, but as soon as the user completes a sign in or sign up process, you can update their information and turn them into a Known user. This will update their information in the Inbox Portal, and will connect any other devices they have to the same user.

You will at this point need to provide a user Id, which can be used to uniquely recognise this user in your App Space, we recommend using the same unique value you use with your own systems e.g. email, mobile number, username, etc..., so that the user can be easily recognised and data correlated with your systems as they use the same user ids.

We recommend each time a user signs in that you check to ensure that the user id that the Donky SDK has matches that of the authenticated user, if not you should update the details held by the SDK to match.

The logical flow for this pattern is shown below:

Logic flow when a user registers or logs in

Logic flow when a user registers or logs in

Use the following methods in the SDK to create this logic:

Ready to develop?

See each OS section for more details on implementing the SDK on that platform

2. Register a Known User

If you want to only register users that are known to you, then you must delay initialising the SDK for the first time until the user signs in or signs up for you app / website, however once registered with the SDK the SDK should be initialised immediately from that point to ensure your app can receive new messages.

You will at this point need to provide a user Id, which can be used to uniquely recognise this user in your App Space, we recommend using the same unique value you use with your own systems e.g. email, mobile number, username, etc..., so that the user can be easily recognised and data correlated with your systems as they use the same user ids.

Here is the logic flow for when the app / website is opened:

Website / App open logic flow for known user only approach

Website / App open logic flow for known user only approach

Here is the logic flow for when a user signs up or signs in:

User signs up or signs in logic flow for known user only approach

User signs up or signs in logic flow for known user only approach

Use the following methods in the SDK to create this logic:

Ready to develop?

See each OS section for more details on implementing the SDK on that platform

Suggest Edits

SDK Architecture

Exploring Inbox's Modular SDK, custom notifications and pub/sub event model.

 

Overview

All of the Inbox Client SDKs conform to a common, extensible architecture which enables you to easily build your apps and custom modules that integrate with Inbox with ease.

Modules

The Inbox SDK is composed of a number of modules, each containing specific functionality, allowing you to include only the functionality that you need in your app.

You can also leverage this model to build your own modules that are capable of sending and receiving data over the Inbox Network, as well as utilising the local Event Bus to react to Inbox events or publish your own for other components of your app to use.

You don't have to include any other Inbox modules except Core - it's just as easy to have a setup like the following, enabling you to create a completely bespoke app that leverages Inbox for communications:

Module Definition

A Inbox module is defined by a pair of values:

  • Name (e.g. MyApp.Inbox)
  • Version (e.g. 1.5.23.122)

When you register a module definition with the Core module, these values are attached to the device properties we store within the Inbox Network and can be viewed in the Inbox Portal, which is a useful tool for understanding exactly what code is running on any given device.

Custom Module Example

Registering a module with Inbox is as simple as this:

// Define the module
var myModule = new ModuleDefinition {
	Name = "MyApp.Inbox",
	Version = "1.5.23.122" // Depending on your language, you can set this to pull in the version from the containing binary 	 
};
 
// This should be called during a bootstrap phase
DonkyCore.Instance.RegisterModule(myModule);

// Initialise Inbox after modules have been registered
await DonkyCore.Instance.InitialiseAsync("apikeyhere");
var myModule = {  
  name: "MyApp.Donky", 
  version:"1.5.23.122" 
};

donkyCore.registerModule(myModule);

Notifications

At the heart of any Inbox-enabled app is the handling of Notifications. Inbox's Simple Push, Rich and Chat messages are all delivered to our SDK as types of notification, and you can use this mechanism to define and handle your own notifications as well.
The Core module exposes a Subscription API which is used to add handlers for any type of notification.

Custom Notifications

A custom notification has a simple contract:

  • Type (a string identifying the type of notification, e.g. "PurchaseMade")
  • CustomData (any object, as long as it can be serialised to JSON) up to a maximum 256KB

Subscription API

Adding support for notification subscriptions in your custom module requires a single call to the Core API:

DonkyCore.Instance.SubscribeToNotifications(
	myModule, // reference to your module definition
	new CustomNotificationSubscription {
		Type = "PurchaseMade",
		Handler = async notification => {
			// myData will be a JSON object containing the custom notification data
			var myData = notification.Data["customData"];
			// TODO: handling logic here...
		}
	}
// setup a Content Notification subscription ...
donkyCore.subscribeToContentNotifications(myModule,
{
 	notificationType: "PurchaseMade",
  handler: function(notification) {			
    var data = notification.data.customData;
    // TODO: handling logic here...
  }
});		

Sending notifications

Sending any form of Content to another user / device via the Inbox SDK simply requires a call to the SendContentNotifications API, and custom notifications are just a type of content.

var content = new ContentNotification()
	.ForUsers("hansolo54")
	.WithCustomContent("PurchaseMade", new {
		Product = new {
			Id = 12,
			Description = "Millenium Falcon Drone"
		}
	});

await notificationController.SendContentNotificationsAsync(notification);
// create a custom notification
var notification = donkyCore.createContentNotificationForSpecifiedUsers(["hansolo54"], "PurchaseMade", {		
  Id = 12,
  Description = "Millenium Falcon Drone"
});

// send it
donkyCore.sendContentNotifications(notification, function(result) {
  if (result.succeeded) {
		// message succesfully sent
  }
});								

Local Events - Pub/Sub Model

The Inbox Core module contains an Event Bus which provides a publish/subscribe mechanism that you can use within your app. This may be when writing a custom UI for Inbox content:

DonkyCore.Instance.SubscribeToLocalEvent<SimplePushMessageReceivedEvent>(e => {
	// Do something here
});
donkyCore.subscribeToLocalEvent("NewSimplePushMessagesReceived", function(event) {
  // Do something here
});

Or it can be used to route events around in your own application and provide loosely-coupled comms:

// Somewhere in your code:
DonkyCore.Instance.PublishLocalEvent(new MyEvent{
	SomeProperty = "Value",
	AnotherProperty = 123
}, myModule);

// Elsewhere...
DonkyCore.Instance.SubscribeToLocalEvent<MyEvent>(e => {
	// Do something with the event here
});
donkyCore.publishLocalEvent({ type: "NewSimplePushMessagesReceived", data: newMessages });


donkyCore.subscribeToLocalEvent("NewSimplePushMessagesReceived", function(event) {
  // Do something here
});

Because this is a pub/sub model, you can add multiple subscribers for any event. Inbox's event bus will call all subscribers whenever an event of the specified type is published.

Suggest Edits

Core Module

The heart of the Inbox SDKs

 

In order to connect with and build your app using the Inbox Network, you must integrate the Core module. It is responsible for:

  • Handling registrations of users & devices to the network
  • Management of User and Device specific data
  • Sending and receiving content from the network
  • Publish and subscribe between all other modules in the SDK

The Core module also provides a local event bus, which is used by our other modules to provide inter-module communication and can also be used by your code to help you build your app.

Using this module, you can build an app that uses the Inbox Network to send and receive any data between devices, using your app. However, there are some other modules that may prove beneficial to add on top.

Analytics

  • The analytics module automatically tracks launches, sessions and influenced launches (from clicks on push notifications)

Automation

The automation module enables you to create your own trigger events (using Inbox third party triggers) and send comms via Inbox or notify other systems.

  • A third party trigger can be set up to execute on any event that occurs within your app.
  • The tracking of the event is up to you to create, but the Inbox network can be used to notify an external system or send a message off the back of that event.
  • For example, basket abandonment or completion of a specific form.
  • This trigger can either initiate a triggered campaign or notify your external system to update a database or trigger another activity.

Triggered messages using meta data

Using Triggers and Campaigns, you can send messages to your users when they complete any custom action in your app. If you want to include information for context in the message you can do so using meta data.

For example: "Thanks for browsing Tweed Trousers, please come back soon."

When you send a trigger executed notification to the network, you can include meta data in the format "td_metaitem".

If we had passed the meta data name/value pair of "itemType" , "clothes", then the following token would be used to merge this into a message or link: [[td_itemType]]

For example: "Thanks for browsing [[td_itemType]], please come back soon." This is how we would achieve the above example.

Platforms

Register Users

Send Custom Content

Analytics & Automation

Suggest Edits

Push Module

Real time push notifications across platforms

 

The Push module will set up native or web push notifications for you, on top of which you can optionally add custom UI to enable custom in app banners when a notification is received while the app is open. Inbox supports standard push notifications and interactive push notifications.

A standard push notification example on iOS

A standard push notification example on iOS

An example of a standard push notification that has been rendered inside an application. You must use the Simple Push UI module to achieve this.

An example of a standard push notification that has been rendered inside an application. You must use the Simple Push UI module to achieve this.

Inbox supports Interactive Notifications on iOS 8 and above and Android 4.0 and above, so you can create engaging two way interactions with your users. You can use our button sets, or create your own to add pairs of buttons to your notifications, or set a custom on tap action for the push.

Example interactive push notification on Android

Example interactive push notification on Android

Example interactive push notification on Android

Example interactive push notification on Android

Push for web, via the JavaScript SDK shows either one button for the "on tap" or two buttons for interactive push as well as a default dismiss button, if included in your push UI.

An example of a push notification on web using Inbox's default UI

An example of a push notification on web using Inbox's default UI

Key Features & Benefits

  • Fast and effective method of communicating to app users
  • Personalise with merge fields
  • Add custom UI for in-app banners, customising Android remote notifications, or creating custom web experiences
  • Interactive notifications for generating engagement
  • Deep link in-app or on web

Platforms

Suggest Edits

Rich Module

 

You can send HTML5 custom content into your app either to an in-box or live "view" using Rich content. Using either raw, responsive HTML or by creating with our WYSIWYG editor, you can build any HTML5 page to send.

When you send Rich, you can also send a Push to notify your users that there is new content waiting for them in the app.

Example rich message

Example rich message

Key Features & Benefits

  • Generate two-way integrations with users
  • HTML based rich content
  • Set avatars for your messages, individual to the content
  • Allow sharing to social networks, email, SMS, or other share enabled apps
  • Available for a 30 day period by default
  • Assets hosted on the Inbox network
  • Shorten availability for time sensitive offers
  • Replace expired messages with different message body
  • For silent delivery, disable notifications

Receiving and displaying Rich Content

Rich content is persistent and can be accessed in your application either from the Inbox UI , or your own custom UI. You can create the messages, target them to desired audience and send them using our Campaign Builder tool.

The Rich Logic Module accepts rich messages from the Inbox Core Module and translates them into a form that can be consumed by the UI.

The Rich Logic will accept rich content in the form of a RichMessageEvent from the Core module, this event contains all the information necessary to present the message to the user. The messages can the be accessed from the database through public APIs. You will need a UI layer to present these messages to the user. You can use some of the UI components from another Inbox Modules or you can create your own UI on top of the logic layer.

Using the Inbox UI

The Rich Inbox is an off the shelf solution to allow you to add an inbox to your app with minimal development. It can be implemented with no changes, and become default to your app, or you can customize it to match your theme and styles with very little effort.

  • The SDK will notify the user about new messages by Remote Notification [above screenshot 1].
  • If the user taps this notification SDK will open your application and display an Activity with the message content [screenshot 2] (or inbox if multiple messages were delivered [screenshot 3]).
  • You can access all the saved Rich Messages by displaying Inbox Activity manually [Screenshot 3].

You can also fully customise all the visible colours and icons in all Inbox UI components.

Example of a rich message being displayed on an iPhone 6+, iOS 8.4 in Landscape.

Example of a rich message being displayed on an iPhone 6+, iOS 8.4 in Landscape.

[Screenshot 2] Rich Message Activity launched on tap of notification.

[Screenshot 2] Rich Message Activity launched on tap of notification.

[Screenshot 3] Inbox activity launched in app, or from click of notification for multiple messages.

[Screenshot 3] Inbox activity launched in app, or from click of notification for multiple messages.

Customisation example for Android.

Customisation example for Android.

Pop-up UI

The Pop-Up Module allows you to display rich messages to the user without needing an inbox view. It will show the user the rich messages as soon as they are received from the network and processed by the Rich Logic.

An example of a Rich Pop-up UI

An example of a Rich Pop-up UI

Availability and Expiry

By default, Inbox hosts Rich Content for 30 days, this is called the availability period. You can optionally shorten this period by setting an expiry when you send a campaign.

When configuring expiry, you can also set whether or not to use a replacement message body after expiry, for the remainder of the availability period.

Expired with replacement body

When the defined expiry passes, the new body will be displayed in its place.

Example of a message that has expired and the expired content has been set. Device is iPhone 6+, iOS 8.4 in Landscape.

Example of a message that has expired and the expired content has been set. Device is iPhone 6+, iOS 8.4 in Landscape.

Expired without replacement body

  • When the message expires, it will no longer be visible to the user.
  • If the SDK receives that message after expiry date it won't be saved to the database and the message won't be presented to the user.
  • Expired messages are handled using the isReceivedExpired() method
  • In the Inbox Inbox UI, the row containing the expired message will be greyed out, and the time stamp replaced with "EXPIRED"
  • When an expired message is tapped in the inbox, a pop-up is displayed, from which the user can delete the message or delete all. It is not possible to view the content.
An example of the alert view that is presented to the user if they tap an expired message. On Android device, portrait.

An example of the alert view that is presented to the user if they tap an expired message. On Android device, portrait.

Example of what an expired message will look like. Device is iPhone 6, iOS 8.4 in Portrait.

Example of what an expired message will look like. Device is iPhone 6, iOS 8.4 in Portrait.

An example of the alert view that is presented to the user if they tap an expired message. On an iPhone 6+, iOS 8.4 in Landscape.

An example of the alert view that is presented to the user if they tap an expired message. On an iPhone 6+, iOS 8.4 in Landscape.

Time stamps

When a message is received in the inbox UI, it displays a time stamp. This time stamp is dynamically updated as time passes, according to the following rules.

Received time
Time stamp example

Received in the last 2 minutes

'Just Now'

Received less than 1 hour ago

'x min ago'

Received more than 1 hour ago and wasn't the day before

'x hr ago'

Received more than 1 hour ago and was the previous day

'yesterday'

Received more than 24 hours ago but less than 48 hours

'yesterday'

Received more than 48 hours ago but less than 1 week

'Name of the day e.g. Wednesday'

Received more than 1 week (7 days)

date e.g. 12/05/2015 (this date is localised to the user)

Platforms

Logic

Custom UI

Pre-built Inbox UI

Examples

An example of the rich inbox on an iPhone 6, iOS 8.4 in Portrait. The rich message is unread.

An example of the rich inbox on an iPhone 6, iOS 8.4 in Portrait. The rich message is unread.

An example of a rich message on an iPhone 6, iOS 8.4 in Portrait.

An example of a rich message on an iPhone 6, iOS 8.4 in Portrait.

An example of the extra options provided by the inbox UI. An iPhone 6, iOS 8.4 in Portrait

An example of the extra options provided by the inbox UI. An iPhone 6, iOS 8.4 in Portrait

An example of the rich inbox on an iPhone while in edit mode. Allowing users to delete more than one message at a time. An iPhone 6, iOS 8.4 in Portrait

An example of the rich inbox on an iPhone while in edit mode. Allowing users to delete more than one message at a time. An iPhone 6, iOS 8.4 in Portrait

An example of the rich inbox split view on an iPad, iOS 7.0 in Landscape. With an unread and un selected rich message.

An example of the rich inbox split view on an iPad, iOS 7.0 in Landscape. With an unread and un selected rich message.

An example of a selected rich message in a Split View controller on an iPad, iOS 7.0 in Landscape.

An example of a selected rich message in a Split View controller on an iPad, iOS 7.0 in Landscape.

An example of the extra options provided by the rich inbox UI on an iPad, iOS 7.0 in Landscape.

An example of the extra options provided by the rich inbox UI on an iPad, iOS 7.0 in Landscape.

An example of the edit mode of the rich inbox UI on aniPad, iOS 7.0 in Landscape. Allowing users to delete multiple messages at once.

An example of the edit mode of the rich inbox UI on aniPad, iOS 7.0 in Landscape. Allowing users to delete multiple messages at once.

Suggest Edits

Assets Module

 

The Assets module provides you with the ability to send assets from one user to another via the network. It functions by uploading the asset to the network, which then returns and ID correlating to that asset. This can then be downloaded by any client using the APIs listed below.

Key Features

  • Upload any file to the Inbox network and receive an Asset ID which can be sent to other users
  • Download any file from the network using the Asset ID
  • Maximum file size per asset is 10MB

Platforms

Suggest Edits

Core Location Module

 

The Core Location module makes getting location updates from iOS and Android very simple. You can then use these updates to display the user's location, send their location to another user, or update the Inbox network.

Using this module you can create a custom location request, meaning you can control:

  • Frequency of the update if at rest
  • Minimum distance travelled before an update is needed
  • Battery usage
  • Accuracy required

Once you receive the location from the module, you can render it locally in your UI, or use Inbox to send it out:

  • When / if the user's location is requested by another user
  • To send the user's location to another user, without a request
  • To update the Inbox network with the users last known location

Platforms

Suggest Edits

Authentication

Overview of Inbox's Authentication

 

When using Inbox as a platform for communication, the user id is the primary way of targeting content at specific users. Inbox offers 2 different ways to register your users and control this id - Basic Registration and Authenticated Registration.

Switching between Registration Modes

An App Space will work either in Basic or Authenticated mode. You should consider this when initially configuring your app as the Inbox Network configuration and SDK usage must be aligned.

Basic Registration

With Basic Registration, you simply pass the user id you wish to use to the Inbox SDK when setting user details. If multiple devices are registered with the same id, Inbox assumes these devices are linked to the same profile. For example:

donkyCore.donkyAccount.updateUserDetails({
		id: "joebloggs",
  	displayName: "Joe Bloggs"
	}, 
	function(result) {  
		// TODO
});

Authenticated Registration

Authentication Registration enables you to link the identity used with Inbox to that used in your own systems. Inbox uses a token-based authentication scheme, enabling you to configure Inbox to use trusted identity information from your systems to identify users on our platform.

With Authenticated Registration, you cannot specify any values for the user id to the SDK. The user id will be taken from a claim in the token.

In order to use Authenticated Registration, you must have a service available to your app that can generate a JSON Web Token (JWT - https://en.wikipedia.org/wiki/JSON_Web_Token) with the required information.

Token Requirements

In order to be used with Inbox, your JWT must meet the following requirements:

  • Have a valid, fixed Issuer (iss) claim value
  • Have a valid, fixed Audience (aud) claim value
  • Have a valid signature which can be verified using a Shared Secret
  • Have an expiry value (exp) that is valid at the point of use
  • Have a claim which can be used to uniquely identify the user

In addition to these minimum requirements, we highly recommend that your service / token provider uses the following:

Getting Started

Setting up Authentication in Inbox Control simply requires you to provide details of your authentication provider. Under Settings / General for your App Space, click 'Activate Authentication' and you will see the following section:

Authentication Settings

Authentication Settings

Authentication Activated

Once your details have been submitted here, Authenticated Registration will be active for your App Space

SDK Configuration

You must also configure your app to tell the Inbox SDK to work with Authenticated Registration. This requires you to provide a call-back to handle the authentication challenge, which will be invoked whenever the SDK needs to authenticate the current user.

The call-back will include the required nonce value (if used) and should return a valid token to the SDK.

You can initialise the SDK in Authenticated mode as follows:

public class MyApplication extends Application {

   @Override
   public void onCreate()
   {
      super.onCreate();
      
      //Init needs to be placed in onCreate in Application class
      DonkyCore.initialiseDonkySDK(this,">>ENTER API KEY HERE<<",
         new Authenticator() /* extends DonkyAuthenticator */, 
         new DonkyListener() /*set callback handlers*/);
   }
}

class Authenticator implements DonkyAuthenticator {

	@Override
  public void onAuthenticationChallenge(DonkyAuthClient authClient,
                                        ChallengeOptions options) {
    
    /**
     * Obtain JWT token based on a given nonce and return the value to
     * authClient instance. At this point the user authentication process will 
     * be able to continue automatically.
     * options.getNonce() for nonce to be included in token
     * options.getForUserId() expected user id when re-authenticating
     */
     authClient.authenticateWithToken("TEST_TOKEN");
   }

}
[[DNDonkyCore sharedInstance] authenticatedInitialiseWithAPIKey:@"YOUR_API_KEY" autoRegister:NO];
/**
 * Integrator provided Function - called by SDK to get JWT for the user you want to authenticate.
 * This method will be called during registration and also during token re-authentication.
 * @param {Object} options - object contining the following options:  
 * @param {String} options.nonce - nonce to use when gennerating the JWT 
 * 	(Optional - if not using nonce will be undefined) 
 * @param {String} options.expectedUserId - the userId of user we want to authenticate 
 * 	(this should appear as a "sub" claim in the JWT) 
 * @param {Callback} answerAuthenticationChallenge  
 */
function onAuthenticationChallenge(options, answerAuthenticationChallenge){
  var jwt = ">>YOUR JWT <<";  
  answerAuthenticationChallenge(jwt); 
}


donkyCore.authenticatedInitialise({
  logLevel: 4,
  apiKey: ">>>YOUR API KEY<<<",
  resultHandler: function(result) {
    console.log(JSON.stringify(result, null, 4));
  },
  userInfo: userInfo,			// optional
  deviceInfo: deviceInfo,	// optional
  onAuthenticationChallenge: onAuthenticationChallenge
});

Restrictions

Because Authenticated Registration requires a known user, the following are not possible when this is active:

  • Anonymous Registration
  • Changes to the User Id

For further details on the platform-specific implementations, please see the relevant page:

Deactivating Authentication

If you need to de-activate authentication, this can be done in Inbox Control under Settings / General. This will revert your App Space to Basic Registration.

Suggest Edits

Known Issues

 

Visual Studio 2015 Tooling impacting Nuget installation (10 Nov 2015)

Please be aware that Visual Studio 2015 has a tooling malfunction in relation to how it loads some Nuget packages, and this is affecting the capacity to load Donky.Core.

We are investigating this matter as a Priority 1 and the immediate work-around is to load Donky.Core in either of Visual Studio 2013, or Xamarin Studio, where possible, and once it is installed, to revert to using Visual Studio 2015 if that is your platform preference.

If in the case that these work-arounds do not sufficiently resolve any difficulty in loading Donky.Core, please contact us.

We have also been advised by a Xamarin staff-member that it's likely the same as a previous bug report, now closed, that is still manifesting.

Our Nuget Bug Report is here.

We're also pleased to observe that the bug has been added to Nuget's Milestone 3.4 targets.

Suggest Edits

Reference

 

This section contains useful reference materials when working with Comapi Inbox SDKs.

Suggest Edits

Glossary

Understand those important terms used by Inbox

 
Term
Description

Active device

A mobile handset, tablet or web browser.

Anti-Spam

Security and monitoring services to ensure the safety of your content and users when using the services.

App Space

A logical partition on the Inbox network, which can contain more than one application.

API

Application Programmatic Interface, a mechanism through which multiple technologies can communicate with each other.

API Key

A client or integration authentication key, used to connect your app to your App Space and authenticate it on the network.

APNS

Apple Push Notification System is the transport method that iOS and OS X devices use to receive and display notifications. Inbox uses this in combination with secure HTTPS via the network, to guarantee delivery of your messages.

Avatar

An image used to represent a user on the network.

Campaign Builder

Inbox's enterprise class message creation, segmentation and campaign tool.

CDN

Content Delivery Network; a network of web servers across the world to deliver content reliably with low latency.

Conventional launch

A launch of the app or website that occurs if a user opens the app at any time, without clicking or tapping on a notification.

Forwarding system

A system outside the Inbox network that information can be interacted with via API.

FCM

Fire Cloud Messaging is the replacement for GCM and transport method that Android devices use to receive and display notifications. Inbox is compatible with FCM and GCM

GCM

Google Cloud Messaging is the transport method that Android devices use to receive and display notifications. Inbox uses this in combination with secure HTTPS via the network, to guarantee delivery of your messages.

Guaranteed Delivery

Messages sent through the Inbox network are tracked at every stage of the journey and reported on. Every stage of the message lifecycle is commit and confirm, meaning that unlike other providers we are able to say exactly what happened to your message and what its final disposition was.

Identity

A virtual user on the network that is used as the sender for your communications from your application to your users.

Influenced launch

A launch of the app or website that occurs when a user interactis with a notification to open an app (e.g. taps a push notification in the notification centre or lock screen).

Notification Exchange

Notifications are data entities that can be exchanged between client and server to communicate events or execute actions. Notifications can be queued either on the client or server for processing as a batch whenever notifications are exchanged.

Organic Group Chat

Once a group chat is started within Inbox, the chat is self sustaining and the original creator can leave without destroying the conversation for other users.

Push

Using push technology, Inbox's unified Simple Push sends Push Notifications to iOS and Android handsets as well as web devices.

Rich Message

Rich Messages are HTML app pages which can be sent to app users via an in-app inbox or pop-up. Messages are typically created in some sort of HTML editor, whether from raw mark-up or by using a WYSIWYG editor.

Scheduled Campaign

A campaign configured to be deployed to a group of users on a repeating schedule.

Standard Campaign

A manual campaign that is set up to be deployed at a specified time and date.

Standard Contact

A contact created within the client app that allows users to send inbound messages to your application.

Tags

Preference options for the user to select from which allow for enhanced segmentation in Campaign Builder.

Third party trigger

A custom event stored in your client or server side code, which activates a Inbox Trigger to send a message or notify another system.

Triggered Campaign

A campaign that will deploy on execution of a Inbox Trigger.

Unified Push

The ability to send one message that is received on multiple device types and operating systems and rendered the same way.

User

An end user of your app or website that is registered on the Inbox network.

User ID

A unique identifier used to distinguish a user from all others in the App Space.

Suggest Edits

Supported OS

Which OS are the Inbox SDKs compatible with.

 

Native SDKs

Operating System
Versions

iOS

7.0 or higher

Android

4.0 or higher

Javascript SDK

Browser
Minimum Version

IE

10

Firefox

19

Chrome

13

Safari

6

Opera

12.1

iOS Safari

6.1

Android Browser

4.4

Opera Mobile

12

Chrome for Andriod

42

Firefox for Android

38

IE Mobile

10

Suggest Edits

Security

API Keys, Device Security and Connectivity Details

 

At Inbox we take your security seriously. Every App Space you create provides a separate partition and provides isolation of your data.

API Keys

API Keys are used to control access to our APIs and identify the App Space that a request is intended for.

Viewing / Generating API Keys

You can view and regenerate your API Keys from Settings / Keys area within Inbox Control

Client API Keys

Client API keys are required by our SDK. These keys control access to the registration APIs which are used by our SDK to register your devices with the Inbox Network. These keys are designed to be embedded in your app.

Integration API Keys

Integration API keys provide access to our server APIs. These provide a far greater level of access than our Client API keys and should only be used by your server side applications.

Restrict access to Integration keys

You should never embed an Integration API key within your mobile app as your app bundle could easily be decompiled and the key compromised.

Key Rotation

Inbox allows you to have 2 Client and 2 Integration API Keys active at any one time. If you suspect a key has been compromised you can generate a second key and update your app to use the new key. Once released, the compromised key can be deleted, revoking access to clients that have not got the new key.

Device Secrets

In addition to the API Key, the SDK generates a secret for each device that is used to provide additional security. Subsequent access to data for this device requires the same secret to be supplied.

Encrypted Communication

All communication with Inbox websites and services is secured using 2048 bit TLS encryption.

New Device Notifications

When linking multiple devices to a single profile, Inbox can optionally send an alert to other devices informing them that a new device has been added.

Suggest Edits

Network Membership

Inbox uses the following membership model on the network

 
Item
Definition

Account

An Account is the highest level of ownership on the network. The account owns all the App Spaces, billing and contact details,

Inbox User

A Inbox user is an individual with access to Inbox Control in order to use the Inbox network and services.

App Space

An App Space represents a logical partition within Inbox. It is not be possible to communicate between App Spaces, and App Users are distinct per App Space. An App Space can contain more than one application (e.g. Android and iOS, or UK and America)

App User

A user of one of your apps, belonging to an App Space. A user may have more than one device.

Device

A Device represents an installation of your app on a device.

Suggest Edits

Push Troubleshooting

Common reasons why push tokens cannot be obtained / push fails to deliver

 

In order to get push working successfully with your app, you need to ensure setup of both the app and your App Space in Inbox Control is in sync. If you're having trouble, or seeing high push failure rate warnings in Inbox Control, please check the following.

Jump to:

Ensure your user has allowed push notifications!

If your app user does not allow push notifications (they have said No to the initial prompt or subsequently removed permissions for your app in their phone settings) then Inbox will not receive a push token and will not be able to use native channels. Your messages will still be delivered the next time the user opens the app

APNS (iOS Push)

When configuring APNS, the following factors should be considered:

  • Which APNS environment to use (Production or Sandbox)
  • The certificate / private key uploaded to Inbox Control
  • The provisioning profile used to sign your app
  • The app entitlements
  • The bundle id for your app

Generating provisioning profiles and .p12 key files for Inbox

Please see the APNS Setup page for details on how to generate the relevant files.

Development builds / Sandbox environment

During development, a development provisioning profile will be used to sign and deploy your app to a handset. To ensure push works with Inbox, ensure the following:

  1. Your provisioning profile includes the Push Notification entitlement
  2. You have uploaded a Development p12 (private key) to Inbox Control, selected the Sandbox environment and supplied the correct Bundle identifier for your app
  3. You have included an entitlements file in your app with the following entry:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>aps-environment</key>
	<string>development</string>
</dict>
</plist>

App Store / Distribution builds / Production environment

For release builds, whether for the App Store or for Enterprise Deployment, you will need to ensure the following are all correct:

  1. Your production provisioning profile includes the correct Push entitlement
  2. You have uploaded your Production p12 to Inbox Control and selected the Production environment
  3. The entitlements file in your app has the following entry:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>aps-environment</key>
	<string>production</string>
</dict>
</plist>

Network Conditions

If you are still seeing intermittent / unreliably push, please consider the following:

Network availability

APNS prefers to use cellular data and will always use this if available. If you are in an area with poor cellular coverage, then this can affect delivery. For devices without a SIM, or those where cellular data is not available, the device will attempt to use wifi instead. Wifi access requires certain ports to be open (and not routed via a proxy server) in order to get an optimal connection (see https://support.apple.com/en-gb/HT203609)

Connection reliability

Regardless of the type of network, the operating system will use a single connection to Apple. These connections are optimised for power and data efficiency and so will typically have long keep-alive times (exact details unfortunately not documented by Apple). However, depending on the network you are currently attached to, there is a chance that a router or gateway between the device and Apple’s servers will have a shorter connection timeout than the device. In this case, the network appliance kills the connection but the device does not notice until it next tries to send a keep-alive packet. At this point it will re-establish the connection, however there is a period of time where there is no active connection between the device and Apple’s servers and the push cannot be delivered.

In this instance, Apple will temporarily buffer the push until the device reconnects, however they will only buffer a single push for the device (subsequent sends would overwrite the pending data) and they do not say how long they hold it for.

Scenarios like this are of course difficult to capture empirical evidence for, but we have certainly seen higher instances of cases like this on corporate wifi networks where there are a large number of devices connecting to access points, and connections are more aggressively closed off.

With each evolution of the operating system Apple are making improvements to connection reliability and so these issues are becoming less prevalent, however given you are experiencing this only on iOS 7 devices and the behaviour seems somewhat random it would suggest that Apple a significant improvement in this area between iOS 7 and 8, but the iOS 7 devices are not reconnecting when your network (or the mobile operator if on cellular) closes the APNS connection.

Try this with specific devices if you suspect the APNS connection is broken

If you have a device that does not receive a push during test scenarios, try to put the device into flight mode for a few seconds and then turn flight mode off again. This would force the device to re-establish any connections and then the next push should arrive normally.

GCM (Android Push)

GCM requires less configuration than APNS, and typically you only use one set of configuration for development and release builds.

In order for GCM to function, you need to ensure that the Sender Id supplied to the SDK matches that configured in Inbox Control, and that the API Key configured in Inbox Control is correct for the id you supply. If these are in alignment, and the device has a GCM token, then push should function without issue under normal circumstances. See the GCM Configuration section for details on how to set these values.

Inbox Default GCM

Inbox has a default set of GCM configuration that you can use. If you make no changes to configuration with a new app space and fresh SDK install then this will be used automatically.

Slow GCM delivery

On Android push messaging is delivered by the GCM service run by Google. When manufacturers deploy Android they all tune settings in the Android kernel that control performance vs. battery consumption. This tuning when they lean too far towards battery saving can cause delays in GCM. In addition to these power settings that control sleep levels, there are known issues with older Samsung phones around its memory manager shutting down GCM’s service as detailed here.

It is possible that the issue could be related to the mobile network the phones are using, as Android sometimes does not send heartbeat packets on the connection used by GCM to receive data frequently enough to stop the mobile network closing them, therefore cutting the phone off from GCM until it hits the next heartbeat time, which can be 28 mins apart. More details here.

If you have access to the handsets causing the issues it is possible to see info and logs about the GCM connection by opening the dialler and typing:

*#*#426#*#*

This should open a hidden app to show you the GCM connection status and the logs about what has been happening, which might reveal the issue.

It is also worth checking that on the affected devices the background data for mobile networks is still enabled, see here for more details.

Suggest Edits

Client API

 

This section describes data types and patterns that are common to all the Inbox SDKs.

Inbox is the new Donky

Comapi Inbox was formally known as Donky, so some aspects of the SDK still refer to this brand for backward compatability

Suggest Edits

SDK common types

These properties are the same across all operating systems and APIs

 

UserDetails

Standard UserDetails type used by the Core SDK

Property
Type
Notes

UserId

String

The id for the App User account on the Inbox Network

IsAnonymous

Boolean

True if the device was registered anonymously, otherwise false.

DisplayName

String

EmailAddress

String

MobileNumber

String

CountryCode

String

3 letter ISO country code - must be specified if MobileNumber is used.

AvatarAssetId

String

Inbox Asset Id for a user avatar

SelectedTags

String[]

The tags a user has opted in to

AdditionalProperties

Dictionary(string, string)

Up to 50 custom properties you can attach to a user.

DeviceDetails

Standard DeviceDetails type used by the Core SDK

Property
Type
Notes

Type

String

Free-text type of the device, e.g. Phone, Browser, Toaster, Light

DeviceName

String

The name of the device, e.g. Fred's living room light

AdditionalProperties

Dictionary<string, string>

Dictionary of custom properties for a device (up to 50)

Model

String

Read-only (detected by the SDK)

OperatingSystem

String

Read-only (detected by the SDK)

OperatingSystemVersion

String

Read-only (detected by the SDK)

ServerNotification

A ServerNotification is a notification received by the SDK from the Inbox Network.

Property
Type
Notes

NotificationId

String

Unique identifier for the notification

Type

String

Data

Object

CreatedOn

DateTime

ModuleDefinition

Common definition of a Module in the Core SDK

Property
Type
Notes

Name

String

Version

String

Version of the module, e.g. 1.2.3.4

Subscription

Common definition of a notification subscription in the Core SDK

Property
Type
Notes

NotificationType

String

The type of notification being subscribed to

Handler

Function pointer / delegate

The callback to invoke when the notifications are received. Style depends on language.

ClientNotification

A ClientNotification is a notification sent from the Core SDK to the Inbox Network

Property
Type
Notes

Type

String

Data

Object

ContentNotification

ContentNotification are used to send custom content to other devices attached to the Inbox Network.

Property
Type
Notes

Audience

Defines the users you wish to target with this notification

Filters

Array of Filter

Defines characteristics about the devices targeted

Content

The content being sent.

Audience

An Audience definition is used to define the users you wish to target.

Audience Definition (abstract type)

Property
Type
Notes

Type

String

Constant value for the type of Audience

AllUsersAudience

Type value: AllUsers

No additional properties.

SpecifiedUsersAudience

Type value: SpecifiedUsers

Property
Type
Notes

Users

Array of Audience

AudienceMember

An AudienceMember is a definition of a user defined for use in an Audience

Property
Type
Notes

UserId

String

The id of the user being targeted.

TemplateData

Dictionary<string, string>

Additional data to be used in templating where supported by the Content type.

Filter

Filters are used to restrict the devices that content is delivered to.

User selection

To control the users being targeted, use the Audience definitions

Filter (abstract type)

Property
Type
Notes

Type

String

The type of Filter

OperatingSystemFilter

Filter Type: OperatingSystem

Property
Type
Notes

OperatingSystems

Array of String

Possible values: iOS, Android, Web

SpecificDeviceFilter

Filter Type: SpecificDevice

Property
Type
Notes

DeviceIds

Array of String

The ids you wish to target

Content

The Inbox Network offers functionality for delivery different types of content. This content can either be one of Inbox's native content types, or you can send custom content that is specific to your application.

Content (abstract type)

Property
Type
Notes

Type

String

The type of content being defined

NativePush

Details for native push channels

CustomNotificationContent

Used to send custom notifications via the Inbox Network.

Content Type: Custom

Property
Type
Notes

CustomType

String

The type of the custom notification - should correspond to the type subscribed to in the SubscribeToNotifications call

Data

Object

The custom data

NativePushNotificationContent

Used to send a native push only to specific devices.

Content Type: NativePush

Reliability of NativePush

While Inbox notifications are delivered reliably, when an device is online, NativePush only content is send only using the relevant push technology and so there is no guarantee that it will be delivered.

If you want guaranteed delivery, use Custom notification content in association with NativePush settings.

NativePushNotifications

As part of the definition of Content being sent over the Inbox Network you can define how the native push channels for the target device(s) should be used.

Property
Type
Note

Apns

ApnsNativePushNotification

(optional) details to use for the native push when sending to iOS devices over APNS

Gcm

GcmNativePushNotification

(optional) details to use for the native push when sending to Android devices over GCM

ApnsNativePushNotification

Push settings to use for APNS

Property
Type
Notes

Alert

ApnsNativePushNotificationAlert

Details of the visual alert to display

BadgeCount

Integer

The number to display on the apps badge

Sound

String

The local filename of the sound to play when the alert is displayed

NewContentAvailable

Boolean

Indicates whether new content is available

CustomPayload

Dictionary<String, Object>

The payload using primitive JSON types (dictionary (object), array, string, number, and Boolean)

ApnsNativePushNotificationAlert

Property
Type
Notes

Title

String

Description of the notification's purpose

TitleLocalizedKey

String

The key of a title string in the current localization settings.

TitleLocalizedArgs

Array of String

The arguments to supply to the message specified in

Message

String

The text to display for the alert

MessageLocalizedKey

String

The key of a message in the current localization settings.

MessageLocalizedArgs

String

The arguments to supply to the message specified in

LaunchImage

String

The local filename of an image to use as the launch image when an action is taken on the notification

GcmNativePushNotification

Property
Type
Notes

CollapseKey

String

Used to identify a group of push messages, so that only the last can be sent

WaitUntilActive

Boolean

Indicates if the notification should not appear until the device is activated, or send immediately

ExpiryTimeInSeconds

Seconds

The time after which the push expires and should not be sent

Payload

Dictionary<string, string>

A list of key-value pairs representing the push payload.

LocalEvent

Definition for a LocalEvent, published via the SDK for other modules to act upon.

LocalEvent (abstract type)

Property
Type
Notes

Type

string

The type of event being published

Publisher

The module publishing the event

Timestamp

DateTime

The timestamp of the event

Suggest Edits

Registration

Details of the logical Registration APIs exposed by the Inbox Network Core SDK

 

The Registration APIs help you to get the Core SDK up and running, and provide a way to manage User and Device specific data.

Register Modules and Subscriptions before calling Initialise

Be sure to register for any notifications you are interested in before calling initialise otherwise you risk a race condition where notifications are processed before your subscription is in place

RegisterModule

Called to register a module with the core. Enables a module that doesn’t use any notifications to be discoverable.

For modules not using notification subscriptions

If your module uses notification subscriptions then this call is not needed as the subscribe APIs will automatically register your modules.

Parameter
Direction
Type
Notes

Module

In

ModuleDefinition moduleDef = new ModuleDefinition("demoApp", "1.0.0.0");

DonkyCore.getInstance().registerModule(moduleDef);
DNModuleDefinition *module = [[DNModuleDefinition alloc] initWitName:@"demoApp" version:@"1.0.0.0"];
[[DNDonkyCore sharedInstance] registerModule:module];
// Create Inbox core object
var donkyCore = new DonkyCore();

var myModule = {  
  name: "testModule", 
  version:"9.9.9" 
};

donkyCore.registerModule(myModule);

SubscribeToNotifications

Adds a subscription for specific types of inbound custom ServerNotification

Parameter
Direction
Type
Notes

Module

In

Subscriptions

In

Array of Subscription

ModuleDefinition module = new ModuleDefinition("demoApp", "1.0.0.0");

Subscription<ServerNotification> subscription = 
  new Subscription<>("myNotification",
    new NotificationListener<ServerNotification>() {

      @Override
      public void onNotification(ServerNotification notification) {
        //Logic to handle data
      }

});

DonkyCore.subscribeToContentNotifications(module, subscription);
DNModuleDefinition *moduleDefinition = [[DNModuleDefinition alloc] initWitName:@"demoApp" version:@"1.0.0.0"];

DNSubscription *subscription = [[DNSubscription alloc] initWithNotificationType:@"myNotification" handler:^(id data) {
 	//Logic to handle data
}];

[[DNDonkyCore sharedInstance] subscribeToContentNotifications:moduleDefinition subscriptions:@[subscription]];
// Create instance of donkyCore
var donkyCore = new DonkyCore();

// Define a module
var myModule = {
  name: "demoApp",
  version: "1.0.0.0",
};

// Define a callback handler
function myNotificationHandler(notification) {
	// TODO: process the notification
}

// Subscribe to notification(s) - you can pass in either an array of subscription objects or just a single one
donkyCore.subscribeToContentNotifications(myModule, {notificationType: "myNotification", handler: myNotificationHandler});

SubscribeToOutboundNotifications

Adds a subscription for specific types of outbound ClientNotification

Parameter
Direction
Type
Notes

Module

In

Subscriptions

In

Array of Subscription

ModuleDefinition module = new ModuleDefinition("demoApp", "1.0.0.0");

Subscription<OutboundNotification> subscriptions = 
	new Subscription<>("myNotification",                              
		new NotificationListener<OutboundNotification>() {

      @Override
      public void onNotification(OutboundNotification notification) {
        //Logic to handle data
      }

});

DonkyCore.subscribeToOutboundNotifications(module, subscriptions);
DNModuleDefinition *moduleDefinition = [[DNModuleDefinition alloc] initWitName:@"demoApp" version:@"1.0"];
DNSubscription *subscription = [[DNSubscription alloc] initWithNotificationType:@"sendContent" handler:^(id data) {
        //Handle data here:
}];
    
[[DNDonkyCore sharedInstance] subscribeToOutboundNotifications:moduleDefinition subscriptions:@[subscription]];
// Create instance of donkyCore
var donkyCore = new DonkyCore();

// Define a module
var myModule = {
  name: "demoApp",
  version: "1.0.0.0",
};

// Define a callback handler
function myNotificationHandler(notification) {
  // TODO: process the notification
}

// Subscribe to outbound notification(s) - you can pass in either an array of subscription objects or just a single one
donkyCore.subscribeToOutboundNotifications(myModule, {notificationType: "sendContent", handler: myNotificationHandler});

Initialise

Ensures that the Core SDK is active, and that the device is registered on the network with the correct API key and able to send/receive data.

Register Subscriptions First!

Ensure you have registered any notification subscriptions prior to calling Initialise

Parameter
Direction
Type
Notes

ApiKey

In

String

The Client ApiKey for your Inbox App Space

UserDetails

In (optional)

DeviceDetails

In (optional)

AppVersion

In (optional)

String

The version of your app in the form a.b.c.d

ResultHandler

In

Callback

Called when the operation is complete. Must be provided so that you can handle any validation failures.

public class MyApplication extends Application {

    @Override
    public void onCreate()
    {
        super.onCreate();
				DonkyCore.initialiseDonkySDK(this,">>ENTER API KEY HERE<<",
        	new DonkyListener() /*set callback handlers*/ );
    }
  
}
 [[DNDonkyCore sharedInstance] initialiseWithAPIKey:@">>ENTER API KEY HERE<<"];
// Create Inbox core object
var donkyCore = new DonkyCore();

donkyCore.initialise({
    apiKey: ">>ENTER API KEY HERE<<",    
    resultHandler: function(result){
      // TODO: your app logic ...      
    }
});

IsRegistered

The IsRegistered call will return whether the current device has a valid Inbox Network registration or not.

Parameter
Direction
Type
Notes

(result)

Out

Boolean

True if you have a valid registration with the Inbox Network, otherwise False.

boolean isRegistered = DonkyAccountController.getInstance().isRegistered();
BOOL isRegistered = [DNAccountController isRegistered];
// Create Inbox core object
var donkyCore = new DonkyCore();

var isRegistered = donkyCore.donkyAccount.isRegistered();

GetRegistrationDetails

Gets details about the current Inbox Network registration.

Parameter
Direction
Type

UserDetails

Out

DeviceDetails

Out

RegistrationDetails details = 
  DonkyAccountController.getInstance().getRegistrationDetails();
DNRegistrationDetails *registrationDetails = [DNAccountController registrationDetails];
// Create Inbox core object
var donkyCore = new DonkyCore();

var registrationDetails = donkyCore.donkyAccount.getRegistrationDetails();

UpdateRegistrationDetails

Updates custom data about the user or device for the current Inbox Network registration.

Parameter
Direction
Type
Notes

UserDetails

In (optional)

DeviceDetails

In (optional)

ResultHandler

In

Callback

Required so that validation failures can be handled.

UserDetails userDetails = new UserDetails();
	userDetails.
  	setUserId("johnsmith").
    setUserFirstName("Johnny").
    setUserLastName("Smith").
    setUserMobileNumber("07979497300").
    setUserCountryCode("GBR").
    setUserEmailAddress("john.smith@gmail.com").
    setUserDisplayName("Johnny Smith").
    setUserAdditionalProperties(new TreeMap<String, String>()).
    setSelectedTags(new LinkedHashSet<String>());

DeviceDetails deviceDetails = 
  new DeviceDetails("Nexus", "Johny's Nexus", new TreeMap<String, String>());

DonkyAccountController.getInstance().
  updateRegistrationDetails(userDetails, deviceDetails, 
		new DonkyListener() /*set callback handlers*/);
DNUserDetails *userDetails = [[DNUserDetails alloc] initWithUserID:@"johnsmith" displayName:@"Johnny Smith" emailAddress:@"john.smith@gmail.com" mobileNumber:@"07979497300" countryCode:@"GBR" firstName:@"Johnny" lastName:@"Smith" avatarID:nil selectedTags:@[] additionalProperties:@{}];

DNDeviceDetails *deviceDetails = [[DNDeviceDetails alloc] initWithDeviceType:@"iPhone" name:@"Johny's iPhone" additionalProperties:@{}];
    
[DNAccountController updateRegistrationDetails:userDetails deviceDetails:deviceDetails success:^(NSURLSessionDataTask *task, id responseData) {
  //Completion here
} failure:^(NSURLSessionDataTask *task, NSError *error) {
  //Error here
}];
// Assume we have already called initialise and it succeeded

var settings = {
  userDetails: {
    id: "johnsmith",
    displayName: "Johnny Smith",
    firstName: "John",
    lastName: "Smith",
    emailAddress: "john.smith@gmail.com",
    countryCode: "GBR",
    phoneNumber: "07979497300",
  },
  deviceDetails: {
    type: "genericWebApp",
    name: "generic",
  }
};

donkyCore.donkyAccount.updateRegistrationDetails(settings, function(result) {  
  if(result.succeeded){
    // update has succeeded 
  } else{
		// update has failed, probably a validation error ...
    alert("updateRegistrationDetails failed ...\n\n" + JSON.stringify(result));    
  }
});       

ReplaceRegistration

Replaces the current registration with different details.

Only for multi-user scenarios

This operation does not update the current registration, it clears down local state and registers again with the details provided, leaving the original registration intact on the Inbox Network. This is intended for use with multi-user scenarios.

Parameter
Direction
Type
Notes

UserDetails

In (optional)

DeviceDetails

In (optional)

ResultHandler

In

Callback

Required to handle any validation failures.

UserDetails userDetails = new UserDetails();
	userDetails.
  	setUserId("johnsmith").
    setUserFirstName("Johnny").
    setUserLastName("Smith").
    setUserMobileNumber("07979497300").
    setUserCountryCode("GBR").
    setUserEmailAddress("john.smith@gmail.com").
    setUserDisplayName("Johnny Smith").
    setUserAdditionalProperties(new TreeMap<String, String>()).
    setSelectedTags(new LinkedHashSet<String>());

DeviceDetails deviceDetails = 
  new DeviceDetails("Nexus", "Johny's Nexus", new TreeMap<String, String>());

DonkyAccountController.getInstance().replaceRegistration(
  userDetails, deviceDetails, 
  	new DonkyListener() /*set callback handlers*/);
DNUserDetails *userDetails = [[DNUserDetails alloc] initWithUserID:@"johnsmith" displayName:@"Johnny Smith" emailAddress:@"john.smith@gmail.com" mobileNumber:@"07979497300" countryCode:@"GBR" firstName:@"Johnny" lastName:@"Smith" avatarID:nil selectedTags:@[] additionalProperties:@{}]

DNDeviceDetails *deviceDetails = [[DNDeviceDetails alloc] initWithDeviceType:@"iPhone" name:@"Joe's iPhone" additionalProperties:@{}];

[DNAccountController replaceRegistrationDetailsWithUserDetails:userDetails deviceDetails:deviceDetails success:^(NSURLSessionDataTask *task, id responseData) {
  //Completion here
} failure:^(NSURLSessionDataTask *task, NSError *error) {
  //Error here
}];
var settings = {
  userDetails: {
    id: "joebloggs",
    displayName: "Joe Bloggs",
    firstName: "Joe",
    lastName: "Bloggs",
    emailAddress: "joe.bloggs@gmail.com",
    countryCode: "GBR",
    phoneNumber: "07979497200",
  },
  deviceDetails: {
    type: "genericWebApp",
    name: "generic",
  }
};

donkyCore.donkyAccount.replaceRegistration(settings, function(result) {  
  if(result.succeeded){
    // update has succeeded 
  }else{
		// update has failed, probably a validation error ...
    alert("replaceRegistration failed ...\n\n" + JSON.stringify(result));    
  }
});  
Suggest Edits

Notification

Common operations for sending notifications via the Inbox Network

 

QueueContentNotifications

Allows you to queue content notifications for sending via the Inbox Network.

Parameter
Direction
Type
Notes

Notifications

In

The notifications being sent.

JSONObject jsonObject = new JSONObject();
jsonObject.put("message", "hello world");

List<ContentNotification> contentNotifications = new LinkedList<>();
contentNotifications.add(
  new ContentNotification("joebloggs", "chatMessage", jsonObject));

DonkyNetworkController.getInstance().queueContentNotifications(
  contentNotifications);
 DNContentNotification *sendMessage = [[DNContentNotification alloc] initWithUsers:@[@"joebloggs", @"johnsmith"] customType:@"chatMessage" data:@{@"message" : @"hello world"}];
    
[[DNNetworkController sharedInstance] queueContentNotifications:@[sendMessage]];
// Create a content notification
var notification1 = donkyCore.createContentNotificationForSpecifiedUsers(
  ["joebloggs","johnsmith"],
  "chatMessage", 
  {
    "message": "hello"
  }
);
// Queue it - can pass in an array of notifications or just a single object
donkyCore.queueContentNotifications([notification]);

GetServerNotification

Gets a single ServerNotification by id, either from locally stored data or the Inbox Network as required.

Parameter
Direction
Type
Notes

NotificationId

In

String

The server notification id

Notification

Out

DonkyNetworkController.getInstance().
  getServerNotification("775ebba2-9af5-4b3e-97ec-7e8949345e20", 
  	new DonkyResultListener<ServerNotification>() /*set callback handlers*/);
[[DNNetworkController sharedInstance] serverNotificationForId:@"775ebba2-9af5-4b3e-97ec-7e8949345e20" success:^(NSURLSessionDataTask *task, id responseData) {
	DNServerNotification *serverNotification = responseData;
} failure:^(NSURLSessionDataTask *task, NSError *error) {
        
}];
donkyCore.donkyNetwork.getServerNotification("775ebba2-9af5-4b3e-97ec-7e8949345e20", function(notification){
	if(notification !== null){
		alert(JSON.stringify(notification));
  }
});            

SendContentNotifications

Sends notifications via the Inbox Network immediately if possible.

Parameter
Direction
Type

Notifications

In

ResultHandler

In

Callback

JSONObject jsonObject = new JSONObject();
jsonObject.put("message", "hello world");

List<String> recipients = new LinkedList<>();
recipients.add("johnsmith");
recipients.add("joebloggs");

ContentNotification contentNotification =
	new ContentNotification(recipients, "chatMessage", jsonObject);

DonkyNetworkController.getInstance().sendContentNotification(
	contentNotification,
  new DonkyListener() /*set callback handlers*/);
 DNContentNotification *contentNotification4 = [[DNContentNotification alloc] initWithUsers:@[@"johnsmith"] customType:@"chatMessage" data:@{@"message" : @"hello world"}];
        
[[DNNetworkController sharedInstance] sendContentNotifications:@[contentNotification] success:^(NSURLSessionDataTask *task, id responseData) {
	//Notification sent.
} failure:^(NSURLSessionDataTask *task, NSError *error) {

}];
// Create a content notification
var notification1 = donkyCore.createContentNotificationForSpecifiedUsers(
  ["joebloggs","johnsmith"],
  "chatMessage", 
  {
    "message": "hello"
  }
);
// Send it - can pass in an array of notifications or just a single object
// callback function os optional
donkyCore.sendContentNotifications([notification], function(){
	alert("notification sent");
});

Synchronise

Synchronises notifications with the Inbox Network, sending anything that has been queued and downloading any pending server notifications.

Parameter
Direction
Type
Notes

Callback

In (optional)

Callback

Optional callback for callers that need to know when the operation has completed.

DonkyNetworkController.getInstance().synchronise(
  new DonkyListener() /*set callback handlers*/);
//Fire and forget
[[DNNetworkController sharedInstance] synchronise];

//Synchronise with call back
[[DNNetworkController sharedInstance] synchroniseSuccess:^(NSURLSessionDataTask *task, id responseData) {
        
} failure:^(NSURLSessionDataTask *task, NSError *error) {
        
}];
donkyCore.donkyNetwork.synchronise(function(){
	alert("synchronised");
}); 
Suggest Edits

Logging

Details about the Core SDK logging capabilities

 

The Core SDK provides logging functionality that can be used within other modules or by your app. The SDK maintains a rolling log file so you don't need to worry about space.

Requesting Logs

You can request the logs from a device using Inbox Control to help diagnose issues.

WriteLog

Writes information to the log.

Parameter
Direction
Type
Notes

LogLevel

In

LogLevel (enum)

Error | Warning | Info | Debug

Message

In

String

The message to log. Can be templated.

Arguments

In (optional)

Array of object

Arguments for a templated message

Exception

In (optional)

Exception

If logging an exception / error, the exception object can be passed to the logging API and will be included in the message.

DLog log = new DLog("TAG");
        
log.info("message");
        
log.debug("message");
        
log.error("message");
        
log.error("message", new Exception());
        
log.warning("message");
        
log.sensitive("message");
//Logs can be enabled/disabled in the DNConfiguration.plist.

DNInfoLog(@"An Info Log");

DNWarningLog(@"A warning log.");

DNSensitiveLog(@"Sensitive logs are only output when debugger is attached.");

DNErrorLog(@"An Error Log");
    
DNDebugLog(@"Debug logs are only output when debugger is attached.");
// Javascript SDK has helper functions for each log type so you don't need to specify the Enum

donkyCore.donkyLogging.debugLog("A debug message");

donkyCore.donkyLogging.infoLog("An Info mesage");

donkyCore.donkyLogging.warnLog("A Warning message");

// Note: logging an Error will cause the current debug log to be sent to Inbox network  
donkyCore.donkyLogging.errorLog("An error message");


GetLog

Gets the current contents of the log file.

Parameter
Direction
Type
Notes

Log

Out

String

The log content

String currentLog = DonkyLoggingController.getInstance().getLog();
//The most recent log data:
NSString *currentLog = [DNLoggingController activeDebugLog];

//The archived log detail, logs are archived once they hit a file size limit. Only 1 archived log file is mantained. 
NSString *archivedLog = [DNLoggingController archivedDebugLog];
var currentLog = donkyCore.donkyLogging.getLog();

SubmitLog

Manually submit the log file to the Inbox Network.

Log Viewer

Submitted log files are stored for 30 days on the Inbox Network and can be viewed in Inbox Control

DonkyLoggingController.getInstance().submitLog( 
  new DonkyListener() /*set callback handlers*/);
[DNLoggingController submitLogToDonkyNetworkSuccess:^(NSURLSessionDataTask *task, id responseData) {
	//Success here
} failure:^(NSURLSessionDataTask *task, NSError *error) {
	//Error here
}];
donkyCore.submitLog(function(result) {                    
  if(result.succeeded){
  	alert("logfile submitted");
  }else{
    alert("Failed to submit logfile");
  }    
});            
Suggest Edits

Module Info & Versions

APIs providing details about registered modules and versions

 

GetRegisteredModules

Gets details of all modules registered with the SDK.

Parameter
Direction
Type
Notes

Modules

Out

List<ModuleDefinition> moduleDefinitions =
  DonkyCore.getInstance().getRegisteredModules();
NSArray *registeredModules = [[DNDonkyCore sharedInstance] allRegisteredModules];
var registeredModules = donkyCore.getRegisteredModules();

IsModuleRegistered

Checks for a registration of the specified module.

Parameter
Direction
Type
Notes

Name

In

String

The name of the module to check for

MinimumVersion

In (optional)

String

If specified, the registered version number will be checked against the supplied value

(result)

Out

Boolean

True if a matching module is found, otherwise false.

DonkyCore.getInstance().registerModule(
  new ModuleDefinition("someModule","1.0.1.0"));

//Returns true as module version is lower than registered module
boolean isModuleRegistered = DonkyCore.getInstance().isModuleRegistered("someModule","1.0.0.0");
  
//Returns false as module version is higher than registered module
boolean isModuleRegistered = DonkyCore.getInstance().isModuleRegistered("someModule","2.0.0.0");
DNModuleDefinition *module = [[DNModuleDefinition alloc]initWitName:@"appDemo" version:@"1.0.1.0"];
[[DNDonkyCore sharedInstance] registerModule:module];

//Returns true as module version is lower than registered module
BOOL moduleRegistered = [[DNDonkyCore sharedInstance] isModuleRegistered:@"appDemo" moduleVersion:@"1.0.0.0"];
  
//Returns false as module version is higher than registered module
BOOL moduleRegistered = [[DNDonkyCore sharedInstance] isModuleRegistered:@"appDemo" moduleVersion:@"2.0.0.0"];

//Also returns true, Inbox can handle version numbers up to 4 characters:
BOOL moduleRegistered = [[DNDonkyCore sharedInstance] isModuleRegistered:@"appDemo" moduleVersion:@"1.0"]
var module = {  
  name: "testModule", 
  version:"1.2.3.4" 
};

donkyCore.registerModule(module);

// returns true
var registered = donkyCore.isModuleRegistered("testModule");

// returns true as version number smaller
registered = donkyCore.isModuleRegistered("testModule", "1.0.0.0");

// returns false as version number greater
registered = donkyCore.isModuleRegistered("testModule", "2.0.0.0");
Suggest Edits

Local Services & Events

APIs for registering services, and event pub/sub

 

The Core SDK offers functionality that can be used to connect modules in a loosely coupled manner.

Services

The SDK allows you to register services - basically instances of objects that can be accessed by consumers. This can be used for inter-module communication.

Local Events

Events are used to notify other modules (or your app) of certain things happening, for example:

  • Registration Changed
  • Information written to log
  • Network state changed

RegisterService

Registers a service with the Core SDK.

Parameter
Direction
Type
Notes

Type

In

Type

The type of the service being registered (e.g. typeof(IContactManager) ). Can be a generic parameter if the language supports it.

Instance

In

Object

Instance of the specified type to register

// Controller instance you want to pass between modules
Object service = new Object();

DonkyCore.getInstance().registerService("serviceType", service);
[[DNDonkyCore sharedInstance] registerService:@"Core Service" service:^(id data) {
	//Service:
}];
// Create an object with a function test()

var service = {
  test:function() {
    return "HELLO";
  }
};

// Register it
donkyCore.registerService("test", service);

GetService

Gets an instance of the specified service (or null if not found).

Parameter
Direction
Type
Notes

Type

In

Type

The type of the required service. Can be a generic parameter if the language supports it.

(result)

Out

Object

Instance of the requested type.

// Controller instance you want to pass between modules
Object service = new Object();

DonkyCore.getInstance().registerService("serviceType", service);

DonkyCore.getInstance().getService("serviceType");
[[DNDonkyCore sharedInstance] registerService:@"Core Service" instance:self];

id service = [[DNDonkyCore sharedInstance] serviceForType:@"Core Service"];
// Create service
var service = {
  test:function() {
    return "HELLO";
  }
};

// Register it
donkyCore.registerService("test", service);

// get it
var got = donkyCore.getService("test");

// if service is returned call the method
if(got !== null) {
  // should alert HELLO
  alert(got.test());
}

UnregisterService

Unregisters services of the specified type.

Parameter
Direction
Type
Notes

Type

In

Type

The type to unregister. Can be a generic parameter if the language supports it.

DonkyCore.getInstance().unregisterService("serviceType");
[[DNDonkyCore sharedInstance] unRegisterService:@"Unit Test"];
// Create service
var service = {
  test:function() {
    return "HELLO";
  }
};

// Register it
donkyCore.registerService("test", service);

// Unregister it
donkyCore.unregisterService("test");

SubscribeToLocalEvent

Subscribes the caller to a type of local event.

Parameter
Direction
Type
Notes

EventType

In

String

The type of event to subscribe to

Handler

In

Callback

Callback to invoke when the event is raised

// e.g. subscribe to the LogMessage event

DonkyCore.subscribeToLocalEvent(
  new DonkyEventListener<LogMessageEvent>(LogMessageEvent.class) {
            
    @Override
    public void onDonkyEvent(LogMessageEvent event) {
			// event occurred
    }

});
DNLocalEventHandler handler = ^(DNLocalEvent *event) {
};

[[DNDonkyCore sharedInstance] subscribeToLocalEvent:@"Test Event" handler:handler];
// Sample callback function
function LogCallback(event){
  // Just log out the event
  console.log(JSON.stringify(event));
}

// Subscribe to the LogMessage event
donkyCore.subscribeToLocalEvent("LogMessage", LogCallback);

Don't miss any event!

To ensure you don't miss any events please ensure you subscribe to the local events you ar e interested in prior to initialising the modules.

UnsubscribeFromLocalEvent

Unsubscribes a handler for a local event type.

Parameter
Direction
Type
Notes

EventType

In

String

The type of event to subscribe to

Handler

In

Callback

Callback to invoke when the event is raised

DonkyEventListener<LogMessageEvent> listener = 
  new DonkyEventListener<LogMessageEvent>(LogMessageEvent.class) {
            
    @Override
    public void onDonkyEvent(LogMessageEvent event) {
			// event occurred
    }

};

DonkyCore.subscribeToLocalEvent(listener);

DonkyCore.unsubscribeFromLocalEvent(listener);
DNLocalEventHandler handler = ^(DNLocalEvent *event) {
};

[[DNDonkyCore sharedInstance] subscribeToLocalEvent:@"Test Event" handler:handler];

[[DNDonkyCore sharedInstance] unSubscribeToLocalEvent:@"Test Event" handler:handler];
// Sample callback function
function LogCallback(event){
  // Just log out the event
  console.log(JSON.stringify(event));
}

// Subscribe to the LogMessage event
donkyCore.subscribeToLocalEvent("LogMessage", LogCallback);

// Unsubscribe from that event 
donkyCore.unsubscribeFromLocalEvent("LogMessage", LogCallback);

PublishLocalEvent

Publishes a local event to all registered subscribers.

Parameter
Direction
Type
Notes

Event

In

The event to publish

public class MyEvent extends LocalEvent {
 
  Object someData;
        
  public MyEvent(Object someData) {
    super();
    this.someData = someData;
  }

  public Object getSomeData() {
    return someData;  
  }
}

DonkyCore.publishLocalEvent(new MyEvent());
DNLocalEvent *localEvent = [[DNLocalEvent alloc] initWithEventType:@"Test Event" publisher:NSStringFromClass([self class]) timeStamp:[NSDate date] data:@{}];
    
[[DNDonkyCore sharedInstance] publishEvent:localEvent];
function LocalEventTestCallback(event){
	console.log(JSON.stringify(event));
}

donkyCore.subscribeToLocalEvent("LocalEventTest", LocalEventTestCallback);

donkyCore.publishLocalEvent({ 
  type : "LocalEventTest", 
  data: {
    param1:"hello",
    param2:"world"
  } 
});
 

This is the SDK reference for the native Android Comapi Inbox SDK.

Inbox is the new Donky

Comapi Inbox was formally known as Donky, so some aspects of the SDK still refer to this brand for backward compatability

Maven

Bintray Maven Repository is a repository for build artifacts. You can find Inbox Core and Module libraries here.
Bintray repository is also synced with Maven Central Repository.

Gradle / JCenter

Bintray's JCenter is a collection of all Maven Open Source Software artifacts. By default Android Studio adds this repository into generated build.gradle files. You can easily browse the repository in Android Studio and add Inbox Core and Module libraries along with their dependencies.

Android Open Source Project (AOSP)

In a case when you are developing for Open Source Android devices, without pre-installed Google Play Services library, Play Store etc. we have a good news for you.

The Inbox SDKs do not rely solely on Google Cloud Messaging. We are using GCM push only to trigger Network communication which results in full sync. This means that if there is no GCM connection your application can still receive and send messages to Inbox Network and another devices.

Polling frequency is set to 5 minutes by default when the app is open, which we think is a good balance between battery/data usage vs. responsiveness. You can increase the frequency by calling 'synchronise' method manually.

Third Party Dependencies

The Android modular SDK has the following third party dependencies:

  • Android SDK dependencies:
    • Appcompat-v7-22.2.1
    • Support-annotations-22.2.1
    • Support-v4-22.2.1
  • 3rd party dependencies:
    • Gson-2.3.1
    • Hamcrest-core-1.3 (not a direct dependency)
    • Okhttp-2.4.0
    • Okkio-1.4.0 (not a direct dependency)
    • Retrofit-1.9.0
    • de.hdodenhof:circleimageview:1.3.0 (dependency in UI modules)
Suggest Edits

GCM Configuration

 

If you are not using your own GCM configuration in your application already, Inbox can use our own default GCM project. In this case no additional GCM configuration is needed and the SDK will work straight away delivering push messages via the Inbox Network.

You can find more useful information for configuration of GCM here.

However if your application needs to register using custom GCM sender ID you will need to follow the steps below:

1. Go to the App Space settings.

Click on PUSH tab.

2. Provide Sender ID and API key

Provide Sender ID and API Key for your GCM project.

How to find your Sender ID and API Key in FCM

  1. Open the Firebase Console
  2. If you haven’t created an API project yet, click Create Project
  3. Find your Firebase project and click Project Name
  4. Click the setting icon and select Project Settings from the cog menu
  1. Select Could Messaging tab and copy the Legacy server key (highlighted in yellow below) and Sender ID (highlighted in green below) from the page and paste it into the Configure GCM pop-up in Inbox Control
Copying your GCM details from FCM Control Panel

Copying your GCM details from FCM Control Panel

Enter your GCM details into Inbox Control

Enter your GCM details into Inbox Control

3. Set Sender ID in your application

Set Sender ID in your application resources in res/values/strings.xml

<string name="GcmSenderId">YOUR_SENDER_ID</string>

Done.

Custom GCM settings are now configured.

4. If you are using GcmListenerService in your app

ver. 2.4.0.0

Relevant for Inbox ver. 2.4.0.0 and above.

For existing apps that extend a WakefulBroadcastReceiver, Google recommends migrating to GCMReceiver and GcmListenerService.

No GcmListenerService

If you don't use GcmListenerService in your application you can ignore this paragraph.

GcmListenerService

If you already implemented the GcmListenerService in your application project this will make the GcmListenerService implemented in SDK stop receiving messages.

In that case please call DonkyGcmIntentService.onDonkyMessageReceived in your onMessageReceived callback:

public class MyGcmIntentService extends GcmListenerService {
    @Override
    public void onMessageReceived(String from, Bundle data) {
      DonkyGcmIntentService.onDonkyMessageReceived(from, data);
    }
}

This will trigger processing of Inbox related GCM messages.

InstanceID

Please remember that Inbox SDK stores the GCM token in the application InstanceID singleton. If you call InstanceID.deleteInstanceID() it will revoke GCM token used by the SDK. In that case you will need to call DonkyGcmController.getInstance().registerPush to register a new GCM token.

Suggest Edits

Register Users

How to initialise core and register users to the Inbox network

 

In order to use any Inbox SDK modules, you must first initialise the core module, and register a user on the network. The most common way to do this is to:

  1. Register an anonymous user as soon as the website is launched / app is opened for the first time
  2. When the user completes your sign in or registration process, you can call "Update User" and update their information with the new "Known" details
  3. If the user logs out of your app, you can use "Replace registration" to remove the known user information and replace with an anonymous user

At any time, you can also update the user's additional properties, or tags. For more information on doing that, see below.

Download the core SDK

GitHub

cd "Directory where you wish to clone the repository"

git clone git@github.com:Donky-Network/DonkySDK-Android-Modular.git

Gradle

// Add jCenter repository to build.gradle. Default for Android Studio.
repositories {
  jcenter()
}

// Add core aar dependancy to build.gradle.
dependencies {
    compile 'net.donky:donky-core:2.7.0.3'
}

1. Register an Anonymous user

When the user first downloads your app or opens your website, the core SDK should be initialised with no information, this will automatically create an anonymous user on the network.

public class MyApplication extends Application {

    @Override
    public void onCreate()
    {
        super.onCreate();
      
        DonkyCore.initialiseDonkySDK(this,">>ENTER API KEY HERE<<",
          new DonkyListener() /*set callback handlers*/);
    }
  
}

Important

The initialisation needs to be performed in onCreate method of Android Application class and
the initialisation of Core Module needs to go after the initialisation of all the other modules.

Analytics

Please make sure you initialise the Analytics module to report app session analytics to Inbox. Find out more in the documentation.

GCM

If you don't receive message notifications when the application is closed

  • Check if you initialising Inbox SDK in a class that is extending Application class and if this class is declared in Android Manifest file:
    <application android:name="MyApplicationClassName"

  • If you are using your own GCM project please see GCM Configuration page for more details how to set up your project for the SDK

Log level

You can increase the log level by adding the following snippet to the strings.xml file

<string name="LoggingEnabled">true</string>
<string name="ErrorLogsEnabled">true</string>
<string name="WarningLogsEnabled">true</string>
<string name="InfoLogsEnabled">true</string>
<string name="DebugLogsEnabled">true</string>
<string name="SensitiveLogsEnabled">true</string>

2. Update details to become Known

When the user completes a sign in or sign up process, you can update their information and turn them into a Known user. This will update their information in the Inbox Portal, and will connect any other devices they have to the same user.

UserDetails userDetails = new UserDetails();
	userDetails.
  	setUserId("johnsmith").
    setUserFirstName("Johnny").
    setUserLastName("Smith").
    setUserMobileNumber("07979497300").
    setUserCountryCode("GBR").
    setUserEmailAddress("john.smith@gmail.com").
    setUserDisplayName("Johnny Smith").
    setUserAdditionalProperties(new TreeMap<String, String>()).
    setSelectedTags(new LinkedHashSet<String>());

DonkyAccountController.getInstance().updateUserDetails(userDetails, 
	new DonkyListener() /*set callback handlers*/ );

You will at this point need to provide a user ID, which can be used to uniquely recognise this user in your App Space, we recommend, email, mobile number or username, so that the user can recognise their identifier later and to keep it synchronised with how you recognise your users normally.

Maintain User ID

We recommend that you do not allow updates to the user ID to ensure that it is consistent throughout the system and can be relied upon by other modules as the unique identifier for a user.

Obtain registration details

To get the user details for current registration

UserDetails user = DonkyAccountController.getInstance().getCurrentDeviceUser();

Update tags

Tags can be used to mark a user's preferences in the app, you can update these at any time for either an anonymous or known user. The information will be available for segmentation on Inbox messaging.

To obtain the current tag selections

DonkyNetworkController.getInstance().getTags(new DonkyResultListener<List<TagDescription>>() /*set callback handlers*/);

To update tag selections

DonkyNetworkController.getInstance().getTags(
  
  new DonkyResultListener<List<TagDescription>>() {
    
    @Override
    public void success(List<TagDescription> tags) {

      for (TagDescription tagDescription : tags) {

        tagDescription.setSelected(boolean);

        DonkyNetworkController.getInstance().updateTags(tags, 
          new DonkyListener() /*set callback handlers*/);

      }

    }

    @Override
    public void error(DonkyException donkyException, 
                      Map<String, String> validationErrors)	{}
});

Update additional properties

You can use additional properties to pass through and additional information to the network about your user that is not supported by the default profile fields. This could be used to send us any other information that you capture on completion of forms or registration that might be useful for profiling and segmentation.

To obtain the current additional properties for a user

DonkyAccountController.getInstance().getCurrentDeviceUser().getUserAdditionalProperties();

To update additional properties

TreeMap<String, String> properties = new TreeMap<>();
properties.put("testKey", "testValue");

UserDetails userDetails = DonkyAccountController.getInstance().getCurrentDeviceUser();
userDetails.setUserAdditionalProperties(properties);
  
DonkyAccountController.getInstance().updateUserDetails(userDetails, 
	new DonkyListener() /*set callback handlers*/);

To use Inbox's sequencing module to update additional properties

Include the sequencing module

dependencies {
    compile 'net.donky:donky-module-sequencing:2.7.0.3'
}

Initialise in Android Application class before Core Module initialisation.

public class MyApplication extends Application {

    @Override
    public void onCreate()
    {
        super.onCreate();

        DonkySequencing.initialiseDonkySequencing(this, 
            new DonkyListener() /*set callback handlers*/);

        DonkyCore.initialiseDonkySDK(this,">>ENTER API KEY HERE<<", 
            new DonkyListener() /*set callback handlers*/);

    }
}

And then update additional properties

DonkySequenceAccountController.getInstance().setAdditionalProperties(properties, 
	new DonkySequenceListener() /*set callback handlers*/);
Suggest Edits

Delayed Registration

How to delay Donky registration until you need it

 

If you don't want to register automatically when you are initialising SDK for the first time you will need to call initialise method with additional shouldRegisterUser='false' parameter.

1. Initialise

public class MyApplication extends Application {

    @Override
    public void onCreate()
    {
        super.onCreate();
      
        DonkyCore.initialiseDonkySDK(this,">>ENTER API KEY HERE<<",
          false /* do not attempt to register user */,
          new DonkyListener() /*set callback handlers*/);
    }
  
}

Important

The initialisation needs to be performed in onCreate method of Android Application class and
the initialisation of Core Module needs to go after the initialisation of all the other modules.

Initialisation done.

SDK is initialised but if there is no user registration present on the client, the SDK will not register a new one automatically.

2. Register user

If no user was registered on a client and SDK has been successfully initialised you can start registration process by calling

UserDetails userDetails = new UserDetails();
  userDetails.setUserId("some_id");
  userDetails.setUserDisplayName("someone");

DonkyAccountController.getInstance().register(userDetails, null,
  new DonkyListener() /* set callback handlers */);

Registration done.

SDK is initialised, user and device are registered on the Inbox Network and the SDK is ready to use.

Suggest Edits

Inbox Authentication

How to use Inbox Android SDK in a secured way

 

Below you will find a description how to delegate the authentication process from Inbox. You can delegate authentication responsibility to another trusted system that can issue a JWT containing the required information.

1. Provide a code to obtain a Token

Inbox is using the industry standard JSON Web Token JWT.

In order to be able to register to Inbox Network in a secured way you need to provide a callback that will respond to authentication challenges and provide a valid JWT token. The nonce need to be included into scopes your application requests from authorisation provider.

class Authenticator implements DonkyAuthenticator {

	@Override
  public void onAuthenticationChallenge(DonkyAuthClient authClient,
                                        ChallengeOptions options) {
    
    /**
     * Obtain JWT token based on a given nonce and return the value to
     * authClient instance. At this point the user authentication process will 
     * be able to continue automatically.
     * options.getNonce() for nonce to be included in token
     * options.getForUserId() expected user id when re-authenticating
     */
     authClient.authenticateWithToken("TEST_TOKEN");
   }

}

2. Initialise Core with above authorisation callback

Option a. Initialise the Core SDK with an instance of the DonkyAuthenticator class you created in paragraph 1.

public class MyApplication extends Application {

    @Override
    public void onCreate()
    {
        super.onCreate();
      
        DonkyCore.initialiseDonkySDK(this,">>ENTER API KEY HERE<<",
          new Authenticator() /* extends DonkyAuthenticator */, 
          new DonkyListener() /*set callback handlers*/);
    }
  
}

After calling this method the SDK client is locked in authenticated mode of operation until it will be initialised again in standard mode when the application is being created.

If a correct token is provided the SDK will be initialised and registered with an user. If user registration exist on the client the SDK will resume operation with saved data.

Important

The initialisation needs to be performed in onCreate method of Android Application class and
the initialisation of Core Module needs to go after the initialisation of all the other modules.

Initialised and registered.

At this point the SDK is initialised and registered with an user with id deduced from token provided in authClient.authenticateWithToken.

Option b. If you want to initialise but don't make any attempt to registered a new user change the initialisation call by adding an additional flag:

public class MyApplication extends Application {

    @Override
    public void onCreate()
    {
        super.onCreate();
      
        DonkyCore.initialiseDonkySDK(this,">>ENTER API KEY HERE<<",
          new Authenticator() /* extends DonkyAuthenticator */,
          false /* don't attempt new user registration */,
          new DonkyListener() /*set callback handlers*/);
    }
  
}

Again if user registration exist on the client the SDK will resume operation with saved data.

Initialised but not registered (if registerAuthenticated was never called).

At this point the SDK is initialised however if no user registration is present the SDK will not register a new user.

3. Register an authenticated user

If you have followed option b in paragraph 2 you will still need to register a user enabling SDK for the first time.

Option a. To register a user for the first time you need to call

DonkyAccountController.getInstance().registerAuthenticated(
  new DonkyListener() /*set callback handlers*/);

This method will authenticate the user with token you provided (paragraph 1) and register a new user or a new device for old user account on the Inbox Network.

Done

You integrated the Modular Inbox SDK in a secured, authenticated mode.

Option b. To register a user with some additional details you can pass them in a following way:

UserDetails userDetails = new UserDetails();
	userDetails.
    setUserFirstName("Johnny").
    setUserLastName("Smith").
    setUserMobileNumber("07979497300").
    setUserCountryCode("GBR").
    setUserEmailAddress("john.smith@gmail.com").
    setUserDisplayName("Johnny Smith").
    setUserAdditionalProperties(new TreeMap<String, String>());

DeviceDetails deviceDetails = new DeviceDetails("name", "phone", null);

DonkyAccountController.getInstance().registerAuthenticated(userDetails, deviceDetails,"1.0",
  new DonkyListener() /*set callback handlers*/);

Inbox user id

In authenticated mode the user id cannot be modified. The value for user id in registration/update calls will be ignored.

Suggest Edits

Analytics & Automation

 

Analytics

GitHub

You can use File > New > Import Module in Android Studio to import this module with all its dependencies into your project.

Gradle

// Add jCenter repository to build.gradle. Default for Android Studio.
repositories {
  jcenter()
}

// Add aar dependancy to build.gradle.
dependencies {
    compile 'net.donky:donky-module-analytics:2.7.0.3'
}

Initialise

public class MyApplication extends Application {

    @Override
    public void onCreate()
    {
        super.onCreate();
      
      	DonkyAnalytics.initialiseAnalytics(this,
        	new DonkyListener() /*set callback handlers*/);
      
				DonkyCore.initialiseDonkySDK(this,">>ENTER API KEY HERE<<", 
        	new DonkyListener() /*set callback handlers*/);

		}
}

Important

The initialisation needs to be performed in onCreate method of Android Application class and
the initialisation of Core Module needs to go after the initialisation of all the other modules.

Dependencies

Inbox Analytics Module depends on Core Module.

Done

That's it. The analytics controller will handle the rest automatically. The Inbox Network will be notified every time your application will be launched or closed.

Automation

Setup

Firstly setup the third party automation triggers required in the Inbox Control portal as described here . These triggers are used to drive actions such as sending messages or notifying other systems from within Inbox, and are invoked using the Automation module or our Automation REST services.

GitHub

You can use File > New > Import Module in Android Studio to import this module with all its dependencies into your project.

Gradle

// Add jCenter repository to build.gradle. Default for Android Studio.
repositories {
  jcenter()
}

// Add aar dependancy to build.gradle.
dependencies {
    compile 'net.donky:donky-module-automation:2.7.0.3'
}

Using Custom Meta Data in Campaign Builder

If using triggered campaigns in Campaign Builder with a 3rd Party Trigger you can merge the meta data into messages and links using the td_ prefix followed by the meta data item name.

e.g. If we had passed the meta data name/value pair of "itemType" , "clothes", then the following token would be used to merge this into a message or link: [[td_itemType]]

e.g. "Thanks for browsing [[td_itemType]], please come back soon."

Initialise

public class MyApplication extends Application {

    @Override
    public void onCreate()
    {
      super.onCreate();

      DonkyAutomation.initialiseDonkyAutomation(this, 
      	new DonkyListener() /*set callback handlers*/);

      DonkyCore.initialiseDonkySDK(this,">>ENTER API KEY HERE<<", 
      	new DonkyListener() /*set callback handlers*/);
      
    }
  
}

Important

The initialisation needs to be performed in onCreate method of Android Application class and
the initialisation of Core Module needs to go after the initialisation of all the other modules.

To execute a third party trigger simply do the following:

//Use this method to create a new third party trigger notification and queueue it. It will be sent with the next synchonisation.
AutomationController.getInstance().
  executeThirdPartyTrigger(triggerKey, additionalData);

//Use this method to create a new third party trigger notification and immediately send it to the nextwork. 
AutomationController.getInstance().
  executeThirdPartyTriggerWithKeyImmediately(triggerKey, additionalData, 
		new DonkyListener() /*set callback handlers*/);

Dependencies

Inbox Automation Module depends on Core Module.

Done!

That's all that's needed to start using Inbox's third party triggers.

Suggest Edits

Push

Module to receive and optionally display push notifications sent from Inbox Network

 

New Inbox Push module 2.3.0.0

Starting from version 2.3.0.0 for simplicity and OS parity we merged previous Push Logic and Push UI modules into one. You can still replace our standard UI behaviour with a custom one.

GitHub

You can use File > New > Import Module in Android Studio to import this module with all its dependencies into your project.

Gradle

/*Check if jcentre repository is present in main build.gradle. Default for Android Studio.*/
repositories {
  jcenter()
}

/*Add dependency to build.gradle.*/
dependencies {
    compile 'net.donky:donky-module-push:2.7.0.3'
}

Initialise

public class MyApplication extends Application {

    @Override
    public void onCreate()
    {
        super.onCreate();
      
      	DonkyPush.initialiseDonkyPush(this, shouldDisplayRemoteNotifications,
        	new DonkyListener() /*set callback handlers*/);
      
				DonkyCore.initialiseDonkySDK(this,">>ENTER API KEY HERE<<", 
        	new DonkyListener() /*set callback handlers*/);
    		}
		}
}
  • shouldDisplayRemoteNotifications = true

    The boolean shouldDisplayRemoteNotifications set to true will cause SDK to render notification banners in Android Notification Centre.

  • shouldDisplayRemoteNotifications = false

    If you would like to change the way these push messages are being displayed (and still keep analytics around push message delivery, any embedded deep links etc.) please set the shouldDisplayRemoteNotifications flag to false.

Important

The initialisation needs to be performed in onCreate method of Android Application class and
the initialisation of Core Module needs to go after the initialisation of all the other modules.

Dependencies

If you are using Maven, or importing Android module using Android Studio, dependencies are managed automatically.

If not please remember: Inbox Push Module depends on Common Messaging Logic Module, Assets Module and Core Module.

Analytics

Please make sure you initialise the Analytics module to report app session analytics to Inbox. Find out more on the Analytics & Automation page.

GCM

If you don't receive message notifications when the application is closed

  • Check if you initialising Inbox SDK in a class that is extending Application class and if this class is declared in Android Manifest file:
    <application android:name="MyApplicationClassName"

  • If you are using your own GCM project please see GCM Configuration page for more details how to set up your project for the SDK

Done

With above implementation, your application will now handle all incoming push notifications, record read receipts and send local events containing the message description.

If you have set the shouldDisplayRemoteNotifications flag to true the SDK will display the 'out of the box' Notification banner views when push messages are received.

Note

Our Push for Android uses our guaranteed routes via HTTPS so will support for non-GCM Android apps with push delivered in app.

Intercepting the message

Optional

You can subscribe for push message local events and use the message and metadata to implement your own custom behaviour and UI.

Put the bellow subscription code snippet to onCreate method of your application class to make sure you will handle the events when system will run GCM broadcast receiver and also the handler won't be duplicated.

public class MyApplication extends Application {

    @Override
    public void onCreate()
    {
        super.onCreate();
      
      	DonkyCore.subscribeToLocalEvent(
          new DonkyEventListener<SimplePushMessageEvent>(SimplePushMessageEvent.class) {
          
						@Override
						public void onDonkyEvent(SimplePushMessageEvent event) {
							//handle the new push message e.g.
                List<SimplePushData> messages = event.getBatchSimplePushData();
                for (SimplePushData message : messages) {
                    String messageBody = message.getBody();
                  	//...
                }
						}
				
    		});
        //...
		}
}

For the list of available message details please have a look at SimplePushData class.

Customisation

Optional

If you are using our default remote notifications you can still customise the icon displayed for Inbox push notifications.

Push Notification icon

In order to replace the default remote notification icon you will need create following folders in your top application module:

  • res\drawable-hdpi
  • res\drawable-mdpi
  • res\drawable-xhdpi
  • res\drawable-xxhdpi
  • res\drawable-xxxhdpi

and put in each of these donky_notification_small_icon_simple_push.png file [in appropriate sizes 48x48, 72x72, 96x96, 144x144, 192x192 pixels].

Material Design style for Android 5.0

‘The system ignores all non-alpha channels in action icons and in the main notification icon. You should assume that these icons will be alpha-only. The system draws notification icons in white and action icons in dark gray’ (Android 5.0 changes)

Suggest Edits

Custom Notification Icons

Customise default remote notifications in Inbox Push Module

 

Inbox Push Module for Android gives you some flexibility on how the default remote notifications will look like.

1. Options

What are your options for Android Notifications generated by Push Module? Any combination of below scenarios:

a) No avatar set for both App Space and Messaging Identity set for Campaign.

b) No avatar set for both App Space and Messaging Identity. Custom notification icon.

c) No avatar set for both App Space and Messaging Identity with custom notification icon background colour.

d) Avatar set for App Space or Messaging Identity.

2. How to replace the small icon

a) Create set of new icons following bellow Google guidelines

Material Design

Android 5.0 changes: ‘Update or remove assets that involve colour. The system ignores all non-alpha channels in action icons and in the main notification icon. You should assume that these icons will be alpha-only. The system draws notification icons in white and action icons in dark gray’

b) Rename new icons to
donky_notification_small_icon_simple_push.png

c) put new icons in appropriate sizes in drawable folders in your app project

  • res\drawable-hdpi
  • res\drawable-mdpi
  • res\drawable-xhdpi
  • res\drawable-xxhdpi
  • res\drawable-xxxhdpi

3. How to change the small icon background colour

To change the colour of small icon background include the following code after initialisation of push module and before initialisation of core module in your application class

SimplePushUIConfiguration conf = new SimplePushUIConfiguration(getApplicationContext());
conf.setIconBackgroundColor(Color.RED);
DonkyPushUI.getInstance().setSimplePushUIConfiguration(conf);

For example

public class MyApplication extends Application {
	@Override
	public void onCreate()
	{
  	super.onCreate();
    
  	DonkyPush.initialiseDonkyPush(this,
    	new DonkyListener() /*set callback handlers*/);
    
		SimplePushUIConfiguration conf = 
      new SimplePushUIConfiguration(getApplicationContext());
		conf.setIconBackgroundColor(Color.RED);
		DonkyPush.getInstance().setSimplePushUIConfiguration(conf);

    DonkyCore.initialiseDonkySDK(this,">>ENTER API KEY HERE<<", 
    	new DonkyListener() /*set callback handlers*/);  
}

4. How to set Identity Avatar as big icon

a) Set Avatar for App Space

Inbox Control > Choose App Space > Identities > Edit App Space > Avatar

Or set avatar for one of your app identity

Inbox Control > Choose App Space > Identities > Edit Identity > Avatar

b) Set the app identity with avatar in your campaign (or app identity with no avatar if you are fine with app space avatar being displayed for this campaign)

Campaign Builder > New campaign > Settings > Identity

Suggest Edits

Rich Logic

 

Rich Logic Module is responsible for:

  • Receiving Rich Messages from the Inbox Core Module.
  • Acknowledging the messages on the Inbox Network.
  • Saving non expired Rich Messages to the database.
  • Publish RichMessageEvent to be consumed by UI modules. This event contains all details about received Rich Messages.
  • Provide a database access for saved Rich Messages.

How to start

Gradle

/*Check if jcentre repository is present in main build.gradle. Default for Android Studio.*/
repositories {
  jcenter()
}

/*Add dependency to build.gradle.*/
dependencies {
    compile 'net.donky:donky-module-rich-logic:2.7.0.3'
}

GitHub

You can use File > New > Import Module in Android Studio to import this module with all its dependencies into your project.

Initialise

To initialise the database, register a new user, and sync with the Inbox Network add following code to your Application class (remember to declare this class as an application class in your Android Manifest). The initialisation of Core Module needs to go after the initialisation of all the other modules.

public class MyApplication extends Application {

    @Override
    public void onCreate()
    {
        super.onCreate();

        DonkyRichLogic.initialiseDonkyRich(this,
      	new DonkyListener() /*set callback handlers*/);
         
				DonkyCore.initialiseDonkySDK(this,">>ENTER API KEY HERE<<", 
        	new DonkyListener() /*set callback handlers*/);
    }
  
}

For more details about initialising the SDK and registering the user please read Register Users page.

Dependencies

If you are using Maven, or importing Android module using Android Studio, dependencies are managed automatically.

If not please remember: Rich Logic Module depends on Common Messaging Logic Module and Core Module

Analytics

Please make sure you initialise the Analytics module to report app session analytics to Inbox. Find out more on the Analytics & Automation page.

GCM

If you don't receive message notifications when the application is closed

  • Check if you initialising Inbox SDK in a class that is extending Application class and if this class is declared in Android Manifest file:
    <application android:name="MyApplicationClassName"

  • If you are using your own GCM project please see GCM Configuration page for more details how to set up your project for the SDK

Done

You have initialized the Inbox SDK with Rich Messaging and created a new user registration. You can find details on Register Users page how to obtain or change user id. You can use that id to target rich messages on Campaign Builder.

Listen for new Rich Messages

To receive incomming Rich Messages you can register a listener for RichMessageEvent's

DonkyCore.subscribeToLocalEvent(
  new DonkyEventListener<RichMessageEvent>(RichMessageEvent.class) {

    @Override
    public void onDonkyEvent(RichMessageEvent event) {
      if (event != null) {
        // Received Rich Messages
        for (RichMessage rm : event.getRichMessages()) {
					if (rm.isSilentNotification()) {
            //silent notification, don't interrupt the user
          } else {
            //e.g. display remote notification
          }
        }
			}
		}
});
DonkyCore.unsubscribeFromLocalEvent(donkyEventListener);

This Listener will be then notified every time Rich Messages will be received from Inbox Network by Inbox Core Module.

Subscription will be overridden only if the same instance will be passed to this method, so be careful not to register listener duplicates. You may consider one of the possible implementations:

  • A singleton class holding subscription listener and list of your custom UI listeners to which Activities will subscribe and unsubscribe in onResume and onPause callbacks.
  • Subscribing to RichMessageEvent in onResume of your Activity and unsubscribing in onPause of your Activity.

Binding data to Activites

To be notified about changes in the database you can use the native Android pattern with LoadManagers.

As an example, here is the full implementation of a Activity that displays a ListView containing the RichMessages of a query against the RichMsgContentProvider. It uses a CursorLoader to manage the query on the provider.

public class RichInbox extends Activity implements LoaderManager.LoaderCallbacks<Cursor>{

    private ListView listview;
    private SimpleCursorAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.inbox_activity);
        listview = (ListView) findViewById(R.id.list_view);
        initLoader();
    }

    @Override
    public void onLoadFinished(
      android.support.v4.content.Loader<Cursor> loader, Cursor data) {
         adapter.swapCursor(data);
    }

    @Override
    public void onLoaderReset(
      android.support.v4.content.Loader<Cursor> loader) {
        adapter.swapCursor(null);
    }

    @Override
    public android.support.v4.content.Loader<Cursor> onCreateLoader(
      int id, Bundle args) {
            return new CursorLoader(this,
            RichMsgContentProvider.CONTENT_URI_RICH,
            null, 
            null,
       			null, 
            null);
    }


    private void initLoader() {
        
       	int[] to = new int[] { 
          R.id.rowid, 
          R.id.time, 
          R.id.description,
          R.id.accuracy
        };
        adapter = new SimpleCursorAdapter(this, 
                  R.layout.row, null, 
                  GeoFenceDAO.allColumnsGeoFence, to, 0);
        listview = (ListView) findViewById(R.id.right_drawer);
        listview.setAdapter(adapter);
      	getSupportLoaderManager().initLoader(0, null, this);
    }
}

In this example, you can see as soon as we made some changes in the database, the Content Provider will immediately notify you and you can update the UI or your logic based on these changes.

Access Database

Database Access Object

You can retrieve the Rich Message object either from RichMessageEvent after new messages were received from the Network (see section above) or by accessing the database.

You can get the Rich Messaging Database Access Object in the following way:

RichMessageDataController.getInstance().getRichMessagesDAO();

RichMessagesDAO class expose methods to save, query, delete, mark as read the Rich Messages in SQLite database. E.g.

RichMessageDataController.getInstance().getRichMessagesDAO().getAllRichMessages(new DonkyResultListener<List<RichMessage>>() {
	@Override
	public void success(List<RichMessage> result) {
	}
    
	@Override
	public void error(DonkyException donkyException, Map<String, String> validationErrors) {
	}
});
RichMessageDataController.getInstance().getRichMessagesDAO().getRichMessagesCursorForUI(null, new DonkyResultListener<Cursor>() /*set callback handler*/);
String id = richMessage.getInternalId();

RichMessageDataController.getInstance().getRichMessagesDAO().getRichMessage(id, new DonkyResultListener<RichMessage>() /*set callback handler*/);
RichMessageDataController.getInstance().getRichMessagesDAO().removeRichMessage(richMessage, new DonkyResultListener<Integer>() /*set callback handler*/);