Messages

Last updated 2 months ago

Messages are central to the operations of mitter.io. The design of mitter.io tries to assume as little as it can about Messages in general or the data they contain. The system is designed to be extremely flexible around the type of data a Message can contain. A Message has two important constraints: it requires a sender to be sent, and that it must have a text representation of the data it is sending. This can be an empty string as well but it is not recommend. There are other situational constraints as well, which will be discussed further in this section. A Message as other identifiables is identified by a unique identifier, which can be overriden. The standard restrictions for an identifier apply as usual:

  1. The identifier can use alphanumeric characters.

  2. The identifier can use from a set of symbols (- _ @ $ #)

  3. The first character can only be alphanumeric, @ or #

  4. The identifier must be between 8 and 72 characters

This is the regex: [a-zA-Z0-9@#][a-zA-Z0-9-_@$#]*

The Message Model

{
"messageId": "8b5ae385-b1e7-4279-a1a0-8d65abe74493",
"messageType": "Standard",
"payloadType": "mitter.mt.Text",
"senderId": "0b604184-7abd-453c-b258-7e4425b31e7f",
"textPayload": "Hello world!",
"messageData": [
{
"dataType": "application/json",
"data": {
"weather": "pleasant"
}
}
]
"timelineEvents": [
{
"type": "mitter.mtet.SentTime",
"eventTimeMs": 1506965184943,
"subject": "0b604184-7abd-453c-b258-7e4425b31e7f"
}
],
"audit": {
"createdAt": 144656562,
"updatedAt": 144666852
}
}

A quick overview of the fields in a Message:

  1. The messageId contains the unique identifier of this message.

  2. The messageType holds the type of this message. A message type defines handling parameters for this message. It is a closed set of values, and must be one of Standard, OutOfBand and Notification. Currently, only Standard and Notifcation are supported.

  3. The payloadType defines the type of data that this message contains. By default mitter.io provides support for certain types of messages. Each type of a message prescribes a format for the messageDatum field. This value can be anything the developer wishes and can modify it to use custom payloads.

  4. The senderId field is the identifier of the user who sent this message. This is a mandatory field.

  5. The textPayload is the text representation of the image that is sent.

  6. The messageData field contains custom data in a list of MessageDatum objects. This field is covered in detail in the upcoming sections.

  7. The timelineEvents field contains events associated with this message. This field is covered un detal in the upcoming sections.

  8. The audit field contains createdAt (creation time) and updatedAt (last update time) timestamps. These cannot be overridden in POST calls and will be ignored if sent.

Payload Types

Out of the box mitter.io supports four payload types:

  1. mitter.mt.Text

  2. mitter.mt.ImageMessage

  3. mitter.mt.FileMessage

  4. mitter.mt.EmptyMessage

There is upcoming support for the following payload types:

  1. mitter.mt.FormattedText

  2. mitter.mt.LinkInsetText

You are free to define your own payload types and set it as the value of the payloadType field, however no such type name can start with io.mitter. or mitter..

Text Message

TYPE mitter.mt.Text

This type represents a simple text message. An example is as below:

{
"payloadType": "mitter.mt.Text",
"senderId": "fbb7e84d-60a2-435e-b9c4-800232e06fd0",
"textPayload": "Hello! World",
"timelineEvents": [ ... ]
}

The features of a text message are:

  1. It does not contain a value for messageDatum. However, if the developer sets it, mitter.io will persist it and forward it on most delivery methods. Certain delivery mechanisms will ignore the message data field so it is recommend to not set this field.

  2. This message is handled almost completely unprocessed before delivering messages.

COMING SOON In the upcoming releases, we will be adding support for deconstructed messages, which are messages heavily stripped from their JSON structure and aggresively compressed to reduce the overall byte size when delivering over messaging mechanisms like FCM. This mechanism will not support the messageDatum field.

Image Message

TYPE mitter.mt.ImageMessage

{
"payloadType": "mitter.mt.Image",
"senderId": "fbb7e84d-60a2-435e-b9c4-800232e06fd0",
"textPayload": "My november visit to Yellowstone!",
"messageData": [
{
"dataType": "text/uri-list",
"data": {
"link": "https://content.mitter.io/applications/d796 .. 10478/$repr",
"repr": ["base", "thumbnail", "standard"]
}
}
],
"timelineEvents": [ ... ]
}

The ImageMessage is handled much differently when sending messages, the link field is not directly populated (if the image is to be stored and handled by mitter.io itself). Refer to the section Image and binary payloads later in this section.

The overall contract of this message is:

  1. It must contain EXACTLY one MessageDatum of type text/uri-list.

  2. The corresponding data field must contain a link which is the URI of the hosted image. This URI contains a placeholder $repr which would be one of the specified representations.

  3. The representations available for the image must be provided as an array of strings, with each element denoting a representation of the image.

Do note that the developer does not have to manually make the multiple representations available. mitter.io automatically handles resizing and recompressing the image and making multiple representations available for you automatically. This is covered more in the upcoming sections.

File Message

TYPE mitter.mt.FileMessage

{
"payloadType": "mitter.mt.FileMessage",
"senderId": "fbb7e84d-60a2-435e-b9c4-800232e06fd0",
"textPayload": "My november visit to Yellowstone!",
"messageData": [
{
"dataType": "text/uri-list",
"data": {
"link": "https://content.mitter.io/applications/d796 .. 10478/"
}
}
],
"timelineEvents": [ ... ]
}

Starting with v0.4, mitter.io now supports file messages as well. This is exactly the same as sending an Image message with the only difference that file messages do not have any representations and no post processing is performed on any of the uploaded files. When uploading a file message do note that the request must contain EXACTLY one multi-part request. If it contains more than one or no binary parts, the entire request is rejected.

Formatted Message coming soon

TYPE mitter.mtet.FormattedText

This is a message type that can handle text that is formatted. The initial versions will not support complete rich-text support, but mostly a subset of the markdown specification. Do note that all details regarding this message type are currently tentative. An example payload of this type would look like:

{
"payloadType": "mitter.mt.FormattedText",
"senderId": "fbb7e84d-60a2-435e-b9c4-800232e06fd0",
"textPayload": "Hello! World",
"messageData": [
{
"dataType": "text/markdown",
"data": {
"txt": "Hello! **World**"
}
}
]
}

There is also a tenatative plan to support deconstructed messages for Formatted Messages as well.

Timeline Events

Timeline events are events that are associated with activity related to a message. They generally record the time of user action on a message. There are four timeline event types that are provided by mitter.io:

  1. mitter.mtet.SentTime The time at which the message was sent. This timestamp is populated with the timestamp on the client device.

  2. mitter.mtet.ReceivedTime The time at which the message was received by the server. The subject of this timeline event is always .system.

  3. mitter.mtet.DeliveredTime The time at which the message was delivered to a user. The subject of this event is the receiving user.

  4. mitter.mtet.ReadTime The time at which the message was read by a user. The subject of this event is the receiving user.

A Message can have multiple timeline events of the same type. Furthermore, the event type does not have to be one of the above values and can be set to any custom value. However, types cannot start with io.mitter. or mitter..

Message API Reference

Sending a Message

To send a Message, make a POST request. By default a message can be sent to a channel by any participant with the same user as the sender. This behavior can be overriden by using ACLs. .system can send a message to any channel on behalf of any user.

POST /v1/channels/f8cc57d8-af38-4313-85e8-cbe62a2ebf23/messages
{
"messageType": "mitter.mt.Text",
"senderId": "0b604184-7abd-453c-b258-7e4425b31e7f",
"textPayload": "Hello World!",
"timelineEvents": [
{
"type": "mitter.mtet.SentTime",
"eventTimeMs": 1506966743942,
"subject": "0b604184-7abd-453c-b258-7e4425b31e7f"
}
]
}

Do note that if you send the audit fields, they will be ignored.

On success, the server returns back a Message object itself. This is important because certain messages (like Image Message) undergo processing and modify the message object as is available from mitter.io past this request succeeding. A response to this message would look like:

200 OK
{
"messageId": "9fd6ece8-c4e7-4fd3-81bc-c5d449e9863d",
"messageType": "mitter.mt.Text",
"senderId": "0b604184-7abd-453c-b258-7e4425b31e7f",
"textPayload": "Hello World!",
"timelineEvents": [
{
"type": "mitter.mtet.SentTime",
"eventTimeMs": 1506966743942,
"subject": "0b604184-7abd-453c-b258-7e4425b31e7f"
}
],
"audit": {
"createdAt": 144665321
//There is no updatedAt because it has not yet been updated
}
}

As noted earlier, a Text Message undergoes minimal processing so this object is the exact same as what was sent to it, with the exception that it contains the generated message identifier.

NOTE Sending a Message has an additional constraint which requires that there must be EXACTLY one timeline event of type mitter.mtet.SentTime present in the Message body. The example above includes this case.

Getting Messages from a Channel

Messages can be fetched from a Channel in a paginated manner using a GET query. By default all participants of a channel can fetch messages in the Channel which they have read access to (by default all Messages in a Channel are readable by all participants). This behavior is highly configurable and can be overriden by using ACLs.

GET /v1/channels/f8cc57d8-af38-4313-85e8-cbe62a2ebf23/messages
?limit=<limit>
?before=<message-id>
?after=<message-id>

For any given request, at most one of before or after can be set. The limit can be set to any positive value lesser than 25. To make the first request, make a GET request without any of this parameters:

GET /v1/channels/f8cc57d8-af38-4313-85e8-cbe62a2ebf23/messages?limit=10

The request above will return the last 10 messages of the server.

200 OK
[
{
"messageId": "e70cb",
...
},
...
{
"messageId": "a609f",
...
}
]

The Messages are returned in a sorted manner depending on which parameter was set. If before was set, the messages are sorted such that newer messages appear last in the list. If after was set, newer messages appear first in the list. When neither is provided, before semantics are applied. Following the request above, you can fetch any messages before this using:

GET /v1/channels/f8cc57d8-af38-4313-85e8-cbe62a2ebf23/messages?before=a609f

and to get any new incoming messages in the channel:

GET /v1/channels/f8cc57d8-af38-4313-85e8-cbe62a2ebf23/messages?after=e70cb

If there are no Messages returned, it means there are no Messages for the given criteria. When making a before call, you can stop making any further calls. This method will never return any new messages. For an after call, this is the URI you keep on polling for newer messages till one arrives.

This method however is rarely used for fetching Messages. All of our SDKs use push-based delivery mechanisms like FCM for Message delivery. The HTTP methods are primarily used for syncing messages on a client device.

For more information of receiving messages through those delivery channels refer to the Delivery Endpoints section of this reference document.

Deleting Messages from a Channel

To delete a Message from a Channel, make a DELETE call. By default all Users can delete Messages that they have sent. Any deletion of a Message does not delete any delivered entities from any recipients end-device. .system can delete any Message for any User on any Channel.

DELETE /v1/channels/f8cc57d8-af38-4313-85e8-cbe62a2ebf23/messages/a609f

On successful execution, the server returns a 204

204 No Content

Sending Binary and Image messages

mitter.io allows you to upload image and file messages by supporting multipart requests. The multipart request (for images as well as files) should contain two parts:

  1. name=io.mitter.wire.requestbody of type application/json. This contains the message request body, i.e. a JSON of the image model that is sent.

  2. Exactly one part which contains the image binary. The type must be either image/jpeg, image/png or image/gif. The name of this part is ignored.

An example of such a request is as follows:

POST /v1/channels/f8cc57d8-af38-4313-85e8-cbe62a2ebf23/messages
-----------------RANDOM4239523GENERATED3593FORM2349BOUNDARY
Content-Disposition: form-data; name="io.mitter.wire.requestbody"; filename="<ignored">
Content-Type: application/json
{
"payloadType": "mitter.mt.Image",
"senderId": "fbb7e84d-60a2-435e-b9c4-800232e06fd0",
"textPayload": "My november visit to Yellowstone!",
"timelineEvents": [
{
"type": "mitter.mtet.SentTime",
"eventTimeMs": "1502890238119",
"subject": "fbb7e84d-60a2-435e-b9c4-800232e06fd0"
}
]
}
-----------------RANDOM4239523GENERATED3593FORM2349BOUNDARY
Content-Disposition: form-data; name="<ignored>"; filename="<ignored>"
Content-Type: image/png
>> binary-data
-----------------RANDOM4239523GENERATED3593FORM2349BOUNDARY--

All other constraints that apply to a message still apply to the request JSON i.e. a timeline event of type mitter.mtet.SentTime must be set.

Image Message Processing

When an image is uploaded, mitter.io performs the following processing:

  1. Converts the image into two additional formats: standard and thumbnail.

  2. The thumbnail representation is used to display a thumbnail image, mostly in messge bubbles on a UI.

  3. The standard is a reduced-size, compressed image that can be used to display the enlarged format, probably when the user clicks on the displayed thumbnail.

  4. Additionally a represetnation called base is created which contains the image AS-IS, but the support for this is subject to revocation.

An uploaded image is immediately transformed and stored in an intermediate storage on the mitter.io servers. In the background, the image is uploaded to a CDN. It might take any amount of time for the image to be made available in the CDN. Till the image is available in the CDN, any request to the returned link in the image message returns a 200 OK with the image data in the payload. Once it is uploaded to the CDN, the service returns a 301 PERMANENTLY MOVED with the link from the CDN.

Timeline events

Adding a timeline event to a message

To add a timeline event to a message, make a POST call

POST /v1/channels/f8cc57d8-af38-4313-85e8-cbe62a2ebf23/messages/a609f/timeline
{
"type": "mitter.mtet.ReadTime",
"eventTimeMs": 1506968364492
"subject": "a6097f2f-b5cf-4afc-b246-019da17c281b"
}

This call supports setting timeline events for multiple messages in one go. This is generally useful when a user reads multiple received messages at the same time. For this, pass the message ids in a CSV format:

POST /v1/channels/f8cc57d8-af38-4313-85e8-cbe62a2ebf23/messages/a609f,dbef/timeline

On successful execution, the server returns a 204

204 No Content

Getting Timeline Events for a Message

To get timeline events for a message, make a GET call

GET /v1/channels/f8cc57d8-af38-4313-85e8-cbe62a2ebf23/messages/a609f,dbef

This returns a list of MessageTimelineEvent objects

200 OK
[
{
"messageId": "a609f",
"timelineEvent": { ... }
},
{
"messageId": a609f",
"timelineEvent": { ... }
},
{
"messageId": "dbef",
"timelineEvent": { .. }
}
]

If you want to fetch events of only a certain type, a timeline event filter can be passed to the GET call.

GET /v1/channels/f8cc57d8-af38-4313-85e8-cbe62a2ebf23/messages/a609f,dbef
?eventTypeFilter=mitter.mtet.DeliveredTime,mitter.mtet.ReadTime

Timeline Events one set cannot be deleted even if custom types are used.