# Welcome

Welcome to Mitter.io Docs. This short guide will help you cruise around our documentation without breaking your head.

This documentation is split into **3** broad categories for easy access:

* **Platform Reference** - where you can learn more about the Mitter.io platform and the core concepts
* **Getting Started** - where you can learn how to quickly whip up apps using Mitter.io for Android and the Web
* **SDKs** - where you can explore our SDKs in depth and take full advantage of what they have to offer

Head over to the [Get Mitter.io](/mitter.io-on-docker) page and get the platform running on your machine.

> **When running the container**
>
> You will have to change the endpoint when using our SDKs to and endpoint serving the docker container. This depends on which ports you have exposed and how you are accessing your docker installation. Refer to the individual SDK documentation on how to override the default API endpoint.
>
> **If using the cloud sandbox**
>
> You can use the SDKs without changing the API URLs. They by default point to the cloud sandbox.

If you’re new to Mitter.io, we would recommend you start by looking into the **Getting Started** section and get a hands-on idea of how Mitter.io works.

After that, you can browse around the core platform concepts to learn more about the platforms and all the APIs that we expose for our service.

Lastly, check out the SDKs section to see how you can leverage the platform features in your app without putting in too much effort writing boilerplate code.

This flow should get you accustomed to Mitter.io and how it works. Nevertheless, feel free to browse around as you feel is best for you.

Want a more detailed guide to the platform and how to build with it? Watch our webinars [here](https://www.youtube.com/watch?v=2EERT1O0CHQ)!

We're excited to see what you build with Mitter.io!


# Migrating from cloud to on-prem

To migrate from a cloud mitter.io account to an on-prem solution, roughly the following steps need to be followed:

1. Host an instance of mitter on-prem using your deployment stack (in this walkthrough, we will use docker compose)
2. Make your instances reachable from the outside world
3. Migrate all the data from your cloud account to the on-prem account
4. Replace all endpoints that your services/apps were using to the new endpoints you have configured

For getting a reachable endpoint, you'll most probably need a domain that points to wherever you have hosted mitter. A general overview of how mitter.io on docker works is provided on the [mitter.io on docker page](https://docs.mitter.io/mitter.io-on-docker). Do note that the image used on that page is not suitable for production deployments, as it bundles all services in a single container making replications and horizontal scaling that much difficult.

### Using docker-compose to host your mitter instance

To get started, install docker compose from <https://docs.docker.com/compose/>

Following is a sample configuration of how you can host all mitter services from within docker compose:

```
version: '3.4'

x-pgsql-common-variables: &pgsql-common-variables
  POSTGRES_USER: <pg_user>
  POSTGRES_PASSWORD: <pg_password>
  POSTGRES_DB: mitter-io-central
  PGSQL_HOST: <pg_host>
  PGSQL_PORT: 5432

x-rabbitmq-common-variables: &rabbitmq-common-variables
  RABBITMQ_DEFAULT_USER: <rabbitmq_user>
  RABBITMQ_DEFAULT_PASS: <rabbitmq_password>
  RABBITMQ_HOST: rabbit-mq
  RABBITMQ_PORT: 5672

x-minio-common-variables: &minio-common-variables
  MINIO_ACCESS_KEY: <minio_user>
  MINIO_SECRET_KEY: <minio_secret_key>
  MINIO_MEDIA_STORE_BUCKET: equities-media

services:
  rabbit-mq:
    image: rabbitmq:3.7.17-management
    restart: always
    logging: 
      <<: *mitter-logging-config
    environment:
      <<: *rabbitmq-common-variables
      RABBITMQ_VM_MEMORY_HIGH_WATERMARK: 0.85
    ports:
      - "15672"
    networks:
      - mitter-subnet

  redis:
    image: 'redis:4-alpine'
    command: 'redis-server --appendonly yes --appendfsync everysec --save "60 0"'
    restart: always
    ports:
      - "6379"
    volumes:
      - ./.data/redis-data:/data
    networks:
      - mitter-subnet

  minio:
    image: 'minio/minio:RELEASE.2019-08-07T01-59-21Z'
    command: 'server /data'
    restart: always
    ports:
      - "9000"
    environment: *minio-common-variables
    volumes:
      - ./.data/minio-data:/data
    networks:
      - mitter-subnet

  weaver:
    image: mitterio/weaver:dc-rc1-JU1120
    restart: always
    labels:
      autoheal: "true"
    healthcheck:
      test: ["CMD", "wget", "-O", "-", "http://localhost:7180/health"]
      start_period: 40s
      timeout: 5s
      interval: 20s
      retries: 3
    environment:
      <<: *pgsql-common-variables
      <<: *rabbitmq-common-variables
      WEAVER_ARGS: --config-file=/config/weaver.application.conf
      INIT_WAIT_TIME: 10
    depends_on:
      - rabbit-mq
    ports:
      - "7180:7180"
      - "11951:11951"
    volumes:
      - ./config:/config
      - ./javaagent:/javaagent
    networks:
      - mitter-subnet

  mitter:
    image: mitterio/platform:dc-rc1-JU1120
    restart: always
    logging: 
      <<: *mitter-logging-config
    labels:
      autoheal: "true"
    healthcheck:
      test: ["CMD", "wget", "-O", "-", "http://localhost:11901/health"]
      start_period: 1m10s
      timeout: 5s
      interval: 20s
      retries: 3
    depends_on:
      - weaver
      - redis
      - minio
    environment:
      <<: *pgsql-common-variables
      <<: *minio-common-variables
      CENTRAL_APPLICATION_CONFIG: |
        {
          "mitter.security.jwt.signing-key": "<your-signing-key>",
          "mitter.security.jwt.issuer": "<issuer-name">",
          "mitter.central.cache.application-resolution.expireAfterCreate": "1",
          "mitter.central.cache.application-resolution.expireAfterUpdate": "1",
          "mitter.central.cache.application-resolution.expireAfterRead": "400",
          "mitter.central.cache.channel-resolution.expireAfterCreate": "1",
          "mitter.central.cache.channel-resolution.expireAfterUpdate": "1",
          "mitter.central.cache.channel-resolution.expireAfterRead": "400",
          "mitter.central.cache.user-resolution.expireAfterCreate": "1",
          "mitter.central.cache.user-resolution.expireAfterUpdate": "1",
          "mitter.central.cache.user-resolution.expireAfterRead": "400",
          "mitter.central.cache.counts.expireAfterCreate": "1",
          "mitter.central.cache.counts.expireAfterUpdate": "1",
          "mitter.central.cache.counts.expireAfterRead": "1",
          "mitter.services.internal.weaver.enabled": "true",
          "mitter.services.internal.weaver.internal-uri": "http://weaver:7181",
          "mitter.central.database-url": "postgres://$${POSTGRES_USER}:$${POSTGRES_PASSWORD}@$${PGSQL_HOST}:$${PGSQL_PORT}/$${POSTGRES_DB}",
          "mitter.plugins.directory": "/plugins",
          "mitter.media.store.minio.uri": "http://minio:9000",
          "mitter.security.token-issuance.redis-uri": "redis:6379:0",
          "mitter.security.contexts.user-jwt-cookie.enabled": "true",
          "spring.servlet.multipart.max-file-size": "6MB",
          "spring.servlet.multipart.max-request-size": "7000KB",
          "logging.level.io.mitter.commons.spring.RequestBeanManagement": "ERROR",
          "logging.level.io.mitter.security.manager.PrincipalManager": "ERROR",
          "logging.level.acl-execution": "WARN",
          "logging.level.io.mitter.auth.context": "WARN",
          "logging.level.io.mitter.auth.resolvers": "WARN",
          "logging.level.io.mitter.security.support.WebPrincipalResolutionState": "WARN",
          "logging.level.io.mitter.auth.filters.JwtProcessingFilter": "WARN",
          "mitter.central.skip-request-logging": "^OPTIONS:.*$$,^GET:.*/presence.*$$,^GET:.*/counts/.*$$,^GET:.*/users/me$$",
          "mitter.central.cors-allowed-origins": "*",
          "server.tomcat.max-threads": "400",
          "spring.datasource.hikari.maximumPoolSize": "10",
          "mitter.central.outflow": "false",
          "mitter.security.token-issuance.users.expiry-time": "2112912000",
          "mitter.security.token-issuance.users.maximum-tokens": "3000"
        }
      DASHBOARD_JVM_ARGS: |
        -Dmitter.security.auth.basic-auth.enabled=true
        -Dmitter.security.auth.basic-auth.username=<dashboard-user>
        -Dmitter.security.auth.basic-auth.password=<dashboard-password>
        -Dmitter.sui.base-uri=https://mitter-sui-nyc1-a0.equities.chat
    volumes:
      - ./plugins:/plugins
      - ./javaagent:/javaagent
    ports:
      - "11901:11901"
      - "11902:11902"
      - "11950:11950"
    networks:
      - mitter-subnet

networks:
  mitter-subnet:
    ipam:
      driver: default
      config:
       - subnet: 172.24.0.0/22

```

A few things to note in the configuration above:

1. The configuration is not expected to work by just copy-pasting the file as-is. It is there to give a reference for your implementation.
2. In the example above, it is assumed that you are hosting your postgres database separately. For servicing real-world loads, we would recommend using CloudSQL or AWS RDS or a similar managed postgres service. If you want to run your own db, a docker container similar to the following will work:
   1. `docker run --name mitter-postgres-db -e POSTGRES_PASSWORD=<user> -e POSTGRES_USER=<user> -e POSTGRES_DB=mitter-io-central -p 35332:5432 -d postgres:9.6.6-alpine`
3. Make sure you edit the `username` and `password` values for all running services in the first 3 sections of the file. They are then referenced in the `CENTRAL_APPLICATION_CONFIG` section of the configuration.
4. If you wish to continue running the mitter container with the dashboard enabled, then you should ideally not expose the dashboard port (in this example `11902`) to the outside world or the public internet. We would strongly recommend using either using an ssh tunnel or similar proxy mechanisms to access the dashboard. In supernova installs, the dashboard runs without authentication (as you would've expected on your cloud hosted account). In case you wish to access it using a public address, then you can enable basic auth with a username/password that can be specified as on line no. 147 / 148 in the snippet above.
5. Please do review the mount point of the `/data` volume in this docker compose installation. All media/images etc. get stored on this location - you would need to ensure adequate capacity as per your needs.

### Making your instance reachable

While this will greatly differ from installation to installation, a sample of how a machine running the docker compose shown above can use `nginx` to make these services accessible is as follows

```
server {
    client_max_body_size 10M;
    server_name mitter.your-host.com;

    location / {
	    proxy_pass http://localhost:11901;
    }

    listen [::]:443 ssl;
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/<ssl-cert-directory>/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/<ssl-cert-directory>/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}

server {
    server_name mitter-dashboard.your-host.com;

    location / {
	    proxy_pass http://localhost:11902;
    }

    listen [::]:443 ssl;
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/<ssl-cert-directory>/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/<ssl-cert-directory>/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}

server {
    server_name mitter-weaver.your-host.com;

    location / {
	      proxy_pass http://localhost:7180;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 86400;
    }

    listen [::]:443 ssl;
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/<ssl-cert-directory>/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/<ssl-cert-directory>/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}

server {
    if ($host = mitter.your-host.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    if ($host = mitter-dashboard.your-host.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

    if ($host = mitter-weaver.your-host.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    listen 80 default_server;
    listen [::]:80 default_server;

    server_name mitter-weaver.your-host.com mitter.your-host.com mitter-dashboard.your-host.com
    return 404; # managed by Certbot
}


```

### Migrating your data

By now, you would have received your entire data from the cloud account on your email. If you haven't, please reach out to `support@mitter.io`. To get started on the data migration, you will need three things:

1. An installation of `psql` on the some machine that can reach your postgres instance. This `psql` cli must be compatible with Postgres 9.6.
2. The data backup from your cloud account
3. A pre and post import script, available here : <https://gist.github.com/rohanprabhu-jm/bddeed8cdcc7cebb8fbea7fffcb866c6>

Once you have the three things ready, let's start first by applying the pre-import script:

```
psql -U <user> -h <host> -p <port> -d mitter-io-central -f mitter-pre-import.sql
```

Then, extract your data archive and import that:

```
tar -xvzf <your-email>.sql.tar.gz
psql -U <user> -h <host> -p <port> -d mitter-io-central -f <your-email>.sql
```

Then finally, apply the post-import script:

```
psql -U <user> -h <host> -p <port> -d mitter-io-central mitter-post-import.sql
```

Your data has already been transformed to run with mitter supernova builds, and all of your application ids, application keys, settings, user ids, message ids are preserved. All of your existing apps and APIs will continue to work as long as the API endpoints are modified. However, any user login information will not be persisted, and all of your users will have to fetch new authentication tokens. This is due to the fact that user tokens are signed by mitter's private key during issue and that data can longer continue to function without us distributing mitter's private key itself. (This is the 'signing-key' part of the configuration specificed in the docker-compose file).

### Migrating your apps

To migrate your apps, all you will need to do is:

1. Modify the endpoint to which your API calls were being made to (i.e. you will have to change it from `https://api.mitter.io` to `https://mitter.your-host.com` or a similar domain name you have chosen to host these services on)
2. Do note that you will have to use some form a web server or a load balancer to expose your docker containers to the public internet via a domain name. It is not recommended to directly expose your docker ports from your machines.
3. For apps, you will require all of your users to re-login and fetch new tokens. If they have registered any prior FCM delivery endpoints, they will continue to work without any changes from your side.
4. Depending on which SDK you are using, please refer to the SDKs individual page for instructions on how to specify a different endpoint

### Troubleshooting

If any of the containers refuse to start, or they are not working as expected, you can check the logs to troubleshoot these errors. To get a list of containers running, you could run

```
docker ps -a
```

If you used a docker compose file similar to the one above, you'd be seeing container names that begin with `mitter_`. Against these containers you should also be able to see if the containers are in a healthy or an unhealthy state. To check the logs, you can simply use:

```
docker logs --follow <container-name>
```


# Get mitter.io

Mitter is also distributed as a docker container publicly available on docker hub. It contains all the features of the public sandbox hosted at [mitter.io](https://mitter.io/) with the following exceptions:

1. You cannot register for an account or have multiple accounts for your services. Mitter runs in a single tenant mode. You can still however create multiple applications and use them with the same guarantees on data isolation.
2. Certain features like temporary media storage, distributed message broker, event bus is either not available or not supported.
3. If you are using multiple docker containers behind a load balancer, websocket deliveries will work intermittently. This is because there is no exchange of messages and connections between the serving nodes. To be able to serve traffic from multiple nodes, refer to the section 'Configuring an external broker'.

> **NOTE** The Mitter.io docker container is currently in public beta and is encouraged to be used for development and testing. If you wish to run this in production, reach out to us at <support@mitter.io>.

### Getting the docker container <a href="#getting-the-docker-container" id="getting-the-docker-container"></a>

Getting the docker container is simple:

```
docker pull mitterio/mitter:sa
```

The mitter docker containers do not support the `latest` tag as mitter is expected to be delivered in the following formats:

1. `sa` - (AVAILABLE) The standalone build which bundles the platform, dashboard and all dependent servies.
2. `sa-headless` - (COMING SOON) The standalone build which bundles the platform all the dependent services, but not the dashboard.
3. `headless` - (COMING SOON) The `headless` build which bundles only the platform. It does not come with the dashboard and/or any dependent services.

### Running the docker container <a href="#running-the-docker-container" id="running-the-docker-container"></a>

To run the container, simply execute:

```
docker run -p11901:11901 -p11902:11902 mitterio/mitter:sa
```

The two commands that are exposed make the platform and the dashboard available to the host. If you are exposing ports other than the ones mentioned, you will have to specify to the running container how to externally reach the API server. This is required for certain features in the dashboard to work (specifically the user and channels dev panel). To do so:

```
docker run -eAPI_ENDPOINT='http://localhost:1234'
    -p1234:11901 -p80:11902 mitterio/mitter:sa
```

Do note that if you do not wish to use the dashboard or do not intend to use the user and channels dev panel, specifying the step above is optional.

### Accessing the dashboard <a href="#accessing-the-dashboard" id="accessing-the-dashboard"></a>

Once the container is running, you can access the dashboard on whichever port you mapped to `11902` on the container. This might take a while.

> **NOTE** Sometimes even when the dashboard is available, you might see an error page for a while. This is due to the API server not being ready as it takes longer than the dashboard to start up. Refresh the page after sometime if that is the case.

On the dashboard you can continue to create applications, users, channels etc. as usual.

### Building apps with the docker container <a href="#building-apps-with-the-docker-container" id="building-apps-with-the-docker-container"></a>

The same SDKs and APIs work with your docker container as with the cloud solution. All SDKs provide a way to override the API URL which you must now point to the endpoint serving your docker container (and mapped to docker container `11901`).

For specific information on the above, consult the individual SDK documentation.

### Persistence <a href="#persistence" id="persistence"></a>

The provided docker image contains the required `VOLUME` directives for data to be persisted across container runs. For instance, if you were to run a container:

```
docker run -p.... mitterio/mitter:sa
```

Assume the container id was `9ba5901f6b80`, create users, send messages etc. and then stop the container and restart it:

```
docker stop 9ba5901f6b80
docker start 9ba5901f6b80
```

then all the data would be persisted between the two runs. However, the following data will be no longer available:

1. All user authorizations will no longer be available.
2. Any pending message deliveries on websockets will not be persisted and those delivery attempts will be marked as permanent failures on subsequent runs.

We are working on remedies to both the solutions. However, user authorizations will never be completely guaranteed to be persisted between container runs as they are only periodically flushed to the disk.

#### Backing up volumes <a href="#backing-up-volumes" id="backing-up-volumes"></a>

If you wish to keep all data in a location at a convenient place rather than the docker volume storage, the following mountpoints need to be mapped:

1. `/data/supernova` - This contains all the data from PostgreSQL and Minio.
2. `/logs/supernova` - This contains all the logs generated by the API server, dashboard and the dependent services (Redis, PostgreSQL, Minio).


# Custom configuration

The Mitter.io docker container comes with some parameters that are configurable. In its current format the configurability is quite limited and we plan to add a lot more options very soon.

## Basic configuration

When running the container the following environment variables can be provided:

1. `APP_AUTHORITY_NAME` - The name used to issue JWT tokens to users. On the cloud instance it is `mitter-io`
2. `API_ENDPOINT` - The publicly resolvable address for the API server.
3. `DASHBOARD_ENDPOINT` - The publicly resolvable address for the dashboard. This is not required to be modified unless you are extending the docker image and need some services to use the dashboard externally.

## Specifying an `APPLICATION_CONFIG`

To further configure the running services, you can also supply an `APPLICATION_CONFIG` containing key-value pairs for various properties. The two environment variables used would be:

1. `CENTRAL_APPLICATION_CONFIG` - This passes an application configuration object to the API server.
2. `DASHBOARD_APPLICATION_CONFIG` - This passes an application configuration object to the dashboard.

On the command line you would supply them using:

```
docker run -eCENTRAL_APPLICATION_CONFIG='{\
    "mitter.property": "value"\
}' mitterio/mitter:sa
```

Since this can get quite lengthy to type and manage on the CLI (especially with the newlines), it is recommended to store the configuration in a file, let's say `mitter.api.config` and `mitter.dashboard.config` with the specific JSON and then supply it using:

```
docker run -eCENTRAL_APPLICATION_CONFIG=`cat mitter.api.config` \
           -eDASHBOARD_APPLICATION_CONFIG=`cat mitter.dashboard.config` \
            -p11901:11901 -p11902:11902 mitterio/mitter:sa
```

Do note that you cannot nest JSON properties, the entire property path must be specified as a single string in the `key`. You can, however use integer/boolean values when applicable.

The supported properties are covered in the following sub-sections.

### Configuring the signing key for JWTs

By default a random string is used for signing the JWTs for user authorization. This might not always be desired (although will always work if the JWTs are being verified by no other than your running container) and can be overridden using (along with other parameters for JWTs) the following in `CENTRAL_APPLICATION_CONFIG`:

```
{
    "mitter.security.jwt.issuer": ".. your issuer name ...",
    "mitter.security.jwt.validity": 1000, // Milliseconds
    "mitter.security.jwt.signing-key": ".. your key, base64 encoded .."
}
```

### Configuring an external broker

To push messages via websockets from multiple docker containers behind a load balancer you will need to enable a rabbitmq server that can act as a central broker between the multiple containers.

> **NOTE** This feature is currently experimental and not officially supported.

A test container can be run simply using:

```
docker run rabbitmq
```

Refer to the rabbitmq documentation to find out the following information:

1. The port the RabbitMQ server is running on
2. The username and the password.

Then enable the broker by adding the following in `CENTRAL_APPLICATION_CONFIG`:

```
{
    "mitter.delman.websockets.broker.enabled": true,
    "mitter.delman.websockets.broker.host": ".. host ..",
    "mitter.delman.websockets.broker.port": RABBITMQ_PORT,
    "mitter.delman.websockets.broker.username": ".. rabbitmq username ..",
    "mitter.delman.websockets.broker.password": ".. rabbitmq password .."
}
```

### Storing media in S3

If you do not wish to store media from messages (file messages, image messages etc.) locally where your container is running but instead on AWS S3 then you can configure the media storage to do so.

> **NOTE** When images are stored in S3 there is no access protection mechanism active. You must designate a publically-accessible bucket that can be accessed by anyone. This is not recommended if you are handling sensitive data.

> **NOTE** This feature is currently experimental and not officially supported.

To configure the API server to store media in S3, set the following keys in your `CENTRAL_APPLICATION_CONFIG`:

```
{
    "mitter.media.store.minio.enabled": false,
    "mitter.media.store.s3.enabled": true,
    "mitter.media.store.s3.bucketname": ".. S3 bucket to store media in ..",
    "mitter.media.store.s3.aws.access-key": ".. aws access key ..",
    "mitter.media.store.s3.aws.access-secret": ".. aws secret key ..",
    "mitter.media.store.s3.aws.region": ".. aws region ..",
}
```

### Using an event bus

You can also have the docker container send installation-wide events to an AWS SNS topic. The events sent on event bus are extremely granular and can result in a huge flux of messages to your message queue. Most use-cases can be solved by using a simpler and minimal Webhooks interface.

> **NOTE** This feature is currently experimental and not officially supported.

To configure an AWS SNS queue to receive all events on your installation, set the following keys in `CENTRAL_APPLICATION_CONFIG`:

```
{
    "mitter.eventing.entbus.enabled": true,
    "mitter.eventing.entbus.sns.topic-arn": ".. aws sns topic ..",
    "mitter.eventing.entbus.sns.aws.access-key": ".. aws access key ..",
    "mitter.eventing.entbus.sns.aws.access-secret": ".. aws secret key ..",
    "mitter.eventing.entbus.sns.aws.region": ".. aws region .."
}
```


# Build Your First Android App

Get started with Mitter.io by building a fully functional app that sends/receives messages in a group of users.

> **NOTE** The Getting Started docs show you how to build your first Mitter.io app with our cloud-hosted sandbox only.
>
> To use it with your docker container, simply change the base API Url (when creating the mitter object) to the address of your running docker container.

## Introduction

Before we get started with this tutorial, let’s have a quick primer on what Mitter.io is and what we will be building in this tutorial.

### What is Mitter.io?

Mitter.io is a messaging platform that allows you to build apps around messaging. You can treat a message as something more than just an envelope for text with our platform.

### What are we going to build?

In this tutorial, we’re going to build a simple chat app which allows private replies to specific participants in a group.

### Where's the repository for this project?

You can find the complete project [**hosted on GitHub**](https://github.com/mitterio/mitter-android-demo). Feel free to clone the repository and play around.

### Tell me more!

Want a detailed guide to building with the platform? Watch our webinars [here](https://www.youtube.com/watch?v=2EERT1O0CHQ)!


# Setup

We’ll get started by setting up the SDK first.

## Android

The Mitter.io Android SDK is distributed freely via **jCenter** and you can add it easily to your project by adding it as a standard dependency.

### Creating a new project

Create a new Android app project by following the standard procedure listed below. Alternatively, you can skip the setup part by cloning [**our starter template**](https://github.com/mitterio/mitter-android-starter) and loading it into Android Studio.

To create a project from scratch, do the following:

* Go to **File**
* Click on **New**, followed by **New Project**
* Follow the steps, keep everything set to default or customize to your needs

### Adding dependency

Now add the Android SDK to your newly created project by opening up your `build.gradle` file and pasting the following line in the **dependencies** block:

{% code title="build.gradle" %}

```groovy
implementation 'io.mitter.android:core:0.1.7'
```

{% endcode %}

Once that is added, perform a Gradle sync.

### Basic setup

Now that you’ve added the SDK, the next step is to add some initial configuration for the SDK to work.

> Note: This documentation contains code snippets for both Kotlin and Java. Switch to the relevant tab while building the demo.

To start working, you need to configure a `Mitter` object with your application details, which you can access from the Mitter.io Dashboard.

Before you proceed, visit the Mitter.io Dashboard, create an application and grab the application ID.

Next, you need to create a custom implementation of the `Application` class for your app. You can do so by creating a new class and extending the `Application` class:

{% tabs %}
{% tab title="Kotlin" %}
{% code title="MyApp.kt" %}

```kotlin
class MyApp: Application() {}
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="MyApp.java" %}

```java
class MyApp extends Application {}
```

{% endcode %}
{% endtab %}
{% endtabs %}

Once that’s in place, override the `onCreate()` method. This is where you’ll be configuring the `Mitter` object.

Your custom `Application` class should look something like this:

{% tabs %}
{% tab title="Kotlin" %}
{% code title="MyApp.kt" %}

```kotlin
class MyApp: Application() {
    //Defining the mitter object
    lateinit var mitter: Mitter

    override fun onCreate() {
        super.onCreate()
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="MyApp.java" %}

```java
class MyApp extends Application {
    //Defining the mitter object
    private Mitter mitter;

    @Override
    public void onCreate() {
        super.onCreate();
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

Just defining the `Mitter` object isn’t enough. You need to configure it with the application ID that you retrieved from the Mitter.io Dashboard.

Here's how to do that: Just paste this inside your `onCreate()` method in the `MyApp` class:

{% tabs %}
{% tab title="Kotlin" %}
{% code title="MyApp.kt" %}

```kotlin
val mitterConfig = MitterConfig(
    applicationId = "your-application-id-here"
)

mitter = Mitter(
    context = this,
    mitterConfig = mitterConfig
)
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="MyApp.java" %}

```java
MitterConfig mitterConfig = new MitterConfig(
    "your-application-id-here",
    LoggingLevel.BASIC,
    null
);

mitter = new Mitter(
    this,
    mitterConfig,
    new UserAuth("", "")
);
```

{% endcode %}
{% endtab %}
{% endtabs %}

#### Using the SDK with containerised Mitter.io

If you're using the Mitter.io docker container, then you need to *override* the default API endpoint in the SDK, as follows:

{% tabs %}
{% tab title="Kotlin" %}
{% code title="MyApp.kt" %}

```kotlin
val mitterConfig = MitterConfig(
    applicationId = "your-application-id-here",
    apiEndpoint = MitterApiEndpoint(
        "http://localhost:11901",
        "http://localhost:11901"
    )
)
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="MyApp.java" %}

```java
MitterConfig mitterConfig = new MitterConfig(
    "your-application-id-here",
    LoggingLevel.BASIC,
    new MitterApiEndpoint(
        "http://localhost:11901",
        "http://localhost:11901"
    )
);
```

{% endcode %}
{% endtab %}
{% endtabs %}

That’s all you need to do for the very basic configuration. It’s not ready to connect with the platform just yet, but we’ll get to that part in the later sections.

### Setup FCM

Mitter.io works hand-in-hand with **Firebase Cloud Messaging** (FCM) to deliver messages in real-time to Android devices.

To start receiving messages, you need to setup FCM in your Android app and make a couple of changes in your application in the Mitter.io Dashboard.

First of all, you need to setup FCM in your Android project. The steps for this setup are beyond the scope of this tutorial, and since Google has done a pretty good job explaining the same, you can [check out their docs](https://firebase.google.com/docs/cloud-messaging/android/client) if you haven’t already added FCM to your project.

After you’re through with that, the only thing that's left is to feed your FCM server key in your Mitter.io application inside the Dashboard.

You can do that by following these steps:

* Open the Mitter.io Dashboard and select your application from the list
* Go to the **Properties** tab
* Click on **New Property** -> **Google** -> **FCM** -> **FCM Property**
* You’ll get a modal where you need to fill out your app’s instance ID, which can be easily retrieved from the [Google Cloud Console](https://cloud.google.com/resource-manager/docs/creating-managing-projects)​
* Also, you need to feed in your **FCM server key**, which can be accessed from your FCM admin panel
* After you’re done feeding this data, click on **New FCM Configuration Property**

Once you’ve completed these steps, you need to register a delivery endpoint for Mitter.io to deliver your messages.

In your Android project, create a class called `MyFirebaseMessagingService` which extends `FirebaseMessagingService` and override the `onNewToken()` method.

In the `onNewToken()` you need to get a reference to your `Mitter` object which you declared in your custom `Application` class.

{% tabs %}
{% tab title="Kotlin" %}
{% code title="MyFirebaseMessagingService.kt" %}

```kotlin
class MyFirebaseMessagingService: FirebaseMessagingService() {
    override fun onNewToken(token: String?) {
        val mitter = (application as MyApp).mitter
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="MyFirebaseMessagingService.java" %}

```java
public class MyFirebaseMessagingServiceJava extends FirebaseMessagingService {
    @Override
    public void onNewToken(String token) {
        Mitter mitter = ((MyApp) getApplication()).mitter;
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

Now that you have a reference to the `Mitter` object, you need to register the token with Mitter.io. Just add the following piece of code in your `onNewToken()` method:

{% tabs %}
{% tab title="Kotlin" %}
{% code title="MyFirebaseMessagingService.kt" %}

```kotlin
token?.let {
    mitter.registerFcmToken(
        it,
        object : Mitter.OnValueAvailableCallback<DeliveryEndpoint> {
            override fun onValueAvailable(value: DeliveryEndpoint) {
                //Delivery endpoint registered
            }

            override fun onError(error: ApiError) {
                //Delivery endpoint failed to register, retry
            }
        }
    )
}
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="MyFirebaseMessagingService.java" %}

```java
if (token != null) {
    mitter.registerFcmToken(
        token,
        new Mitter.OnValueAvailableCallback<DeliveryEndpoint>() {
            @Override
            public void onValueAvailable(DeliveryEndpoint deliveryEndpoint) {
                //Delivery endpoint registered
            }

            @Override
            public void onError(ApiError apiError) {
                //Delivery endpoint failed to register, retry
            }
        }
    );
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

Once that is done, the next step is to intercept any incoming push messages and pass them to the SDK for processing.

Get started by overriding the `onMessageReceived()` method and adding this piece of code inside the method:

{% tabs %}
{% tab title="Kotlin" %}
{% code title="MyFirebaseMessagingService.kt" %}

```kotlin
if (remoteMessage.data.isNotEmpty()) {
    val mitter = (application as MyApp).mitter
    val messagingPipelinePayload = mitter.parseFcmMessage(remoteMessage.data)

    if (mitter.isMitterMessage(messagingPipelinePayload)) {
        mitter.processPushMessage(messagingPipelinePayload)
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="MyFirebaseMessagingService.java" %}

```java
if (!remoteMessage.getData().isEmpty()) {
    Mitter mitter = ((MyApp) getApplication()).mitter;
    MessagingPipelinePayload messagingPipelinePayload = mitter.parseFcmMessage(remoteMessage.getData());

    if (mitter.isMitterMessage(messagingPipelinePayload)) {
        mitter.processPushMessage(messagingPipelinePayload, null);
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

Okay, the SDK is all set to intercept incoming messages and process them. The only thing that’s left is to register a callback on the `Mitter` object to get notified when a new message arrives.

Head back to your custom `Application` class and register a `OnPushMessageReceivedCallback` on the `Mitter` object that you had already created.

{% tabs %}
{% tab title="Kotlin" %}
{% code title="MyApp.kt" %}

```kotlin
mitter.registerOnPushMessageReceivedListener(object : Mitter.OnPushMessageReceivedCallback {
    override fun onChannelStreamData(
        channelId: String,
        streamId: String,
        streamData: ContextFreeMessage
    ) {
        //Called when there's some streaming data such as typing indicator
    }

    override fun onNewChannel(channel: Channel) {
        //Called when a new channel is created where the user is a participant
    }

    override fun onNewChannelTimelineEvent(
        channelId: String,
        timelineEvent: TimelineEvent
    ) {
        //Called when there's a new timeline event for a channel
    }

    override fun onNewMessage(
        channelId: String,
        message: Message
    ) {
        //Called when a new message has arrived for the user
    }

    override fun onNewMessageTimelineEvent(
        messageId: String,
        timelineEvent: TimelineEvent
    ) {
        //Called when there's a new timeline event for a message
    }

    override fun onParticipationChangedEvent(
        channelId: String,
        participantId: String,
        newStatus: ParticipationStatus,
        oldStatus: ParticipationStatus?
    ) {
        //Called when the user has joined a new channel or has been removed from one
    }
})
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="MyApp.java" %}

```java
mitter.registerOnPushMessageReceivedListener(new Mitter.OnPushMessageReceivedCallback() {
    @Override
    public void onNewMessage(
        String channelId,
        Message message
    ) {
        //Called when a new message has arrived for the user
    }

    @Override
    public void onNewChannel(Channel channel) {
        //Called when a new channel is created where the user is a participant
    }

    @Override
    public void onNewMessageTimelineEvent(
        String messageId,
        TimelineEvent timelineEvent
    ) {
        //Called when there's a new timeline event for a message
    }

    @Override
    public void onNewChannelTimelineEvent(
        String channelId,
        TimelineEvent timelineEvent
    ) {
        //Called when there's a new timeline event for a channel
    }

    @Override
    public void onParticipationChangedEvent(
        String channelId, String participantId,
        ParticipationStatus participationStatus,
        ParticipationStatus participationStatus1
    ) {
        //Called when the user has joined a new channel or has been removed from one
    }

    @Override
    public void onChannelStreamData(
        String channelId,
        String streamId,
        ContextFreeMessage contextFreeMessage
    ) {
        //Called when there's some streaming data such as typing indicator
    }
});
```

{% endcode %}
{% endtab %}
{% endtabs %}

You’re now done with the basic setup. Let’s move on to set up a user to start interacting with the platform and send messages.


# Authenticate a User

Add some users to your app and authenticate via the SDK.

Before we can authenticate a user from our app, we need to create one. You can create a user in either of these **2** ways:

* Use the dev panel (the "Users" tab when you select an application) in the Mitter.io Dashboard
* Make an API call from your backend server

In this example, we’ll be creating a user from the Mitter.io Dashboard using the dev panel.

## Create users from the dashboard

Head over to the Mitter.io Dashboard and do the following:

* Select your application from the list
* In the left sidebar, click on the **Users** icon, and then click on **New User**
* Give a name for your user and create one

Since we’ll be creating a group chat channel in the upcoming steps, go ahead and create **2** or **3** more users with different names.

Now, select one of the users you’ve created and click on **Add Token**. This will create an access token for the user. Copy the access token and keep it handy.

Follow the same process for creating a channel. Just choose the channel type as **Group Chat** and add all the users that you created in the previous step. Once your channel is created, you’ll see a channel ID. Copy it and keep it handy as well.

## Add a user to your app

Go back to your Android project and navigate to your custom `Application` class.

First, you need to define a `UserAuth` object with the user details you acquired in the previous step.

{% tabs %}
{% tab title="Kotlin" %}
{% code title="MyApp.kt" %}

```kotlin
val userAuth = UserAuth(
    userId = "8ed92f3c-0696-4513-a842-085e3cee589e",
    userAuthToken = "eyJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJtaXR0ZXItaW8iLCJ1c2VyVG9rZW5JZCI6ImJtRlI5bWNQaDhkQnJHaWIiLCJ1c2VydG9rZW4iOiJiM2dvY2ZhZ3ZyNWlrNHJkbXJlc29wNnNlcyJ9.dlE1QOYmUJpqoh1kORm3hEI3KbBM0v8kKZQnQQwXR6TZuFiCaDQrJMlp-2dgNP1CTYCPMFYoqGctRWQ5JyNiOQ"
)
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="MyApp.java" %}

```java
UserAuth userAuth = new UserAuth(
    "8ed92f3c-0696-4513-a842-085e3cee589e",
    "eyJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJtaXR0ZXItaW8iLCJ1c2VyVG9rZW5JZCI6ImJtRlI5bWNQaDhkQnJHaWIiLCJ1c2VydG9rZW4iOiJiM2dvY2ZhZ3ZyNWlrNHJkbXJlc29wNnNlcyJ9.dlE1QOYmUJpqoh1kORm3hEI3KbBM0v8kKZQnQQwXR6TZuFiCaDQrJMlp-2dgNP1CTYCPMFYoqGctRWQ5JyNiOQ"
);
```

{% endcode %}
{% endtab %}
{% endtabs %}

You can get the user ID from the list of users in the **Users** tab. Make sure that the ID and auth token both belong to the same user.

Now, just pass this `UserAuth` object to your previously configured `Mitter` object inside the `onCreate()` method in your `MyApp` class, like this:

{% tabs %}
{% tab title="Kotlin" %}
{% code title="MyApp.kt" %}

```kotlin
mitter = Mitter(
    context = this,
    mitterConfig = mitterConfig,
    userAuth = userAuth
)
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="MyApp.java" %}

```java
mitter = new Mitter(
    this,
    mitterConfig,
    userAuth
);
```

{% endcode %}
{% endtab %}
{% endtabs %}

Your Android app is now fully configured to make calls to the Mitter.io Platform. Let’s test that out by fetching some basic information for the currently logged-in user.

## Fetch currently logged-in user details

Head over to your `MainActivity` and get a reference to the `Mitter` object inside the `onCreate()` method.

{% tabs %}
{% tab title="Kotlin" %}
{% code title="MainActivity.kt" %}

```kotlin
class MainActivity : AppCompatActivity() {

    private lateinit var mitter: Mitter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        mitter = (application as MyApp).mitter
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="MainActivity.java" %}

```java
public class MainActivity extends AppCompatActivity {
    private Mitter mitter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mitter = ((MyApp) getApplication()).mitter;
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

Now, before making any calls, you need to get access to the basic group objects:

* `Users`
* `Channels`
* `Messaging`

Inside your `onCreate()`, define **3** objects like this:

{% tabs %}
{% tab title="Kotlin" %}
{% code title="MainActivity.kt" %}

```kotlin
val users = mitter.Users()
val channels = mitter.Channels()
val messaging = mitter.Messaging()
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="MainActivity.java" %}

```java
Mitter.Users users = mitter.new Users();
Mitter.Channels channels = mitter.new Channels();
Mitter.Messaging messaging = mitter.new Messaging();
```

{% endcode %}
{% endtab %}
{% endtabs %}

You’re now all set to make calls. Let’s test out our setup by making a call to fetch the currently logged-in user details.

You need to call the `getCurrentUser()` method on the `Users` object that you defined just now.

{% tabs %}
{% tab title="Kotlin" %}
{% code title="MainActivity.kt" %}

```kotlin
users.getCurrentUser(
    onValueAvailableCallback = object : Mitter.OnValueAvailableCallback<User> {
        override fun onError(apiError: ApiError) {
            Log.d("MSA", "Error while fetching user: $apiError")
        }

        override fun onValueAvailable(value: User) {
            Log.d("MSA", "User is: ${value.screenName.screenName}")
        }
    }
)
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="MainActivity.java" %}

```java
users.getCurrentUser(new Mitter.OnValueAvailableCallback<User>() {
    @Override
    public void onValueAvailable(User user) {
        Log.d("MSA", "User is: "+ user.getScreenName().getScreenName());
    }

    @Override
    public void onError(ApiError apiError) {
        Log.d("MSA", "Error while fetching user: " + apiError);
    }
});
```

{% endcode %}
{% endtab %}
{% endtabs %}

If you’ve set everything up correctly, you should see something similar to this in your app’s logs:

```
D/MSA: User is: Jason
```


# Start a Basic Chat

Let’s take this tutorial even further by building a simple chat UI and using Mitter.io to send/receive messages.

## Build the basic layouts

Before we start handling data, we need a UI to show the outgoing/incoming data. Get started by adding some layouts for your chat bubbles and the chat `RecyclerView`.

To speed up this tutorial, just copy and paste the following pieces of code to your project. This is very basic Android stuff and we’ll not be diving deep into this part.

{% code title="back\_blue\_rounded.xml" %}

```markup
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <corners android:radius="50dp" />
    <solid android:color="@color/colorPrimary" />

</shape>
```

{% endcode %}

{% code title="back\_gray\_rounded.xml" %}

```markup
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <corners android:radius="50dp" />
    <solid android:color="#EEEEEE" />

</shape>
```

{% endcode %}

{% code title="item\_message\_self.xml" %}

```markup
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <android.support.v7.widget.AppCompatTextView
        android:id="@+id/selfMessageText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="5dp"
        android:background="@drawable/back_blue_rounded"
        android:paddingBottom="10dp"
        android:paddingEnd="20dp"
        android:paddingStart="20dp"
        android:paddingTop="10dp"
        android:textColor="@android:color/white"
        android:textSize="16sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="Hi, there!" />

</android.support.constraint.ConstraintLayout>
```

{% endcode %}

{% code title="item\_message\_other.xml" %}

```markup
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <android.support.v7.widget.AppCompatTextView
        android:id="@+id/otherMessageText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="5dp"
        android:background="@drawable/back_gray_rounded"
        android:padding="10dp"
        android:paddingBottom="10dp"
        android:paddingEnd="20dp"
        android:paddingStart="20dp"
        android:paddingTop="10dp"
        android:textColor="@android:color/black"
        android:textSize="16sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="Hello!" />

</android.support.constraint.ConstraintLayout>
```

{% endcode %}

So, you’ve added some basic layouts for rendering the chat bubbles. Now, let’s style the chat window. Open up your `activity_main.xml` layout file and add the following code:

{% code title="activity\_main.xml" %}

```markup
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white"
    tools:context=".MainActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/chatRecyclerView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginBottom="10dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintBottom_toTopOf="@id/inputMessage"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="#EEEEEE"
        app:layout_constraintTop_toBottomOf="@id/chatRecyclerView" />

    <Button
        android:id="@+id/sendButton"
        style="@style/Base.Widget.AppCompat.Button.Borderless"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="5dp"
        android:text="Send"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <android.support.v7.widget.AppCompatEditText
        android:id="@+id/inputMessage"
        android:layout_width="0dp"
        android:layout_height="40dp"
        android:layout_marginBottom="10dp"
        android:layout_marginEnd="10dp"
        android:layout_marginStart="20dp"
        android:background="@android:color/white"
        android:hint="Type your message"
        android:inputType="textCapSentences"
        android:textSize="16sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@id/sendButton"
        app:layout_constraintStart_toStartOf="parent" />

</android.support.constraint.ConstraintLayout>
```

{% endcode %}

## Prepare the adapter

Now that your layouts are in place, get started by adding an adapter for your `RecyclerView`.

Assuming you’ve named your adapter `ChatRecyclerViewAdapter`, your class should look something like this:

{% tabs %}
{% tab title="Kotlin" %}
{% code title="ChatRecyclerViewAdapter.kt" %}

```kotlin
class ChatRecyclerViewAdapter(
    private val messageList: List<Message>,
    private val currentUserId: String
) : RecyclerView.Adapter<ChatRecyclerViewAdapter.ViewHolder>() {
    private val MESSAGE_SELF_VIEW = 0
    private val MESSAGE_OTHER_VIEW = 1

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val layoutId = if (viewType == MESSAGE_SELF_VIEW) R.layout.item_message_self else R.layout.item_message_other
        val itemView = LayoutInflater.from(parent.context).inflate(layoutId, parent, false)
        return ViewHolder(itemView)
    }

    override fun getItemCount(): Int = messageList.size

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bindMessage(messageList[position])
    }

    override fun getItemViewType(position: Int) = if (messageList[position].senderId.domainId() == currentUserId)
        MESSAGE_SELF_VIEW else MESSAGE_OTHER_VIEW

    inner class ViewHolder(itemView: View?) : RecyclerView.ViewHolder(itemView) {
        fun bindMessage(message: Message) {
            with(message) {
                if (senderId.domainId() == currentUserId) {
                    itemView?.selfMessageText?.text = textPayload
                } else {
                    itemView?.otherMessageText?.text = textPayload
                }
            }
        }
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="ChatRecyclerViewAdapter.java" %}

```java
public class ChatRecyclerViewAdapter extends RecyclerView.Adapter<ChatRecyclerViewAdapter.ViewHolder> {
    private List<Message> messageList;
    private String currentUserId;

    private int MESSAGE_SELF_VIEW = 0;
    private int MESSAGE_OTHER_VIEW = 1;

    public ChatRecyclerViewAdapter(
        List<Message> messageList,
        String currentUserId
    ) {
        this.messageList = messageList;
        this.currentUserId = currentUserId;
    }

    @NonNull
    @Override
    public ChatRecyclerViewAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        int layoutId;

        if (viewType == MESSAGE_SELF_VIEW) {
            layoutId = R.layout.item_message_self;
        } else {
            layoutId = R.layout.item_message_other;
        }

        View itemView = LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false);
        return new ViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(@NonNull ChatRecyclerViewAdapter.ViewHolder holder, int position) {
        Message message = messageList.get(position);

        if (message.getSenderId().domainId().equals(currentUserId)) {
            holder.selfMessageText.setText(message.getTextPayload());
        } else {
            holder.otherMessageText.setText(message.getTextPayload());
        }
    }

    @Override
    public int getItemCount() {
        return messageList != null ? messageList.size() : 0;
    }

    @Override
    public int getItemViewType(int position) {
        if (messageList.get(position).getSenderId().domainId().equals(currentUserId)) {
            return MESSAGE_SELF_VIEW;
        } else {
            return MESSAGE_OTHER_VIEW;
        }
    }

    class ViewHolder extends RecyclerView.ViewHolder {
        private TextView selfMessageText;
        private TextView otherMessageText;

        public ViewHolder(View itemView) {
            super(itemView);

            selfMessageText = (TextView) itemView.findViewById(R.id.selfMessageText);
            otherMessageText = (TextView) itemView.findViewById(R.id.otherMessageText);
        }
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

Here, we receive a list of `Message` objects to display the chat items, and compare the `senderId` field to the `currentUserId` field to decide whether the bubble should render on the right or on the left.

## Render messages in the window

The final step to rendering the messages in a channel to the screen is to wire up the `ChatRecyclerViewAdapter` that you created to the `RecyclerView` that you had already added to your layout.

Get started by initialising an empty list for holding the incoming `Message` objects and setting a layout manager for your `RecyclerView`.

{% tabs %}
{% tab title="Kotlin" %}
{% code title="MainActivity.kt" %}

```kotlin
class MainActivity : AppCompatActivity() {

    private lateinit var mitter: Mitter
    private val channelId: String = "6dedfac5-8060-4ad3-8d69-20a72fb86899"

    private val messageList = mutableListOf<Message>()
    private lateinit var chatRecyclerViewAdapter: ChatRecyclerViewAdapter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        mitter = (application as MyApp).mitter

        val users = mitter.Users()
        val channels = mitter.Channels()
        val messaging = mitter.Messaging()

        chatRecyclerView.layoutManager = LinearLayoutManager(this)
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="MainActivity.java" %}

```java
public class MainActivity extends AppCompatActivity {
    private Mitter mitter;
    private String channelId = "6dedfac5-8060-4ad3-8d69-20a72fb86899";

    private RecyclerView chatRecyclerView;
    private List<Message> messageList = new ArrayList<>();
    private ChatRecyclerViewAdapter chatRecyclerViewAdapter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mitter = ((MyApp) getApplication()).mitter;

        Mitter.Users users = mitter.new Users();
        Mitter.Channels channels = mitter.new Channels();
        Mitter.Messaging messaging = mitter.new Messaging();

        chatRecyclerView = (RecyclerView) findViewById(R.id.chatRecyclerView);
        chatRecyclerView.setLayoutManager(new LinearLayoutManager(this));
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

Now, you need to make a call to fetch messages in the channel that you had already created.

If you’ve noticed, in the previous code snippet, the channel ID is already defined in the class. Replace the channel ID string with your actual channel ID over there.

Once that’s done, just make a call to the `getMessagesInChannel()` method on the `Messaging` object to get a list of messages in the channel:

{% tabs %}
{% tab title="Kotlin" %}
{% code title="MainActivity.kt" %}

```kotlin
messaging.getMessagesInChannel(
    channelId = channelId,
    onValueAvailableCallback = object : Mitter.OnValueAvailableCallback<List<Message>> {
        override fun onError(apiError: ApiError) {
            Log.d("MSA", "Error in getting messages")
        }

        override fun onValueAvailable(value: List<Message>) {
            messageList.addAll(value)
            chatRecyclerViewAdapter = ChatRecyclerViewAdapter(
                messageList = messageList,
                currentUserId = mitter.getUserId()
            )

            chatRecyclerView?.adapter = chatRecyclerViewAdapter
        }
    }
)
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="MainActivity.java" %}

```java
messaging.getMessagesInChannel(
    channelId,
    new FetchMessageConfig(),
    new Mitter.OnValueAvailableCallback<List<Message>>() {
        @Override
        public void onValueAvailable(List<Message> messages) {
            messageList.addAll(messages);
            chatRecyclerViewAdapter = new ChatRecyclerViewAdapter(
                messageList,
                mitter.getUserId()
            );

            chatRecyclerView.setAdapter(chatRecyclerViewAdapter);
        }

        @Override
        public void onError(ApiError apiError) {
            Log.d("MSA", "Error in getting messages");
        }
    }
);
```

{% endcode %}
{% endtab %}
{% endtabs %}

Here, we add the total incoming message list to our already defined empty message list. Next, we hook up that list with our `ChatRecyclerViewAdapter` and also retrieve the currently logged-in user ID and pass it to the adapter for the bubble alignment that we discussed previously.

The final step is to assign our `ChatRecyclerViewAdapter` object as the `RecyclerView` adapter to render the elements on the screen.

If you open up your app right now, it’ll display a blank screen because you haven’t sent any messages yet.

![Basic chat window layout](https://94728489-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LLZR00Qt6hZ5Vke2l2g%2F-LN1aYr9JqmZSDBu3tae%2F-LN1bpnlo2zdcqecEgkd%2FCleanShot%202018-09-23%20at%2000.21.02%402x.png?alt=media\&token=0f75b877-bcc6-4b95-a671-0d096bc42bb7)

Let’s do that now.

## Send a basic text message

We’ve already added a basic `EditText` and a `Button` to our app while defining the layouts. Let’s connect them in the activity to send the typed text on the press of the **Send** button.

{% tabs %}
{% tab title="Kotlin" %}
{% code title="MainActivity.kt" %}

```kotlin
sendButton.setOnClickListener {
    messaging.sendTextMessage(
        channelId = channelId,
        message = inputMessage?.text.toString(),
        onValueUpdatedCallback = object : Mitter.OnValueUpdatedCallback {
            override fun onError(apiError: ApiError) {}

            override fun onSuccess() {
                inputMessage?.text?.clear()
            }
        }
    )
}
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="MainActivity.java" %}

```java
sendButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        messaging.sendTextMessage(
            channelId,
            inputMessage.getText().toString(),
            new AppliedAclList(
                new ArrayList<AppliedAcl>(),
                new ArrayList<AppliedAcl>()
            ),
            new Mitter.OnValueUpdatedCallback() {
                @Override
                public void onSuccess() {
                    inputMessage.getText().clear();
                }

                @Override
                public void onError(ApiError apiError) { }
            }
        );
    }
});
```

{% endcode %}
{% endtab %}
{% endtabs %}

Here, we’re listening to the click events on the **Send** button and sending a message by calling the `sendTextMessage()` method on the `Messaging` object. We pass the typed text to the method as the message text.

After the message has been sent successfully, we clear the input area. You can also show a progress bar while sending the message by utilising this callback.

That’s all wired in. You can test if this works by typing something in the input field and hitting **Send**.

Do note that you won’t see the message appear on the screen even after it’s sent successfully. This is because we haven’t hooked our push message listener with the UI.

We’ll do this in the next section. For now, after sending a message, just close and reopen the app to see the message populated on the screen.

![Messages in the channel being populated in the chat window](https://94728489-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LLZR00Qt6hZ5Vke2l2g%2F-LN1aYr9JqmZSDBu3tae%2F-LN1d_wo4b3ljmR-3ov6%2FCleanShot%202018-09-23%20at%2000.28.40%402x.png?alt=media\&token=949119f8-7b0b-40e9-a030-1fd9f4a0c147)

## Hook push messages with the UI

You can choose any way you want to pass around data from your `Application` class to your `MainActivity`. For this tutorial, we’ll stick to an event bus to do our job.

We’ll use [Greenrobot’s EventBus 3](https://github.com/greenrobot/EventBus) for this project. It’s pretty easy to use, while being reliable. Add the library to your project by including this line in your `build.gradle`:

{% code title="build.gradle" %}

```groovy
implementation 'org.greenrobot:eventbus:3.1.1'
```

{% endcode %}

Once that’s done, add a subscribing method in your `MainActivity` to listen to the incoming messages:

{% tabs %}
{% tab title="Kotlin" %}
{% code title="MainActivity.kt" %}

```kotlin
@Subscribe(threadMode = ThreadMode.MAIN)
fun onNewMessage(message: Message) {
    messageList.add(message)
    chatRecyclerViewAdapter.notifyItemInserted(messageList.size - 1)
}
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="MainActivity.java" %}

```java
@Subscribe(threadMode = ThreadMode.MAIN)
public void onNewMessage(Message message) {
    messageList.add(message);
    chatRecyclerViewAdapter.notifyItemInserted(messageList.size() - 1);
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

After that, you need to register your `MainActivity` to listen to any event bus events. Just modify your `onCreate()` to include this code:

{% tabs %}
{% tab title="Kotlin" %}
{% code title="MainActivity.kt" %}

```kotlin
EventBus.getDefault().register(this)
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="MainActivity.java" %}

```java
EventBus.getDefault().register(this);
```

{% endcode %}
{% endtab %}
{% endtabs %}

Then, override the `onDestroy()` method to clean up any registered listeners:

{% tabs %}
{% tab title="Kotlin" %}
{% code title="MainActivity.kt" %}

```kotlin
override fun onDestroy() {
    super.onDestroy()

    if (EventBus.getDefault().isRegistered(this)) {
        EventBus.getDefault().unregister(this)
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="MainActivity.java" %}

```java
@Override
protected void onDestroy() {
    super.onDestroy();

    if (EventBus.getDefault().isRegistered(this)) {
        EventBus.getDefault().unregister(this);
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

Finally, navigate to your custom `Application` class, locate the previously registered push message listener and modify it to this:

{% tabs %}
{% tab title="Kotlin" %}
{% code title="MyApp.kt" %}

```kotlin
mitter.registerOnPushMessageReceivedListener(object : Mitter.OnPushMessageReceivedCallback {
    override fun onChannelStreamData(
        channelId: String,
        streamId: String,
        streamData: ContextFreeMessage
    ) {}

    override fun onNewChannel(channel: Channel) {}

    override fun onNewChannelTimelineEvent(
        channelId: String,
        timelineEvent: TimelineEvent
    ) {}

    override fun onNewMessage(
        channelId: String,
        message: Message
    ) {
        //Send the incoming message to the registered listener
        EventBus.getDefault().post(message)
    }

    override fun onNewMessageTimelineEvent(
        messageId: String,
        timelineEvent: TimelineEvent
    ) {}

    override fun onParticipationChangedEvent(
        channelId: String,
        participantId: String,
        newStatus: ParticipationStatus,
        oldStatus: ParticipationStatus?
    ) {}
})
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="MyApp.java" %}

```java
mitter.registerOnPushMessageReceivedListener(new Mitter.OnPushMessageReceivedCallback() {
    @Override
    public void onNewMessage(
        String channelId,
        Message message
    ) {
        //Send the incoming message to the registered listener
        EventBus.getDefault().post(message);
    }

    @Override
    public void onNewChannel(Channel channel) { }

    @Override
    public void onNewMessageTimelineEvent(
        String messageId,
        TimelineEvent timelineEvent
    ) { }

    @Override
    public void onNewChannelTimelineEvent(
        String channelId,
        TimelineEvent timelineEvent
    ) { }

    @Override
    public void onParticipationChangedEvent(
        String channelId, String participantId,
        ParticipationStatus participationStatus,
        ParticipationStatus participationStatus1
    ) { }

    @Override
    public void onChannelStreamData(
        String channelId,
        String streamId,
        ContextFreeMessage contextFreeMessage
    ) { }
});
```

{% endcode %}
{% endtab %}
{% endtabs %}

If you open up your app now and send a text message, you should be able to see it added to the list as soon as the message is sent.

![Messages are being transferred in and out the app in real-time](https://94728489-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LLZR00Qt6hZ5Vke2l2g%2F-LN1h30Oe74Lam4c6IjN%2F-LN1h74b8UEKWkyJru_v%2FCleanShot%202018-09-23%20at%2000.43.34.gif?alt=media\&token=84aee0d9-7a5b-4a14-9b9f-4f3ee7aa6ec4)


# Selective Deliveries

Mitter provides Access Control Lists (ACLs) out of the box to let you choose whom to deliver a message to in a group channel. Let’s add a little function to our app to see this in action.

## Login as different users

We created multiple users for our app through the Mitter.io Dashboard at the beginning of this guide. If you haven’t already, take some time to create **3** or more users and copy down their IDs and auth tokens.

Now, you can obviously put up a login page to your app and use a backend server as a token server to login as a user; but for the sake of simplicity, we’ll just swap out the user credentials before building our app.

Get started by defining **3** `UserAuth` objects inside the `onCreate()` method in your `MyApp` class like this:

{% tabs %}
{% tab title="Kotlin" %}
{% code title="MyApp.kt" %}

```kotlin
val jasonAuth = UserAuth(
    userId = "8ed92f3c-0696-4513-a842-085e3cee589e",
    userAuthToken = "eyJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJtaXR0ZXItaW8iLCJ1c2VyVG9rZW5JZCI6IlJkZHZCNXJ5RGdPNUpvSkoiLCJ1c2VydG9rZW4iOiI5cXA1MjQyY2N1NW9lbHI3OTRzc2ltbDY3OCJ9.aonmZhZWCyIHJR6nxlNn_KSgAvdWlB4vtZgfdXbvlIvXBM5oNaUzpF3YbfAlZeyPr8_uMf8HCcoh4dVFr-lYFw"
)

val katieAuth = UserAuth(
    userId = "cb02bc00-979e-4db2-8625-116178c4ad95",
    userAuthToken = "eyJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJtaXR0ZXItaW8iLCJ1c2VyVG9rZW5JZCI6Ilg2ZXZKZDVHSmx1SU5URWEiLCJ1c2VydG9rZW4iOiJwdnA2MGFwa3NpYjgzY21yOGI1N2g0YmhpYSJ9.hVw0h3hmtOQlx9phWrJ7zK9an9GmXv9H481OjrbIPOmE58g86EqozHLhASwl0jdiqC5KjU1_nrIK40pmNEvY9w"
)

val samAuth = UserAuth(
    userId = "5cfe3da1-4467-49b8-8325-3e85cec31c5a",
    userAuthToken = "eyJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJtaXR0ZXItaW8iLCJ1c2VyVG9rZW5JZCI6IlhoSnV5dGw0OG5OSlBJVDYiLCJ1c2VydG9rZW4iOiIyamozaTdhNTA5Yzc3c2Vva2dscGdiZjBpNiJ9.BwPGV2kTdclHdRaIzqjR6yAascqvYE52tH2iLK2aDBaBZA841SM0gW0WdblxLYeAyyM-XKXIEHwM2K0xQwoh7w"
)
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="MyApp.java" %}

```java
UserAuth jasonAuth = new UserAuth(
    "8ed92f3c-0696-4513-a842-085e3cee589e",
    "eyJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJtaXR0ZXItaW8iLCJ1c2VyVG9rZW5JZCI6ImJtRlI5bWNQaDhkQnJHaWIiLCJ1c2VydG9rZW4iOiJiM2dvY2ZhZ3ZyNWlrNHJkbXJlc29wNnNlcyJ9.dlE1QOYmUJpqoh1kORm3hEI3KbBM0v8kKZQnQQwXR6TZuFiCaDQrJMlp-2dgNP1CTYCPMFYoqGctRWQ5JyNiOQ"
);

UserAuth katieAuth = new UserAuth(
    "cb02bc00-979e-4db2-8625-116178c4ad95",
    "eyJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJtaXR0ZXItaW8iLCJ1c2VyVG9rZW5JZCI6Ilg2ZXZKZDVHSmx1SU5URWEiLCJ1c2VydG9rZW4iOiJwdnA2MGFwa3NpYjgzY21yOGI1N2g0YmhpYSJ9.hVw0h3hmtOQlx9phWrJ7zK9an9GmXv9H481OjrbIPOmE58g86EqozHLhASwl0jdiqC5KjU1_nrIK40pmNEvY9w"
);
​
UserAuth samAuth = new UserAuth(
    "5cfe3da1-4467-49b8-8325-3e85cec31c5a",
    "eyJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJtaXR0ZXItaW8iLCJ1c2VyVG9rZW5JZCI6IlhoSnV5dGw0OG5OSlBJVDYiLCJ1c2VydG9rZW4iOiIyamozaTdhNTA5Yzc3c2Vva2dscGdiZjBpNiJ9.BwPGV2kTdclHdRaIzqjR6yAascqvYE52tH2iLK2aDBaBZA841SM0gW0WdblxLYeAyyM-XKXIEHwM2K0xQwoh7w"
);
```

{% endcode %}
{% endtab %}
{% endtabs %}

Next, fire up **3** emulator instances or **3** devices, whichever you prefer, each time swapping out the `userAuth` parameter on the `Mitter` object configuration defined in your `Application` class. Like this:

{% tabs %}
{% tab title="Kotlin" %}
{% code title="MyApp.kt" %}

```kotlin
mitter = Mitter(
    context = this,
    mitterConfig = mitterConfig,
    userAuth = samAuth
)
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="MyApp.java" %}

```java
mitter = new Mitter(
    this,
    mitterConfig,
    samAuth
);
```

{% endcode %}
{% endtab %}
{% endtabs %}

Therefore, first, you can pass `userAuth` as `jasonAuth` and run the project on one emulator instance. Then you can change the `userAuth` to `katieAuth` and then run on a different emulator instance.

This way you can get **3** different users in your **3** emulator instances, and then you can chat between the **3** in real-time.

## Implement the @username mapping

For our demo, we’ll be adding a feature where a user can mention another specific user by the `@username` notation while typing their messages, and the message will only be visible to the mentioned user and the sender.

Before we can get started on that, we need to have a local mapping of the usernames with their actual user IDs on the platform.

Define an object class (or a class with static methods, if using Java) named `UserIdProvider` and add the following piece of code, changing the user IDs and names to your actual user details.

{% tabs %}
{% tab title="Kotlin" %}
{% code title="UserIdProvider.kt" %}

```kotlin
object UserIdProvider {
    fun getUserId(username: String): String = when (username.toLowerCase()) {
        "sam" -> "5cfe3da1-4467-49b8-8325-3e85cec31c5a"
        "jason" -> "8ed92f3c-0696-4513-a842-085e3cee589e"
        "katie" -> "cb02bc00-979e-4db2-8625-116178c4ad95"
        else -> ""
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="UserIdProvider.java" %}

```java
public class UserIdProvider {
    public static String getUserId(String username) {
        switch (username.toLowerCase()) {
            case "sam":
                return "5cfe3da1-4467-49b8-8325-3e85cec31c5a";
            case "jason":
                return "8ed92f3c-0696-4513-a842-085e3cee589e";
            case "katie":
                return "cb02bc00-979e-4db2-8625-116178c4ad95";
            default:
                return "";
        }
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

Do note that these usernames are just local names that you can use to identify a user with the `@username` notation.

## Add ACL rules

Now that you’ve defined the mapping, you need to define some ACL rules that you’ll be passing along with your messages to make the selective delivery work.

Create another object class (or a plain class with static method, if using Java) called `AclUtils` and add the following piece of code:

{% tabs %}
{% tab title="Kotlin" %}
{% code title="AclUtils.kt" %}

```kotlin
object AclUtils {
    fun meAndSam(senderId: String): AppliedAclList {
        return AppliedAclList(
            plusAppliedAcls = listOf(
                AppliedAcl
                (
                    ReadMessagePrivilege(),
                    UserIdAccessorSelector(IdUtils.of(UserIdProvider.getUserId("sam")))
                ),
                AppliedAcl
                (
                    ReadMessagePrivilege(),
                    UserIdAccessorSelector(IdUtils.of(senderId))
                )
            ),
            minusAppliedAcls = emptyList()
        )
    }

    fun meAndJason(senderId: String): AppliedAclList {
        return AppliedAclList(
            plusAppliedAcls = listOf(
                AppliedAcl
                (
                    ReadMessagePrivilege(),
                    UserIdAccessorSelector(IdUtils.of(UserIdProvider.getUserId("jason")))
                ),
                AppliedAcl
                (
                    ReadMessagePrivilege(),
                    UserIdAccessorSelector(IdUtils.of(senderId))
                )
            ),
            minusAppliedAcls = emptyList()
        )
    }

    fun meAndKatie(senderId: String): AppliedAclList {
        return AppliedAclList(
            plusAppliedAcls = listOf(
                AppliedAcl
                (
                    ReadMessagePrivilege(),
                    UserIdAccessorSelector(IdUtils.of(UserIdProvider.getUserId("katie")))
                ),
                AppliedAcl
                (
                    ReadMessagePrivilege(),
                    UserIdAccessorSelector(IdUtils.of(senderId))
                )
            ),
            minusAppliedAcls = emptyList()
        )
    }

    fun getAclListFromUsername(username: String, senderId: String) = when (username) {
        "sam" -> meAndSam(senderId)
        "jason" -> meAndJason(senderId)
        "katie" -> meAndKatie(senderId)
        else -> emptyAclList()
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="AclUtils.java" %}

```java
public class AclUtils {
    public static AppliedAclList meAndSam(String senderId) {
        List<AppliedAcl> plusAppliedAcls = new ArrayList<>();
        List<AppliedAcl> minusAppliedAcls = new ArrayList<>();

        plusAppliedAcls.add(
            new AppliedAcl(
                new ReadMessagePrivilege(),
                new UserIdAccessorSelector(IdUtils.of(UserIdProvider.getUserId("sam"), User.class))
            )
        );
        plusAppliedAcls.add(
            new AppliedAcl(
                new ReadMessagePrivilege(),
                new UserIdAccessorSelector(IdUtils.of(senderId, User.class))
            )
        );

        return new AppliedAclList(
            plusAppliedAcls,
            minusAppliedAcls
        );
    }

    public static AppliedAclList meAndJason(String senderId) {
        List<AppliedAcl> plusAppliedAcls = new ArrayList<>();
        List<AppliedAcl> minusAppliedAcls = new ArrayList<>();

        plusAppliedAcls.add(
            new AppliedAcl(
                new ReadMessagePrivilege(),
                new UserIdAccessorSelector(IdUtils.of(UserIdProvider.getUserId("jason"), User.class))
            )
        );
        plusAppliedAcls.add(
            new AppliedAcl(
                new ReadMessagePrivilege(),
                new UserIdAccessorSelector(IdUtils.of(senderId, User.class))
            )
        );

        return new AppliedAclList(
            plusAppliedAcls,
            minusAppliedAcls
        );
    }

    public static AppliedAclList meAndKatie(String senderId) {
        List<AppliedAcl> plusAppliedAcls = new ArrayList<>();
        List<AppliedAcl> minusAppliedAcls = new ArrayList<>();

        plusAppliedAcls.add(
            new AppliedAcl(
                new ReadMessagePrivilege(),
                new UserIdAccessorSelector(IdUtils.of(UserIdProvider.getUserId("katie"), User.class))
            )
        );
        plusAppliedAcls.add(
            new AppliedAcl(
                new ReadMessagePrivilege(),
                new UserIdAccessorSelector(IdUtils.of(senderId, User.class))
            )
        );

        return new AppliedAclList(
            plusAppliedAcls,
            minusAppliedAcls
        );
    }

    public static AppliedAclList getAclListFromUsername(String username, String senderId) {
        switch (username) {
            case "sam":
                return meAndSam(senderId);
            case "jason":
                return meAndJason(senderId);
            case "katie":
                return meAndKatie(senderId);
            default:
                return new AppliedAclList(new ArrayList<AppliedAcl>(), new ArrayList<AppliedAcl>());
        }
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

Here, we just define some basic ACL rules based on the chosen username. You can learn more about ACLs and how to use them over [**here**](/platform-reference-1/acls-and-advanced-permission-model).

For example, the method `meAndSam()` provides an `AppliedAclList` object which tells Mitter.io that the message is only for Sam and the current sender to view. Anyone else in the group will be unaware of this message.

This way you can have a private conversation between users in a group of multiple users.

## Apply ACLs to your outgoing messages

The `sendTextMessage()` method that we used previously to send out messages, accepts another optional parameter called `appliedAcls` where you can pass an `AppliedAclList` object containing your ACL rules for the message you’re sending.

The goal here is to parse the input text and check if there’s an `@` symbol present in the message and then extract the username from that message to determine our ACLs.

We’ll be using our utility methods to determine the ACLs for the username that we get from the input text.

Modify your button click event to this:

{% tabs %}
{% tab title="Kotlin" %}
{% code title="MainActivity.kt" %}

```kotlin
sendButton.setOnClickListener {
    var typedInput = inputMessage?.text.toString()
    var appliedAcls = emptyAclList()

    if (typedInput.contains('@')) {
        val username = typedInput.substring(1, typedInput.indexOf(' '))
        typedInput = typedInput.substringAfter(' ')
        appliedAcls = AclUtils.getAclListFromUsername(username, mitter.getUserId())
    }

    messaging.sendTextMessage(
        channelId = channelId,
        message = typedInput,
        appliedAcls = appliedAcls,
        onValueUpdatedCallback = object : Mitter.OnValueUpdatedCallback {
            override fun onError(apiError: ApiError) {}

            override fun onSuccess() {
                inputMessage?.text?.clear()
            }
        }
    )
}
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="MainActivity.java" %}

```java
sendButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        String typedInput = inputMessage.getText().toString();
        AppliedAclList appliedAcls = new AppliedAclList(
            new ArrayList<AppliedAcl>(),
            new ArrayList<AppliedAcl>()
        );

        if (typedInput.contains("@")) {
            String username = typedInput.substring(1, typedInput.indexOf(" "));
            typedInput = typedInput.substring(typedInput.indexOf(" "));
            appliedAcls = AclUtils.getAclListFromUsername(username, mitter.getUserId());
        }

        messaging.sendTextMessage(
            channelId,
            typedInput,
            appliedAcls,
            new Mitter.OnValueUpdatedCallback() {
                @Override
                public void onSuccess() {
                    inputMessage.getText().clear();
                }

                @Override
                public void onError(ApiError apiError) { }
            }
        );
    }
});
```

{% endcode %}
{% endtab %}
{% endtabs %}

Here, we just do a basic check for the `@` symbol, substring the text accordingly, and pass the message along with the applied ACLs to the `sendTextMessage()` method.

Now, if you run the app on all the **3** emulator instances with **3** different users, you can chat among the users and send private messages by just mentioning a user’s name prefixed with an `@` symbol.

For example, if **Katie** would like to send a message to just Jason, she can type “*@jason What’s up?*”.

This will make the message visible only to **Katie** and **Jason**. **Sam** will be totally unaware that this conversation ever happened.

![Selective delivery in action](https://94728489-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LLZR00Qt6hZ5Vke2l2g%2F-LNFsCslCk-iJI7AheSA%2F-LNFsJMTeEH43DwhLwgO%2FCleanShot-2018-09-23-at-01.gif?alt=media\&token=4588cb6b-02e3-47b5-b6d2-04705fdb49c5)

That’s how you can use ACLs to control who sees what. Feel free to define your own use cases and play around.


# Custom Payloads

Learn how to add actions & extra information to your messages and build a playful and engaging chat experience.

In the previous chapter, we learnt how to send messages to specific people in a group chat with ACLs.

In this chapter, we’ll see how to take the demo app one step further by adding some **custom payloads** to our outgoing messages.

## Schedule meetings on chat

To demonstrate the power and use of custom payloads, we’ll be letting our users schedule meetings on our app. The basic workflow is as follows:

* Pick a date while sending a message
* Receivers confirm their availability with a **Yes/No** action

## Add the DatePickerDialog

We’ll use the standard `DatePickerDialog` to render a calendar dialog on the screen and allow the user to pick a date from the displayed calendar.

Get started by adding a button to the chat screen. For this example, you can use this vector drawable as the calendar button. Just copy this code to your project under the `drawable` package:

Now, navigate to your `activity_main.xml` and modify the code to look similar to this:

{% code title="activity\_main.xml" %}

```markup
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white"
    tools:context=".MainActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/chatRecyclerView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginBottom="10dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintBottom_toTopOf="@id/inputMessage"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <View
        android:id="@+id/divider"
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="@color/colorGray"
        app:layout_constraintTop_toBottomOf="@id/chatRecyclerView" />

    <Button
        android:id="@+id/sendButton"
        style="@style/Base.Widget.AppCompat.Button.Borderless"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="5dp"
        android:text="Send"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <ImageView
        android:id="@+id/pickDateButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_date_range_black_24dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@id/sendButton"
        app:layout_constraintTop_toBottomOf="@id/divider" />

    <android.support.v7.widget.AppCompatEditText
        android:id="@+id/inputMessage"
        android:layout_width="0dp"
        android:layout_height="40dp"
        android:layout_marginBottom="10dp"
        android:layout_marginEnd="10dp"
        android:layout_marginStart="20dp"
        android:background="@android:color/white"
        android:hint="Type your message"
        android:inputType="textCapSentences"
        android:textSize="16sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@id/pickDateButton"
        app:layout_constraintStart_toStartOf="parent" />

</android.support.constraint.ConstraintLayout>
```

{% endcode %}

If you compare this file to your previously created layout, you’ll see that we’ve just added an `ImageView` showing up the calendar icon that we added in the previous step and adjusted the surrounding views to accommodate this icon.

Lastly, go to your `MainActivity` and add the listener for this button to open up the `DatePickerDialog` and listen for inputs from the user.

Define an instance variable in your `MainActivity` to hold the picked date:

{% tabs %}
{% tab title="Kotlin" %}
{% code title="MainActivity.kt" %}

```kotlin
private var pickedDate: String = ""
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="MainActivity.java" %}

```kotlin
private String pickedDate = ""
```

{% endcode %}
{% endtab %}
{% endtabs %}

Then, add this block of code inside your `onCreate()` method:

{% tabs %}
{% tab title="Kotlin" %}
{% code title="MainActivity.kt" %}

```kotlin
pickDateButton?.setOnClickListener {
        val calendar = Calendar.getInstance()
        val year = calendar.get(Calendar.YEAR)
        val month = calendar.get(Calendar.MONTH)
        val day = calendar.get(Calendar.DAY_OF_MONTH)

        val datePickerDialog = DatePickerDialog(
            this,
            { _, year, month, day ->
                pickedDate = "$day/${month + 1}/$year"
            },
            year,
            month,
            day
        )

        datePickerDialog.show()
}
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="MainActivity.java" %}

```java
pickDateButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Calendar calendar = Calendar.getInstance();
        final int year = calendar.get(Calendar.YEAR);
        final int month = calendar.get(Calendar.MONTH);
        final int day = calendar.get(Calendar.DAY_OF_MONTH);

        DatePickerDialog datePickerDialog = new DatePickerDialog(
            MainActivity.this,
            new DatePickerDialog.OnDateSetListener() {
                @Override
                public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
                    pickedDate = dayOfMonth + "/" + (month + 1) + "/" + year;
                }
            },
            year,
            month,
            day
        );

        datePickerDialog.show();
    }
});
```

{% endcode %}
{% endtab %}
{% endtabs %}

> Note: To use the `DatePickerDialog` you need to have your `minSdkVersion` set to 24 or above

![Choosing a date for the meeting using the built-in date picker dialog](https://94728489-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LLZR00Qt6hZ5Vke2l2g%2F-LNLUvTsd3Vs4l6lZaxP%2F-LNLV1hVYnjOAEOIEdgH%2FCleanShot%202018-09-26%20at%2020.58.41.gif?alt=media\&token=4f0c6ed9-6e1d-4cf9-a3a0-9f0a68e32bd0)

## Send a message with the picked date

Now that we’ve added the functionality to capture a date from the user, we can send it along with our message to be used by our `RecyclerView` to render messages differently.

Get started by adding a new data class (or a POJO, if using Java) to hold the picked date:

{% tabs %}
{% tab title="Kotlin" %}
{% code title="PickedDate.kt" %}

```kotlin
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
data class PickedDate(
    @JsonProperty("date") val date: String
)
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="PickedDate.java" %}

```java
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class PickedDate {
    @JsonProperty("date")
    String date;

    public PickedDate() {
    }

    public PickedDate(String date) {
        this.date = date;
    }

    public String getDate() {
        return date;
    }

    public void setDate(String date) {
        this.date = date;
    }

    @Override
    public String toString() {
        return "PickedDate{" +
            "date='" + date + '\'' +
            '}';
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

Now, go to `MainActivity` and inside the `sendButton.setOnClickListener {}` block (or inside the `onClick()` method, if using Java) , modify your code to this:

{% tabs %}
{% tab title="Kotlin" %}
{% code title="MainActivity.kt" %}

```kotlin
if (pickedDate.isEmpty()) {
        messaging.sendTextMessage(
            channelId = channelId,
            message = typedInput,
            appliedAcls = appliedAcls,
            onValueUpdatedCallback = object : Mitter.OnValueUpdatedCallback {
                override fun onError(apiError: ApiError) {}

                override fun onSuccess() {
                    inputMessage?.text?.clear()
                }
            }
        )
} else {
       val objectMapper = ObjectMapper()
       val sender = IdUtils.of<User>(mitter.getUserId())

       val pickedDate = PickedDate(this.pickedDate)

       val timelineEvent = TimelineEvent(
            eventId = UUID.randomUUID().toString(),
            type = StandardTimelineEventTypeNames.Messages.SentTime,
            eventTimeMs = System.currentTimeMillis(),
            subject = sender
       )

        val message = Message(
            messageId = UUID.randomUUID().toString(),
            senderId = sender,
            textPayload = typedInput,
            timelineEvents = listOf(timelineEvent),
            appliedAcls = appliedAcls,
            messageData = listOf(
                MessageDatum(
                    "io.mitter.android.messages.DateMessage",
                    objectMapper.valueToTree(pickedDate)
                )
            )
        )

        messaging.sendMessage(
            channelId = channelId,
            message = message,
            onValueUpdatedCallback = object : Mitter.OnValueUpdatedCallback {
                override fun onError(apiError: ApiError) {

                }

                override fun onSuccess() {
                    inputMessage?.text?.clear()
                    this@MainActivity.pickedDate = ""
                }
            }
        )
}
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="MainActivity.java" %}

```java
if (pickedDate.isEmpty()) {
    messaging.sendTextMessage(
        channelId,
        typedInput,
        appliedAcls,
        new Mitter.OnValueUpdatedCallback() {
            @Override
            public void onSuccess() {
                inputMessage.getText().clear();
            }

            @Override
            public void onError(ApiError apiError) {
            }
        }
    );
} else {
    ObjectMapper objectMapper = new ObjectMapper();
    Identifiable<User> sender = IdUtils.of(mitter.getUserId(), User.class);

    PickedDate date = new PickedDate(pickedDate);

    TimelineEvent timelineEvent = new TimelineEvent(
        UUID.randomUUID().toString(),
        "",
        StandardTimelineEventTypeNames.Messages.SentTime,
        System.currentTimeMillis(),
        sender,
        null
    );

    List<TimelineEvent> timelineEvents = new ArrayList<>();
    timelineEvents.add(timelineEvent);

    MessageDatum messageDatum = new MessageDatum(
        "io.mitter.android.messages.DateMessage",
        objectMapper.valueToTree(date)
    );

    List<MessageDatum> messageData = new ArrayList<>();
    messageData.add(messageDatum);

    Message message = new Message(
        UUID.randomUUID().toString(),
        "",
        StandardMessageType.Standard,
        StandardPayloadTypeNames.TextMessage,
        sender,
        typedInput,
        messageData,
        timelineEvents,
        appliedAcls,
        new EntityMetadata(),
        null
    );

    messaging.sendMessage(
        channelId,
        message,
        new Mitter.OnValueUpdatedCallback() {
            @Override
            public void onSuccess() {
                inputMessage.getText().clear();
                pickedDate = "";
            }

            @Override
            public void onError(ApiError apiError) {

            }
        }
    );
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

What we did here is:

* Wrapped our previous `sendTextMessage()` method inside an `if-else` block
* Wrapped the picked date string inside a `PickedDate` object ready to be serialised
* Constructed a `SentTime` timeline event for our `Message` object
* Constructed a `Message` object with all the previous params and a new `MessageDatum` list

A `MessageDatum` is a single unit of your custom payload. It has a unique ID to identify the type of payload you’re sending and also the payload in *serialised form*.

Now, if you type a message in the input field and tap on the calendar icon you’ll see a date picker pop-up. Select a date and then hit **Send**.

The app will send out a message with the selected date as its payload.

We’ll make use of this payload in the next section where we’ll show a different chat bubble with this date information.

## Show the date message bubbles

To make use of the custom payload that we attached to our message, we need to add some custom layouts for our chat bubbles to accommodate the attached date.

Get started by adding these two layouts to your project:

{% code title="item\_message\_date\_self.xml" %}

```markup
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <android.support.constraint.ConstraintLayout
        android:id="@+id/messageContainer"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/back_blue_rounded"
        app:layout_constraintEnd_toEndOf="parent">

        <android.support.v7.widget.AppCompatTextView
            android:id="@+id/selfDateMessageText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:paddingBottom="5dp"
            android:paddingEnd="20dp"
            android:paddingStart="20dp"
            android:paddingTop="10dp"
            android:textColor="@android:color/white"
            android:textSize="16sp"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:text="Can we have a meeting?" />

        <android.support.v7.widget.AppCompatTextView
            android:id="@+id/selfDate"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingBottom="15dp"
            android:paddingEnd="20dp"
            android:paddingStart="20dp"
            android:textColor="#eee"
            android:textSize="12sp"
            android:textStyle="italic"
            app:layout_constraintStart_toStartOf="@id/selfDateMessageText"
            app:layout_constraintTop_toBottomOf="@id/selfDateMessageText"
            tools:text="On: 26/09/2018" />

    </android.support.constraint.ConstraintLayout>

</android.support.constraint.ConstraintLayout>
```

{% endcode %}

{% code title="item\_message\_date.xml" %}

```markup
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <android.support.constraint.ConstraintLayout
        android:id="@+id/messageContainer"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/back_gray_rounded">

        <android.support.v7.widget.AppCompatTextView
            android:id="@+id/dateMessageText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:paddingBottom="5dp"
            android:paddingEnd="20dp"
            android:paddingStart="20dp"
            android:paddingTop="10dp"
            android:textColor="@android:color/black"
            android:textSize="16sp"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:text="Can we have a meeting?" />

        <android.support.v7.widget.AppCompatTextView
            android:id="@+id/date"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingBottom="15dp"
            android:paddingEnd="20dp"
            android:paddingStart="20dp"
            android:textSize="12sp"
            android:textStyle="italic"
            app:layout_constraintStart_toStartOf="@id/dateMessageText"
            app:layout_constraintTop_toBottomOf="@id/dateMessageText"
            tools:text="On: 26/09/2018" />

    </android.support.constraint.ConstraintLayout>

    <Button
        android:id="@+id/yesButton"
        style="@style/Base.Widget.AppCompat.Button.Borderless"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Yes"
        app:layout_constraintTop_toBottomOf="@id/messageContainer" />

    <Button
        android:id="@+id/noButton"
        style="@style/Base.Widget.AppCompat.Button.Borderless"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="No"
        app:layout_constraintStart_toEndOf="@id/yesButton"
        app:layout_constraintTop_toBottomOf="@id/messageContainer" />

</android.support.constraint.ConstraintLayout>
```

{% endcode %}

After you’ve added these, the next step is to modify your `ChatRecyclerViewAdapter` to something like this:

{% tabs %}
{% tab title="Kotlin" %}
{% code title="ChatRecyclerViewAdapter.kt" %}

```kotlin
class ChatRecyclerViewAdapter(
    private val messageList: List<Message>,
    private val currentUserId: String
) : RecyclerView.Adapter<ChatRecyclerViewAdapter.ViewHolder>() {
    private val objectMapper = ObjectMapper()

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val itemView = LayoutInflater.from(parent.context).inflate(getLayoutId(viewType), parent, false)
        return ViewHolder(itemView)
    }

    override fun getItemCount(): Int = messageList.size

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bindMessage(messageList[position], holder.itemViewType)
    }

    override fun getItemViewType(position: Int): Int = when (messageList[position].senderId.domainId()) {
        currentUserId -> {
            val message = messageList[position]

            if (message.messageData.isEmpty()) {
                MessageTypes.SELF_PLAIN_MESSAGE
            } else {
                MessageTypes.SELF_DATE_MESSAGE
            }
        }
        else -> {
            val message = messageList[position]

            if (message.messageData.isEmpty()) {
                MessageTypes.OTHER_PLAIN_MESSAGE
            } else {
                MessageTypes.OTHER_DATE_MESSAGE
            }
        }
    }

    object MessageTypes {
        const val SELF_PLAIN_MESSAGE = 0
        const val OTHER_PLAIN_MESSAGE = 1
        const val SELF_DATE_MESSAGE = 2
        const val OTHER_DATE_MESSAGE = 3
    }

    private fun getLayoutId(viewType: Int): Int = when (viewType) {
        MessageTypes.SELF_PLAIN_MESSAGE -> R.layout.item_message_self
        MessageTypes.SELF_DATE_MESSAGE -> R.layout.item_message_date_self
        MessageTypes.OTHER_DATE_MESSAGE -> R.layout.item_message_date
        else -> R.layout.item_message_other
    }

    inner class ViewHolder(itemView: View?) : RecyclerView.ViewHolder(itemView) {
        fun bindMessage(message: Message, viewType: Int) {
            with(message) {
                when (viewType) {
                    MessageTypes.SELF_PLAIN_MESSAGE -> {
                        itemView?.selfMessageText?.text = textPayload
                    }
                    MessageTypes.OTHER_PLAIN_MESSAGE -> {
                        itemView?.otherMessageText?.text = textPayload
                    }
                    MessageTypes.SELF_DATE_MESSAGE -> {
                        val pickedDate = objectMapper.treeToValue(messageData[0].data, PickedDate::class.java)
                        itemView?.selfDateMessageText?.text = textPayload
                        itemView?.selfDate?.text = "On: ${pickedDate.date}"
                    }
                    MessageTypes.OTHER_DATE_MESSAGE -> {
                        val pickedDate = objectMapper.treeToValue(messageData[0].data, PickedDate::class.java)
                        itemView?.dateMessageText?.text = textPayload
                        itemView?.date?.text = "On: ${pickedDate.date}"
                    }
                }
            }
        }
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="ChatRecyclerViewAdapter.java" %}

```java
public class ChatRecyclerViewAdapter extends RecyclerView.Adapter<ChatRecyclerViewAdapter.ViewHolder> {
    private List<Message> messageList;
    private String currentUserId;

    private ObjectMapper objectMapper = new ObjectMapper();

    public ChatRecyclerViewAdapter(
        List<Message> messageList,
        String currentUserId
    ) {
        this.messageList = messageList;
        this.currentUserId = currentUserId;
    }

    @NonNull
    @Override
    public ChatRecyclerViewAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext()).inflate(
            getLayoutId(viewType),
            parent,
            false
        );
        return new ViewHolder(itemView, viewType);
    }

    @Override
    public void onBindViewHolder(@NonNull ChatRecyclerViewAdapter.ViewHolder holder, int position) {
        Message message = messageList.get(position);
        PickedDate pickedDate = null;

        if (!message.getMessageData().isEmpty() && message.getMessageData().get(0).getData() != null) {
            try {
                pickedDate = objectMapper.treeToValue(message.getMessageData().get(0).getData(), PickedDate.class);
            } catch (JsonProcessingException jpe) {
            }
        }

        switch (holder.getItemViewType()) {
            case MessageTypes.SELF_PLAIN_MESSAGE:
                holder.selfMessageText.setText(message.getTextPayload());
                break;
            case MessageTypes.OTHER_PLAIN_MESSAGE:
                holder.otherMessageText.setText(message.getTextPayload());
                break;
            case MessageTypes.SELF_DATE_MESSAGE:
                holder.selfDateMessageText.setText(message.getTextPayload());
                holder.selfDate.setText("On: " + pickedDate != null ? pickedDate.getDate() : "");
                break;
            case MessageTypes.OTHER_DATE_MESSAGE:
                holder.dateMessageText.setText(message.getTextPayload());
                holder.date.setText("On: " + pickedDate != null ? pickedDate.getDate() : "");
                break;
            default:
                break;
        }
    }

    @Override
    public int getItemCount() {
        return messageList != null ? messageList.size() : 0;
    }

    @Override
    public int getItemViewType(int position) {
        if (messageList.get(position).getSenderId().domainId().equals(currentUserId)) {
            Message message = messageList.get(position);

            if (message.getMessageData().isEmpty()) {
                return MessageTypes.SELF_PLAIN_MESSAGE;
            } else {
                return MessageTypes.SELF_DATE_MESSAGE;
            }
        } else {
            Message message = messageList.get(position);

            if (message.getMessageData().isEmpty()) {
                return MessageTypes.OTHER_PLAIN_MESSAGE;
            } else {
                return MessageTypes.OTHER_DATE_MESSAGE;
            }
        }
    }

    class MessageTypes {
        static final int SELF_PLAIN_MESSAGE = 0;
        static final int OTHER_PLAIN_MESSAGE = 1;
        static final int SELF_DATE_MESSAGE = 2;
        static final int OTHER_DATE_MESSAGE = 3;
    }

    private int getLayoutId(int viewType) {
        switch (viewType) {
            case MessageTypes.SELF_PLAIN_MESSAGE:
                return R.layout.item_message_self;
            case MessageTypes.SELF_DATE_MESSAGE:
                return R.layout.item_message_date_self;
            case MessageTypes.OTHER_DATE_MESSAGE:
                return R.layout.item_message_date;
            default:
                return R.layout.item_message_other;

        }
    }

    class ViewHolder extends RecyclerView.ViewHolder {
        private TextView selfMessageText;
        private TextView otherMessageText;
        private TextView selfDateMessageText;
        private TextView selfDate;
        private TextView dateMessageText;
        private TextView date;


        public ViewHolder(View itemView, int viewType) {
            super(itemView);

            switch (viewType) {
                case MessageTypes.SELF_PLAIN_MESSAGE:
                    selfMessageText = itemView.findViewById(R.id.selfMessageText);
                    break;
                case MessageTypes.OTHER_PLAIN_MESSAGE:
                    otherMessageText = itemView.findViewById(R.id.otherMessageText);
                    break;
                case MessageTypes.SELF_DATE_MESSAGE:
                    selfDateMessageText = itemView.findViewById(R.id.selfDateMessageText);
                    selfDate = itemView.findViewById(R.id.selfDate);
                    break;
                case MessageTypes.OTHER_DATE_MESSAGE:
                    dateMessageText = itemView.findViewById(R.id.dateMessageText);
                    date = itemView.findViewById(R.id.date);
                    break;
                default:
                    break;
            }
        }
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

Here, we did a couple of things:

* Added our new date message layouts
* Mapped them to their specific message types
* Rendered each message based on their message type or rather view type

If you run the app now and send a message with a date, you’ll see the date appearing right below your typed message text. Also, your date messages will appear different for different users:

* Just the message text and the date for self-messages
* Additional **Yes/No** buttons for other users’ messages

Go ahead, try this out.

![The date message received with Yes/No options](https://94728489-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LLZR00Qt6hZ5Vke2l2g%2F-LNLUvTsd3Vs4l6lZaxP%2F-LNLViGnJbUVMrHR7Z8T%2FCleanShot%202018-09-26%20at%2021.02.20%402x.png?alt=media\&token=c6d457b6-d17b-479c-ab35-1009849404e6)

## Add action to the chat bubble buttons

The only thing that’s left in this chapter is to add some actions to the buttons that we added in the previous step.

First, create two empty classes called `YesAction` and `NoAction`. These are basically events that we’ll be sending out on the button clicks and act on them when we receive them in the `MainActivity`.

After you’ve done that, navigate to the `ChatRecyclerViewAdapter` and modify the following block inside your `ViewHolder` class like this:

{% tabs %}
{% tab title="Kotlin" %}
{% code title="ChatRecyclerViewAdapter.kt" %}

```kotlin
MessageTypes.OTHER_DATE_MESSAGE -> {
        val pickedDate = objectMapper.treeToValue(messageData[0].data, PickedDate::class.java)
        itemView?.dateMessageText?.text = textPayload
        itemView?.date?.text = "On: ${pickedDate.date}"

        itemView?.yesButton?.setOnClickListener {
            EventBus.getDefault().post(YesAction())
        }

        itemView?.noButton?.setOnClickListener {
            EventBus.getDefault().post(NoAction())
        }
}
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="ChatRecyclerViewAdapter.java" %}

```java
case MessageTypes.OTHER_DATE_MESSAGE:
    dateMessageText = itemView.findViewById(R.id.dateMessageText);
    yesButton = itemView.findViewById(R.id.yesButton);
    noButton = itemView.findViewById(R.id.noButton);
    date = itemView.findViewById(R.id.date);

    yesButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            EventBus.getDefault().post(new YesAction());
        }
    });

    noButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            EventBus.getDefault().post(new NoAction());
        }
    });

    break;
```

{% endcode %}
{% endtab %}
{% endtabs %}

Here, we’re basically sending out events using the event bus that we previously added to our app, on each button click.

Now, we need to subscribe to these sent events and act on them in our `MainActivity`. Navigate to your `MainActivity` and add the following methods:

{% tabs %}
{% tab title="Kotlin" %}
{% code title="MainActivity.kt" %}

```kotlin
@Subscribe(threadMode = ThreadMode.MAIN)
fun onYesAction(yesAction: YesAction) {
   messaging.sendTextMessage(
        channelId = channelId,
        message = "Yes, I'm available"
    )
}

@Subscribe(threadMode = ThreadMode.MAIN)
fun onNoAction(noAction: NoAction) {
    messaging.sendTextMessage(
        channelId = channelId,
        message = "No, I'm not available"
    )
}
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="MainActivity.java" %}

```java
@Subscribe(threadMode = ThreadMode.MAIN)
public void onYesAction(YesAction yesAction) {
    messaging.sendTextMessage(
        channelId,
        "Yes, I'm available",
        new AppliedAclList(
            new ArrayList<AppliedAcl>(),
            new ArrayList<AppliedAcl>()
        ),
        null
    );
}

@Subscribe(threadMode = ThreadMode.MAIN)
public void onNoAction(NoAction noAction) {
    messaging.sendTextMessage(
        channelId,
        "No, I'm not available",
        new AppliedAclList(
            new ArrayList<AppliedAcl>(),
            new ArrayList<AppliedAcl>()
        ),
        null
    );
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

What we’re doing here is listening to the incoming `YesAction` and `NoAction` events and sending out a plain text message accordingly.

This will act as a shortcut confirmation message action for our users, kind of like what you see in popular apps like **Google Allo** and **Facebook Messenger**.

![Sending out a confirmation message on choosing an action](https://94728489-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LLZR00Qt6hZ5Vke2l2g%2F-LNLUvTsd3Vs4l6lZaxP%2F-LNLWOeXT1ZAy0IcuKN-%2FCleanShot%202018-09-26%20at%2021.04.41.gif?alt=media\&token=94d7465c-1b16-4279-b70d-08886073a3b0)

And that’s the end of the chapter. You’ve successfully added custom payloads to your messages and responded to them accordingly to update your UI.


# Build Your First iOS App

This section is a quickstart for building an iOS App with the SDK

> **NOTE** The Getting Started docs show you how to build your first Mitter.io app with our cloud-hosted sandbox only.
>
> To use it with your docker container, simply change the base API Url (when creating the mitter object) to the address of your running docker container.

## Introduction

Before we get started with this tutorial, let’s have a quick primer on what Mitter.io is and what we will be building in this tutorial.

### What is Mitter.io?

Mitter.io is a messaging platform that allows you to build apps around messaging. You can treat a message as something more than just an envelope for text with our platform.

### What are we going to build?

In this tutorial, we’re going to build a simple chat app which can send and receive messages..

### Where's the repository for this project?

You can find the complete project [**hosted on GitHub**](https://github.com/mitterio/mitter-android-demo). Feel free to clone the repository and play around.


# Overview

The iOS SDK for Mitter currently has very basic features like:

* Getting the current user information
* Creating a channel
* Send text messages
* Receive message via FCM (through APNs)


# Installation

The SDK is currently available on Cocoapods and can be installed as a regular pod.

Add the following dependencies to your `Podfile`:

```
pod ‘Mitter’
```

Before that, make sure you’ve Cocoapods [installed and setup](https://guides.cocoapods.org/using/using-cocoapods) for your project.

Then just navigate to your new project and run:

```
pod install
```


# Basic Setup

Before you can start communicating with Mitter using the SDK, it needs to be configured with your **application ID** and **user auth token**.

The best place to configure a global `Mitter` object is inside your `AppDelegate.swift` file.

Open up the `AppDelegate` file and declare an instance field for the `Mitter` object, like this:

```
var mitter: Mitter = Mitter(applicationId: "")
```

After you’ve done that, locate the function with `didFinishLaunchingWithOptions` signature and initialise your `Mitter` object with your application and user details like this:

```
mitter = Mitter(
            applicationId: "MZzf4-na9nL-O98wq-M1HxS",
            userAuthToken: "eyJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJtaXR0ZXItaW8iLCJ1c2VyVG9rZW5JZCI6IkhYbkZJSXIydUpQRHMzankiLCJ1c2VydG9rZW4iOiJhaHFtNTgzcjRwbzEwZmNqZTllaHE5dDV1NCIsImFwcGxpY2F0aW9uSWQiOiJNWnpmNC1uYTluTC1POTh3cS1NMUh4UyIsInVzZXJJZCI6ImNzckN5LVNKTDN1LThBS01ULVdxdjZ5In0.FTgn0GBgIQrA0NznQEUHyC7SN7rbN9O9cWlI5mejuDG466VSJHjwGWZF2DB3nsn8eoeCg5toIXXh5Sxz2MMU3w"
)
```

The user token is enough to help the SDK figure out the user ID. Therefore, you don’t need to explicitly add the user ID. You can get the application id and token from the [mitter.io dashboard](https://mitter.io/home).

#### Using the SDK with containerised Mitter.io

If you're using the Mitter.io docker container, then you need to *override* the default API endpoint in the SDK, as follows:

```
mitter = Mitter(
            applicationId: "MZzf4-na9nL-O98wq-M1HxS",
            userAuthToken: "eyJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJtaXR0ZXItaW8iLCJ1c2VyVG9rZW5JZCI6IkhYbkZJSXIydUpQRHMzankiLCJ1c2VydG9rZW4iOiJhaHFtNTgzcjRwbzEwZmNqZTllaHE5dDV1NCIsImFwcGxpY2F0aW9uSWQiOiJNWnpmNC1uYTluTC1POTh3cS1NMUh4UyIsInVzZXJJZCI6ImNzckN5LVNKTDN1LThBS01ULVdxdjZ5In0.FTgn0GBgIQrA0NznQEUHyC7SN7rbN9O9cWlI5mejuDG466VSJHjwGWZF2DB3nsn8eoeCg5toIXXh5Sxz2MMU3w",
            mitterApiEndpoint: "http://localhost:11901"
)
```


# Receive Push Messages

The iOS SDK can receive push messages through FCM (which uses APNs). To set up a Firebase Project to user FCM, refer to the [FCM iOS documentation](https://firebase.google.com/docs/cloud-messaging/ios/client) and follow it completely. Make sure to also [configure APNs with FCM](https://firebase.google.com/docs/cloud-messaging/ios/certs).

Once you have done that, add your `GoogleService-Info.plist` to the root of your xcode project.

To register your FCM token with Mitter, do the following:

```
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    print("APNs token retrieved: \(deviceToken.base64EncodedString())")

    InstanceID.instanceID().instanceID { (result, error) in
        if let error = error {
            print("Error fetching remote instange ID: \(error)")
        } else if let result = result {
            print("Remote instance ID token: \(result.token)")

            self.mitter.registerFcmToken(token: result.token) {
                result in
                switch result {
                case .success(let deliveryEndpoint):
                    print("Endpoint is: \(deliveryEndpoint.serializedEndpoint)")
                case .error:
                    print("Unable to register endpoint!")
                }
            }
        }
    }

    // With swizzling disabled you must set the APNs token here.
    // Messaging.messaging().apnsToken = deviceToken
}
```

To receive Messages when the app is in the background and then add it into the Channel view, do the following:

```
@available(iOS 10, *)
extension AppDelegate : UNUserNotificationCenterDelegate {

    // Receive displayed notifications for iOS 10 devices.
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                willPresent notification: UNNotification,
                                withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        let userInfo = notification.request.content.userInfo

        let messageString = userInfo["data"] as! String
        let messagingPipelinePayload = mitter.parseFcmMessage(data: messageString)

        processFcmMessage(pipelinePayload: messagingPipelinePayload)

        // Change this to your preferred presentation option
        completionHandler([])
    }
}
```

To receive messages directly from FCM (and not via APNs push), do the following:

```
extension AppDelegate : MessagingDelegate {
    // [START refresh_token]
    func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
        print("Firebase registration token: \(fcmToken)")

        let dataDict:[String: String] = ["token": fcmToken]
        NotificationCenter.default.post(name: Notification.Name("FCMToken"), object: nil, userInfo: dataDict)
        // TODO: If necessary send token to application server.
        // Note: This callback is fired at each app startup and whenever a new token is generated.
    }
    // [END refresh_token]
    // [START ios_10_data_message]
    // Receive data messages on iOS 10+ directly from FCM (bypassing APNs) when the app is in the foreground.
    // To enable direct data messages, you can set Messaging.messaging().shouldEstablishDirectChannel to true.
    func messaging(_ messaging: Messaging, didReceive remoteMessage: MessagingRemoteMessage) {
        print("Received data message: \(remoteMessage.appData)")
        let messagingPipelinePayload = mitter.parseFcmMessage(data: remoteMessage.appData["data"] as! String)
        processFcmMessage(pipelinePayload: messagingPipelinePayload)
    }
    // [END ios_10_data_message]
}
```

Here is the function (used in the above snippets) that will add the parsed FCM message into the Channel View. Add it to your `AppDelegate`. Refer to the upcoming sections on the structure of the storyboard and `ChannelWindowViewController`:

```
func processFcmMessage(pipelinePayload: MessagingPipelinePayload?) {
    if mitter.isMitterMessage(pipelinePayload) {
        let payload = mitter.processPushMessage(pipelinePayload!)

        switch payload {
            // Handle the new message payload: Add it to the Channel Window View Controller
            case .NewMessagePayload(let message, let channelId):
                if let navController = window?.rootViewController as? UINavigationController {
                    // Get the second controller, which is the ChannelWindowViewController according to the storyboard
                    if let channelWindowViewController = navController.viewControllers[1] as? ChannelWindowViewController {
                        channelWindowViewController.newMessage(channelId: channelId.domainId, message: message)
                    }
                }
            // Ignore all other payloads
            default:
                print("Nothing to print!")
        }
    }
}
```


# Storyboard

The application we will be building is a simple chat application that has the following:

1. A simple list of `Channel`s that the user is a part of.
2. Clicking on the channel will open up the chat view.
3. You can send and receive messages from this channel in this view.

The storyboard (refer to`Main.storyboard`) contains a top-level `NavigationController` inside which is embedded the `ChannelListViewController` (shows a list of Channels) and `ChannelWindowViewController`. (the chat view)


# Channel List

The `ChannelListViewController` contains a simple `TableView` that shows a list of Channels. This section will only deal with the essential functionality and not styling.

&#x20;Set up the `TableView` like so in your `viewDidLoad`:

```

// This is the data backing the TableView
var channels = [ParticipatedChannel]()

override func viewDidLoad() {
    super.viewDidLoad()
    
    // Get the AppDelegate which contains the Mitter object
    let appDelegate = UIApplication.shared.delegate as! AppDelegate
    
    // TableView setup
    ...
    
    // Fetch the list of channels and display
    appDelegate.mitter.channels.getChannelsForCurrentUser {
        result in
        switch result {
        case .success(let fetchedChannels):
            self.channels = fetchedChannels
            print("Received all channels for logged in user")
            self.tableView.reloadData()

        case .error:
            print("Couldn't fetch all channels for logged in user")
        }
    }
}
```

To render the cell, modify the `UITableViewDelegate` function as follows:

```
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        // create a new cell if needed or reuse an old one
        let cell: UITableViewCell = self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as UITableViewCell!

        // set the text from the data model
        cell.textLabel?.text = self.channels[indexPath.row].channel.channelId

        return cell
    }
```

To open the `ChannelWindowViewController` on clicking a cell:

```
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        let channelWindowViewController = storyboard.instantiateViewController(withIdentifier: "channelWindowView") as! ChannelWindowViewController
        channelWindowViewController.channelId = channels[indexPath.row].channel.channelId
        navigationController!.pushViewController(channelWindowViewController, animated: true)
    }
```


# Channel Window

This `ChannelWindowViewController` contains a `TableView` to display a list of Messages, a `UITextField` to input a Message and a Send `UIButton` to send the Text Message.

To initialise the list of Messages, do the following:

```
// The channel id for this Channel. This will be set by ChannelListViewController
var channelId = String()

// The list of Messages in this channel, backing the messages TableView
var messages = [Message]()

override func viewDidLoad() {
    // Get the AppDelegate, which contains the Mitter object
    let appDelegate = UIApplication.shared.delegate as! AppDelegate
    
    // TableView setup
    ...
    
    // Hook up the Send button
    sendButton.addTarget(self, action: #selector(buttonClicked), for: .touchUpInside)
    
    // Fetch all Messages in Channel
    appDelegate.mitter.messaging.getMessagesInChannel(channelId) {
        result in
            switch result {
            case .success(let fetchedMessages):
                self.messages = fetchedMessages.reversed()
                self.tableView.reloadData()
            case .error:
                print("Couldn't fetch messages")
            }
        }
    }
}
```

Set up the Send button click target:

```
@objc func buttonClicked() {
    let appDelegate = UIApplication.shared.delegate as! AppDelegate

    appDelegate.mitter.messaging.sendTextMessage(forChannel: channelId, inputText.text!) { result in
        switch result {
        case .success:
            print("Message sent!")
        case .error:
            print("Couldn't send message")
        }
    }
}
```

Add a public function to add a Message into the `TableView`. This will be used by `AppDelegate` when it receives an FCM message:

```
func newMessage(channelId: String, message: Message) {
    if (self.channelId == channelId) {
        messages.append(message)
        tableView.reloadData()
    }
}
```

The controller also contains some View manipulation logic to align/colour Message bubbles based on who the sender is. Refer to the `cellForRowAt` delegate function for the same.


# Build Your First Web App

> **NOTE** The Getting Started docs show you how to build your first Mitter.io app with our cloud-hosted sandbox only.
>
> To use it with your docker container, simply change the base API Url (when creating the mitter object) to the address of your running docker container.

## Introduction

Before we get started with this tutorial, let’s have a quick primer on what Mitter.io is and what are we going to build in this tutorial.

### What is Mitter.io?

Mitter.io is a messaging platform which allows you to build apps around messaging. You can treat a message as something more than just an envelope for text with our platform.

### What are we going to build?

In this tutorial, we’re going to build a simple chat app that allows private replies to specific participants in a group.

### Where's the repository for this project?

You can find the complete project [**hosted on GitHub**](https://github.com/mitterio/mitter-getting-started-web-demo). Feel free to clone the repository and play around.

### Tell me more!

Want a detailed guide to building with the platform? Watch our webinars [here](https://www.youtube.com/watch?v=2EERT1O0CHQ)!


# Setting Up Your App

### Setting up the project

In this tutorial, we will start with creating a basic chat application where users can chat with each other and then incrementally add more functionality to the app. For this app, we will be using React and generating the project using `create-react-app`. However, to use the Mitter.io SDKs, you do not need to use React or even the `create-react-app` utility. You can use any UI framework of your choice, and you can change the UI components accordingly.

You can also simply clone the `mitter-web-starter` [project from GitHub](https://github.com/mitterio/mitter-web-starter), which has all of this already setup for you.

Firstly, get the `create-react-app` utility using `npm` or `yarn`

```
yarn global add create-react-app​
```

Or using `npm`

```
npm install -g create-react-app​
```

Then create your app and install the dependencies

```
create-react-app my-mitter-app
cd my-mitter-app​

# Install the dependencies
yarn install

​# Install the mitter.io SDKs
yarn add axios @mitter-io/web @mitter-io/models​

```

Now, you can start the app

```
yarn start
```

This will open up the application in your default browser and you will see the standard React application with the placeholder content.

Let us first strip out all the placeholder stuff and just add a generic message. To do so, simply edit `App.js` to look something like this:

{% code title="App.js" %}

```javascript
import React, { Component } from 'react'
​
class App extends Component {
    render() {
        return (
            <div className='App'>
                Hello!
          </div>
        );
    }
}

```

{% endcode %}

### Setting up users

In this example application, we will be adding three users with the usernames `@john`, `@amy`, `@candice`. Among themselves, they will have two channels: `#john-amy` which will be a direct channel be between `@john` and `@amy`, and a channel `#vacation` which will be a channel between `@john`, `@amy` and `@candice`. To recognize which user is which, we will use a pattern from the URL. So, when a user visits `http://localhost:3000/user/@john`, it will recognize the user as `@john`. To do so, open up `index.js` and add the following code after the import statements:

{% code title="index.js" %}

```javascript
const regex = /^\/user\/(@[a-zA-Z0-9-]+)/
const loggedUser = (new URL(document.location.href).pathname.match(regex)[1])
​
```

{% endcode %}

Also, we need to pass this property to the `App` component. Further down in the same file, pass this as a prop to `App`

{% code title="App.js" %}

```javascript
<App
    loggedUser={loggedUser}
/>
​
```

{% endcode %}

Any call that is made to Mitter.io requires the caller to identify itself as an authenticated entity. There are a few anonymous calls that are allowed, but they offer little to no functionality and are present to facilitate the fetching of authorization for the user. In a real world scenario, you would get the user authorization from your application backend which acts as a source of truth. There is no way to anonymously fetch user authorization or to create a new user, unless the application has enabled federated authentication (in which case, the user still has to identify themselves via an authentication provider like Google OAuth).

One way to get user authorization tokens for development purposes (and development purposes ONLY) is to use the Mitter.io Dashboard to get user authorization tokens.

In the same Dashboard, we are going to create our users as well for the purposes of setting up this app. Do note that in a production environment, user management should also be done from a trusted source like an application backend.

Go to the Mitter.io Dashboard and navigate to the application that you created. In this application, navigate to the Users Panel and create three new users, setting their screen names to `@john`, `@amy` and `@candice`.  Do note that this is not the same as the user ID itself. When you create a user on Mitter.io, it generates a user ID automatically. Once you've done that, you can generate user tokens for each of these users. The token itself encodes the generated user ID for the user. You can inspect the contents of the token by decoding it, or online at <https://jwt.io>.

Put these user tokens in your `index.js` file, mapping it to their user IDs:

{% code title="index.js" %}

```javascript
const userAuth = {
    '@john': ' ... johns user token ...',
    '@amy': ' ... amys user token ...',
    '@candice': ' ... candices user token ... '
}
​
```

{% endcode %}

We'll be using this information shortly. Meanwhile, let us build the basic UI for our application.

### Creating a basic Chat UI

Let us add a few basic elements to this application, generic for any app

{% code title="App.js" %}

```javascript
import React, { Component } from 'react'
​
class App extends Component {
    render() {
        return (
            <div className='App'>
                <h2 className='application-title'>
                  My Chat App
                  
                  <div className='user-label'>
                      Welcome, <strong>{this.props.loggedUser}</strong>
                  </div>
              </h2>
          </div>
        );
    }
}
​
```

{% endcode %}

And the associated styling for this component to be added in `App.css` (feel free to remove any content that is already there)

{% code title="App.css" %}

```javascript
.App {
    position: absolute;
    width: 100%;
    bottom: 0;
    top: 0;
}
​
.application-title {
    margin: 0;
    padding: 15pt;
    border-bottom: 1pt solid black;
    color: white;
    background-color: #741090;
}
​
.user-label {
    font-size: 10pt;
    float: right;
    position: relative;
    font-weight: normal;
}
​
```

{% endcode %}

At this point, your app will look something like this

![A basic UI with a generic header](https://94728489-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LLZR00Qt6hZ5Vke2l2g%2F-LNAAmTp4wkLCXMEQL40%2F-LNAVU_Dc5yQmTQ4wNsW%2Fgetting-started-app-001.png?alt=media\&token=59522e94-a20e-47cc-9661-f474f4de6683)

> **NOTE** If you are using the `mitter-react-starter` project as your base, then this is the state you will start at.

So far it doesn't do much, and we need to add something that makes it more of a chat application. To do so, we will create two panels - one with a list of channels a user is a part of, and another panel that shows the messages in a selected channel. Here, we will introduce a component of an `activeChannel` which is simply the channel that the user has currently selected.

Create two new files, `ChannelComponent.js` and `Channel.css`

{% code title="ChannelComponent.js" %}

```javascript
import React, { Component } from 'react'
import './Channel.css'
​
export default class ChannelComponent extends Component {
    constructor() {
        super();
​
        this.state = {
            activeChannel: null
        }
    }
​
    componentDidUpdate() {
        if (Object.keys(this.props.channelMessages).length > 0) {
            this.setActiveChannel(Object.keys(this.props.channelMessages)[0])()
        }
    }
​
    renderChannelList() {
        return Object.keys(this.props.channelMessages).map(channelId => {
            const isChannelActive = this.state.activeChannel === channelId
​
            return (
                <div
                    key={channelId}
                    className={ 'channel-tile' +
                        ((isChannelActive) ? ' active' : '') }
                    onClick={this.setActiveChannel(channelId)}
                >
                    { channelId }
                </div>
            )
        })
    }
​
    renderMessages() {
        if (this.state.activeChannel === null) {
            return <div></div>
        }
​
        const activeChannelMessages =
            this.props.channelMessages[this.state.activeChannel]
​
        return activeChannelMessages.map(message => {
            return (
                <div key={message.messageId}>
                    {message.textPayload}
                </div>
            )
        })
    }
​
    render() {
        return (
            <div className='chat-parent chat-panel'>
                <div className='channel-list'>
                    { this.renderChannelList() }
                </div>
​
                <div className='chat-window chat-panel'>
                    { this.renderMessages() }
                    <div className='message-input-box'>
                        <input className='message-input' type='text' />
                        &nbsp;
                        <input className='send-message' type='submit' value='Send' />
                    </div>
                </div>
            </div>
        );
    }
​
    setActiveChannel(channelId) {
        return () => {
            this.setState((prevState) => Object.assign({}, prevState, {
                activeChannel: channelId
            }))
        }
    }
}

```

{% endcode %}

{% code title="Channel.css" %}

```css
.chat-parent {
    background-color: green;
    display: flex;
    top: 69px;
    position: absolute;
    width: 100%;
    bottom: 0;
}
​
.chat-parent > .channel-list {
    width: 350px;
    background-color: #D3E2FE;
    border-right: 1pt solid black;
}
​
.chat-parent > .chat-window {
    flex: 1;
    background-color: white;
}
​
.chat-parent > .channel-list > .channel-tile {
    padding: 20pt;
    border-bottom: 1pt solid black;
}
​
.chat-parent > .channel-list > .active {
    border-bottom: 2pt solid black;
    font-weight: bold;
    background-color: #082289;
    color: white;
}
​
.chat-parent > .chat-window > .message-input-box {
    display: flex;
    position: absolute;
    bottom: 0;
    right: 0;
    left: 350px;
    padding: 5pt;
}
​
.message-input-box > .message-input {
    flex: 1;
    font-size: 12pt;
    padding: 5pt;
}
​
.message-input-box > .send-message {
    font-size: 12pt;
    padding: 5pt;
}
​
```

{% endcode %}

This is a pretty standard React component, but let's walk through the shapes of the component's `state` and `props`. In the `constructor` you can see the state shape:

{% code title="ChannelComponent.js" %}

```javascript
this.state = {
    activeChannel: null
}
​
```

{% endcode %}

What we are doing is storing the currently active channel in the state, and a `null` basically signifies that there is no channel currently selected. What we will be storing here is simply the identifier of the channel that is currently selected.

The props that it expects are of the shape:

```
{
    channelMessages: {
        channelId: [ array of messages ],
        channelId: [ array of messages ]
    }
}
​
```

We are storing all the messages in a channel in an array and are associating it in a dictionary with the `channelId` as the key of the map. Once you have this, we need to render this component. Also we would really like to test out how this looks, so let us first create a mock of the data we expect:

{% code title="App.js" %}

```javascript
const channelMessages = {
    'channel-a': [{
      messageId: 'message-001',
      textPayload: 'hello world!',
      senderId: {
        identifier: '@john'
      }
    }, {
      messageId: 'message-002',
      textPayload: 'hello back!',
      senderId: {
        identifier: '@amy'
      }
    }],
    'channel-b': []
}
​
class App extends Component {
    render() {
        return (
            <div className='App'>
                <h2 className='application-title'>
                  My Chat App
​
                  <div className='user-label'>
                      Welcome, <strong>{this.props.loggedUser}</strong>
                  </div>
              </h2>
​
              <ChannelComponent
                  channelMessages={channelMessages}
              />
          </div>
        );
    }
}
​
export default App;
​
```

{% endcode %}

Take a look at the `channelMessages` object we created at the beginning of the file. This contains a list of messages in a map, mapped by the channel ID. Do note that all of this is simply dummy data which we will later wire to actual data from Mitter.io. The shape of the message object is what the Mitter.io platform uses in all of its request/response objects. The ones that are shown here are:

1. `messageId` A globally unique ID for the message. Do note that this ID is namespaced by your application, not by the channel it is in, and hence is unique across all messages in your application.
2. `textPayload` A text representation of your message. Any message, be it a file, image, multimedia is always accompanied by a text representation of it. In this example app, we will be using this to render our messages.
3. `senderId` The identifier of the user who sent this message.

> **NOTE** Mitter.io always sends out identifiers in an encapsulated object. Some IDs are also sent with specific names in top-level objects. For example, here `messageId` is directly a string, but `senderId` is an encapsulated object of the shape `{identifier: '...'}`. The actual message object received from the server will have both `messageId` and an `identifier` object as well, both pointing to the same ID. Refer to the docs on Mitter API modelling for more information.

Coming back to our `ChannelComponent.js` file, take a look at the `componentDidMount()` function:

{% code title="ChannelComponent.js" %}

```javascript
    componentDidUpdate() {
        if (Object.keys(this.props.channelMessages).length > 0) {
            this.setActiveChannel(Object.keys(this.props.channelMessages)[0])()
        }
    }

```

{% endcode %}

Nothing fancy, but all that we are doing is setting the first channel as active if there are any channels that were passed to this object. And the render function for the same:

{% code title="ChannelComponent.js" %}

```javascript
    renderChannelList() {
        return Object.keys(this.props.channelMessages).map(channelId => {
            const isChannelActive = this.state.activeChannel === channelId
​
            return (
                <div
                    key={channelId}
                    className={ 'channel-tile' +
                        ((isChannelActive) ? ' active' : '') }
                    onClick={this.setActiveChannel(channelId)}
                >
                    { channelId }
                </div>
            )
        })
    }

```

{% endcode %}

We iterate through the channel objects, and simply add a div for each channel that the user is a part of. We also add another class called `active` to the tile of the channel that is currently active. The exact styles are as shown in `Channel.css` a little above in the documentation.

Clicking on a channel tile sets that tiles channel to be the active channel, as can be seen in the function `setActiveChannel`.

For rendering our messages, we are currently just stacking it over one another in the `renderMessages()` function:

{% code title="ChannelComponent.js" %}

```javascript
    renderMessages() {
        if (this.state.activeChannel === null) {
            return <div></div>
        }
​
        const activeChannelMessages =
            this.props.channelMessages[this.state.activeChannel]
​
        return activeChannelMessages.map(message => {
            return (
                <div key={message.messageId}>
                    {message.textPayload}
                </div>
            )
        })
    }
    
```

{% endcode %}

This doesn't look all that good, but we will get to styling it in a bit. So far, the application should look something like this:

![A basic chat UI, with a channel list, message view and message sending input field](https://94728489-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LLZR00Qt6hZ5Vke2l2g%2F-LNAAmTp4wkLCXMEQL40%2F-LNAaFVg51L__ieZgB78%2Fgetting-started-app-002.png?alt=media\&token=a5d7a4e5-fe91-4821-b9a6-8ae3433b98e2)

What we would like to now do is align the messages that were sent by the logged in user to the right, and other messages to the left. We would also like to show the sender ID for messages not sent by us. To do so, we have to modify the `renderMessages` function a bit:

{% code title="ChannelComponent.js" %}

```javascript
    renderMessages() {
        if (this.state.activeChannel === null) {
            return <div></div>
        }
​
        const activeChannelMessages =
            this.props.channelMessages[this.state.activeChannel]
​
​
        return activeChannelMessages.map(message => {
            const isSelfMessage =
                this.props.selfUserId === message.senderId
​
            return (
                <div key={message.messageId}
                     className={ 'message' + (isSelfMessage ? ' self' : '') }
                >
                    <div className='message-block'>
                        <span className='sender'>{message.senderId}</span>
​
                        <div className='message-content'>
                            {message.textPayload}
                        </div>
                    </div>
                </div>
            )
        })
    }
    
```

{% endcode %}

What we are doing here is rendering the `senderId` in the message block and we have also added some hierarchy to the overall message structure that will allow us to align it. Also, for messages sent by the current user (as checked on line no. 12, in the variable `isSelfMessage`) we will attach another class `self` to the top-level message element.

Add these following style definitions to the `Channel.css` fileChannel.css

{% code title="Channel.css" %}

```css
.message-input-box > .message-input {
    flex: 1;
    font-size: 12pt;
    padding: 5pt;
}
​
.message-input-box > .send-message {
    font-size: 12pt;
    padding: 5pt;
}
​
.chat-parent > .chat-window > .message {
    margin: 10pt;
}
​
.message > .message-block {
    display: inline-block;
}
​
.message > .message-block > .sender {
    font-size: 10pt;
}
​
.message > .message-block > .message-content {
    background-color: #D3E2FE;
    padding: 10pt;
    margin-top: 2pt;
}
​
.message.self > .message-block > .sender {
    display: none;
}
​
.message.self {
    text-align: right;
}
​
```

{% endcode %}

With this done, your app should now look something like:

![A chat UI with some better looking messages, aligned and with the sender ID](https://94728489-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LLZR00Qt6hZ5Vke2l2g%2F-LNBSWO9GG9ntNYcBAu2%2F-LNB_pcFDniqr_hEoP1w%2Fgetting-started-app-003.png?alt=media\&token=3a7b6678-8f96-4e26-aede-04a9e129a6dd)

Great going so far! Now what we need to do is wire up our application to consume the actual data from Mitter.io. To do this, let's head on over to the next section.


# Start a Basic Chat

Make sure that you have followed the steps for creating the users/channels in the previous section. We will be using this data now to integrate your app with the Mitter.io platform

### Setting up the Mitter.io SDK

Before you consume any Mitter.io APIs, you need to setup the Mitter object. In your `index.js` file, add the following lines:

{% code title="index.js" %}

```javascript
import { Mitter } from '@mitter-io/web'

const mitter = Mitter.forWeb({
    applicationId: 'fb70ff76-ea33-4bb0-bd59-90853f103202', /* provide your application id here */
    mitterApiBaseUrl: '<mitter-api-url>', /* look below for the values */
    weaverUrl: '<distributor-url>' /* look below for values */
})

```

{% endcode %}

For the `<mitter-api-url>`, use the following value:

* If you're using the cloud hosted solution, you can omit the `mitterApiBaseUrl` key in the config or explicitly set it to `https://api.mitter.io`
* If you're running it as a docker container set it to `http://localhost:<port>` where the port is port forwarded by docker for `11902`. To find out which port it is, run the following command `docker port $(docker ps --filter expose=11901-11903/tcp --format "")`

For the `<distributor-url>`, use the following value:

* If you're using the cloud hosted solution, you can omit the `weaverUrl` key in the config or explicitly set it to `https://weaver.mitter.io`
* If you're running it as a docker container set it to `http://localhost:<port>` where the port is port forwarded by docker for `11903`. To find out which port it is, run the following command `docker port $(docker ps --filter expose=11901-11903/tcp --format "")`

Your application ID can be fetched from the Mitter.io Dashboard. Once you have this set up, you now need to pass in the user authorization. You can pick the correct user authorization for the user authorization map we had created earlier.

{% code title="index.js" %}

```javascript
const userAuth = {
    '@john': ' ... johns user token ...',
    '@amy': ' ... amys user token ...',
    '@candice': ' ... candices user token ... '
}

mitter.setUserAuthorization(userAuth[loggedUser])

```

{% endcode %}

We will also need to pass the `mitter` object we created to the `App` component so that it can fetch the user data and render it. At the end of this, your `index.js` file should look something like:

{% code title="index.js" %}

```javascript
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import { Mitter } from '@mitter-io/web'

const regex = /^\/user\/(@[a-zA-Z0-9-]+)/
const loggedUser = (new URL(document.location.href).pathname.match(regex)[1])

const userAuth = {
    '@john': ' ... johns user token ...',
    '@amy': ' ... amys user token ...',
    '@candice': ' ... candices user token ... '
}

const mitter = Mitter.forWeb({
    applicationId: 'fb70ff76-ea33-4bb0-bd59-90853f103202', /* provide your application id here */
    mitterApiBaseUrl: '<mitter-api-url>', /* look above for the values */
    weaverUrl: '<distributor-url>' /* look above for values */
})

mitter.setUserAuthorization(userAuth[loggedUser])

ReactDOM.render(
    <App
        mitter={mitter}
        loggedUser={loggedUser}
    />,
    document.getElementById('root')
);

registerServiceWorker();

```

{% endcode %}

### Getting the list of participated channels

We now want to get the list of channels for a user and then render them in our app. To do so, we'll have to make a couple of changes in our application. First, we'll have to move the channel messages object that we created to a variable that can be changed and propagated to the `ChannelComponent`. We'll move it to the state for the `App` component. Also, we'll pass on the Mitter object to the the `ChannelComponent` as we will need to send messages later on.

{% code title="App.js" %}

```javascript
class App extends Component {
    constructor() {
        super();

        this.state = {
            channelMessages: {}
        }
    }

     render() {
        return (
            <div className='App'>
                <h2 className='application-title'>
                  My Chat App

                  <div className='user-label'>
                      Welcome, <strong>{this.props.loggedUser}</strong>
                  </div>
              </h2>

              <ChannelComponent
                  mitter={this.props.mitter}
                  channelMessages={this.state.channelMessages}
                  selfUserId={this.props.loggedUser}
              />
          </div>
        );
    }
}

```

{% endcode %}

Do note that we have even modified the `render()` function to now pass the `channelMessages` prop from `App` via a state rather than the hard-coded variable. If you reload your application, you'll see a blank page. In your `App` component, we will now fetch a list of channels for the user. We will do this in the `componentDidMount()` method:

> **NOTE** Do note that for a messaging-based application, the architecture of this application is not a recommended one. Ideally, you should be using a state management system like `redux` or `flux`, but for the sake of simplicity we are not using this in the application so that we can focus on introducing Mitter.io concepts. There is also a package `mitter-redux` currently in alpha that builds atop redux and handles all intricacies of state management which should be used in production apps.

{% code title="App.js" %}

```javascript
class App extends Component {
    construtor() {
        super()

        this.state = {
            channelMessages: {}
        }

        this.setChannels = this.setChannels.bind(this)
    }

    setChannels(participatedChannels) {
        const activeChannels = {}

        Objects.forEach(participatedChannels, (participatedChannel) => {
            activeChannels[participatedChannel] = []

            this.setState((prevState) => {
                return Object.assign({}, prevState, {
                    activeChannels
                })
            })
        })
    }

    componentDidMount() {
        const mitter = this.props.mitter

        mitter.clients().channels().participatedChannels()
            .then(participatedChannels => this.setChannels(participatedChannels))
    }
}

```

{% endcode %}

In the code above, we are transforming a response we get from Mitter.io, of the form:

```
[
    {
        participantId: '...',
        channel: {
            channelId: 'channel-a'
        },
        participationStatus: 'Active'
    },
    {
        partipantId: '...',
        channel: {
            channelId: 'channel-b'
        },
        participationStatus: 'Active'
    }
]

```

to something of the form:

```
{
    'channel-a': [],
    'channel-b': []
}

```

Which is basically a map of channel IDs to an empty array of messages. We will be adding messages to this object as we get them from the Mitter.io pipeline.

Reload the page and you should see the channels listed for the current selected user.

![The chat window with the user's channels loaded from mitter.io](https://94728489-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LLZR00Qt6hZ5Vke2l2g%2F-LNEUrPfZZmO0w4u2Z7i%2F-LNEFHp7MfSsIoyGRH8e%2Fgetting-started-app-004.png?alt=media\&token=b66fed2a-4201-4f7f-9330-aa6b50a9abf0)

Change the url from `http://localhost:3000/user/@john` to `http://localhost:3000/user/@candice` and you should now see only one channel (`#roadtrip`) as opposed to two channels earlier.

![We are now loading the channels for each user from Mitter.io](https://94728489-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LLZR00Qt6hZ5Vke2l2g%2F-LNG8gQSc2I5rQOqy9cb%2F-LNGBSdd_7-vxkgDfGMw%2Fmitter-gs-vid-001.gif?alt=media\&token=532694cb-dcde-402a-9d83-caef71ad7a14)

### Listening to messages and populating them in a channel

Now, we will move on to setup the next phase of the project, where we listen to incoming events (in this case specifically, messages) and render them in our application. To listen to pipeline payloads (that's what Mitter calls events sent on different front-end mechanisms), you need to subscribe to them. So, add the following lines in the `componentDidMount()` function (also pay attention to the additional import `isNewMessagePayload` at the top of the file):

{% code title="App.js" %}

```javascript
import { Mitter, isNewMessagePayload } from '@mitter-io/core'

// your other code and imports

class App extends  React.Component {
    constructor() {
        // Previous code in constructor
        this.newMessage = this.newMessage.bind(this)
    }
    // other functions in the App

    newMessage(messagePayload) {
        // currently does nothing
    }

    comoponentDidMount() {
        mitter.subscribeToPayload(payload => {
            if (isNewMessagePayload(payload)) {
                this.newMessage(messagePayload)
            }
        })
    }
    
    // ... rest of the file
    
```

{% endcode %}

Adding this new message to our state is quite straightforward now. This is how the `newMessage` method should now look:

{% code title="App.js" %}

```javascript
    newMessage(messagePayload) {
        this.setState((prevState) => {
            const channelId = messagePayload.channelId.identifier // [1]

            if (
                prevState.channelMessages[channelId]
                    .find(x => x.messageId === messagePayload.message.messageId)
                        !== undefined
            ) {                                                   // [2]
                return prevState
            }


            return Object.assign({}, prevState, {                 // [3]
                channelMessages: Object.assign({}, prevState.channelMessages, {
                    [messagePayload.channelId.identifier]:
                        prevState.channelMessages[messagePayload.channelId.identifier]
                                 .concat(messagePayload.message)
                })
            })
        })
    }
    
```

{% endcode %}

A quick description of what's going on here (follow the numbered labels in the code):

1. We extract the channel ID from the payload. This is the channel that the message was sent in.
2. We are checking if the message already exists for the same ID in our `prevState`. While you may not encounter this frequently, Mitter.io might occasionally send duplicate messages on a payload. This usually happens when Mitter.io cannot confidently determine that a message delivery has occurred, but it might still have propagated. Also, the current implementation performs an entire iteration of the messages in a channel, which might not be very efficient. As an exercise to the reader, modify this to a store backed by a hashing algorithm.
3. We now `concat` this message on to the list of messages for the given channel.

> **NOTE** There are certain caveats with this approach, notably that you might get receive payloads for messages for which you do not have a channels object yet. This could happen if a user was added to a channel after the participated channels were fetched. While such a situation will not arise in our setup, production apps need to always be resilient to partial state and must reconstruct the state in whatever form they can from the available events.

This is pretty much it! However, these changes will not result in you seeing anything, because no messages are being sent. In the next section lets wire it up to send messages.

### Sending messages

To send messages, we'll have to wire up the `Send` button in our `ChannelComponent`. We'll add a few methods, namely `sendMessage()` and `updateTypedMessage` to `ChannelComponent`. Also, we'll set up the handlers on the input fields as we usually do for any React App. The input components will now look like this:

{% code title="ChannelComponent.js" %}

```javascript
<div className='message-input-box'>
    <input
      ref={(input) => { this.messageInput = input }}
      onChange={this.updateTypedMessage}
      value={this.state.typedMessage}
      className='message-input'
      type='text'
    />

    &nbsp;

    <input onClick={this.sendMessage} className='send-message'
          type='submit' value='Send' />
</div>

```

{% endcode %}

We'll modify our state to accommodate changes for the input field and also make the appropriate function binds so that we can use them as callbacks:

{% code title="ChannelComponent.js" %}

```javascript
constructor() {
    this.state = {
        activeChannel: null,
        typedMessage: ''
    }

    this.updateTypedMessage = this.updateTypedMessage.bind(this)
    this.sendMessage = this.sendMessage.bind(this)
}

```

{% endcode %}

And the functions to now send the messages:

{% code title="ChannelComponent.js" %}

```javascript
    sendMessage() {
        const mitter = this.props.mitter

        this.setState((prevState) => Object.assign({}, prevState, { // [1]
            typedMessage: ''
        }))

        this.messageInput.focus()                                   // [2]

        mitter.clients().messages()                                 // [3]
            .sendMessage(this.state.activeChannel, {
                senderId: mitter.me(),
                textPayload: this.state.typedMessage,
                timelineEvents: [
                    {
                        type: "mitter.mtet.SentTime",
                        eventTimeMs: new Date().getTime(),
                        subject: mitter.me()
                    }
                ]
            })
    }

    updateTypedMessage(evt) {
        const value = evt.target.value
        this.setState((prevState) => {
            return Object.assign({}, prevState, {
                typedMessage: value
            })
        })
    }
    
```

{% endcode %}

The `updateTypedMessage` is your standard message to store the state of an input field, and have a way to control it. Let us look into what we are doing in the `sendMessage` function. Pay attention to the numbered labels in the code:

1. &#x20;When we send a message, we would like to clear the input field so that the user can type their next message
2. We would also like to re-focus the `messageInput` field (this property is set in the ref callback of the `<input>` field)
3. We now use the message client to send a message. This message contains the basic minimum fields required to send a message. While the `senderId`, `textPayload` have been discussed before, `timelineEvents` are something new. Let's discuss them for a while.

A `TimelineEvent` is used to record events that occur for a given entity. Mitter.io supports timeline events for `Channels` and `Messages`. For example, this is what is used to store and transmit read/delivered receipts. You are free to use any type of timeline events and interpret them as you wish, with the exception that they may not start with `mitter.` or `io.mitter.`. Also, any message that is sent must have a `mitter.mtet.SentTime` timeline event attached to it. The server then attaches another timeline event recording the server receive time, synchronized to the servers clock.

Once you've done this, open up two browser windows and go to `http://localhost:3000/user/@john` and `http://localhost:3000/user/@candice`. Try exchanging a few messages between them and you'll notice that you have a working chat app!

![A basic working chat app with multiple users](https://94728489-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LLZR00Qt6hZ5Vke2l2g%2F-LNG8gQSc2I5rQOqy9cb%2F-LNG8n_xC3qu3QjkTDdw%2Fbasic-chat-2018-09-25_11.gif?alt=media\&token=7f2e092c-5a32-4eb9-a890-899a1bbd800d)

You might be wondering how your own messages got rendered. This is because every message to a channel is sent out to all participants of the channel and hence every user always gets an echo back of their own message. Do note that on slower networks there will be a significant delay in this occurring, so you might want to populate your message state when the user hits `Send` and then let the network call take its time.

Let's now add a slightly more complex behavior in our application. In the next section, we will explore ACLs and see how we can use them to implement selective deliveries.\ <br>


# Selective Deliveries

As our first introduction to ACLs, in this section, we will employ ACLs on our messages to perform selective deliveries.

### Introducing ACLs

ACLs are a way to define fine-grained access to entities within Mitter.io. You can define which user (or class of users) can or cannot perform an action. ACLs can also be modified during the lifetime of an entity (except for `Messages`) to introduce extremely rich behavior for your apps.

In this example, what we will do is allow users to send private messages within a group channel. Whenever a user types a message starting with *@username*, it will send out a message on the group, but ONLY to that particular user.

An ACL is made up of two lists: the p-list (or the `plusAppliedList`) and the m-list (or the `minusAppliedList`). A given user (also called as an `accessor`) can perform an action, if for the action the accessor is a part of at least one selector in the `plusAppliedList` and is a part of no selector in the `minusAppliedList`. For example, if **@john** were to send a message only to **@candice**, his message would have the following ACL:

```
appliedAcls: {
    plusAppliedAcls: ["read_message:user(@candice)"],
    minusAppliedAcls: []
}

```

On the other hand, if **@candice** wanted to send a message to everyone except **@john**, the ACLs on her message would look like:

```
appliedAcls: {
    plusAppliedAcls: ["read_message:any_user()"],
    minusAppliedAcls: ["read_message:user(@john)"]
}

```

### Allowing mentions in your messages

We need to now allow users to mention specific users when sending messages and attach the corresponding ACLs. To do this, open up `ChatComponent.ts` and modify the `sendMessage` function:

{% code title="ChannelComponent.js" %}

```javascript
    sendMessage() {
        const mitter = this.props.mitter
        const messageToSend = this.state.typedMessage
        const privateMessagePattern = /^(@[a-zA-Z0-9]+)/                       // [1]
        const privateMessageMatch = messageToSend.match(privateMessagePattern)
        let appliedAcls = null

        if (privateMessageMatch !== null) {
            appliedAcls = {
                plusAppliedAcls: [                                             // [2]
                    `read_message:user(${mitter.me().identifier})`,
                    `read_message:user(${privateMessageMatch[0]})`
                ]
            }
        }

        this.setState((prevState) => Object.assign({}, prevState, {
            typedMessage: ''
        }))

        this.messageInput.focus()

        mitter.clients().messages()
            .sendMessage(this.state.activeChannel, {
                senderId: mitter.me(),
                textPayload: this.state.typedMessage,
                timelineEvents: [
                    {
                        type: "mitter.mtet.SentTime",
                        eventTimeMs: new Date().getTime(),
                        subject: mitter.me()
                    }
                ],
                appliedAcls: appliedAcls                                     // [3]
            })
    }

```

{% endcode %}

Everything is the same as the previous function, so let's discuss what we have changed so far:

1. We are using a pattern to match the beginning of a string starting immediately with `@` followed by a sequence of alphanumeric characters. This will match any string that starts with a pattern like `@john`, `@candice` etc.
2. If we find that the pattern matches, we construct the ACL object. If no such pattern is found, the ACL object stays as `null`. There are two things to note here. First, we specify the identifiers in an ACL string directly; they are not nested inside any `identifier` object. This is because Mitter.io ACLs use a custom syntax and are not JSON objects. Second, we have added a read privilege for the sending user as well. When you do not use ACLs, Mitter.io attaches ACLs that allow the user and all the participants in a channel to read the message; but if you decide to use ACLs, then Mitter.io does not perform an operation on the incoming ACLs. So if you did not provide a read message privilege for the user, that message would not be delivered even to the sender.
3. Finally we attach the ACLs to the message object.

Now open up three browser windows pointing to `http://localhost:3000/user/@john` , `http://localhost:3000/user/@amy` and `http://localhost:3000/user/@candice`. Point to the `#roadtrip` channel in all three of them. Try sending out message to each other and mentioning a specific user and see what happens. Here's a quick demo of this app in action:

![A demo app showing private replies within a group chat](https://94728489-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LLZR00Qt6hZ5Vke2l2g%2F-LNGCfYBwbjcYNRuw1sG%2F-LNGGIhwD4Y3iOv_Qk56%2Fselective-deliveries-2018-09-25_12.01.gif?alt=media\&token=26852d3b-60e6-4651-b931-031e7a9e0ac0)

We would also like to render such private messages differently. Maybe, send them as non-text messages itself? In the next section, we will be looking at exploring custom payloads and an introduction to some Mitter helper components.


# Build Your First React Native app

> **NOTE** The Getting Started docs show you how to build your first Mitter.io app with our cloud-hosted sandbox only.
>
> To use it with your docker container, simply change the base API Url (when creating the mitter object) to the address of your running docker container.

To build your first react native app, you can start by using the react native starter app for mitter.io. To get it, clone the repo:

```
git clone https://github.com/mitterio/react-native-starter.git
```

And then use `yarn` or `npm` to install all the dependencies:

```
yarn install # or npm install
```

Unlike the other starter apps, the react-native apps require some additional setup from your side before the application can be run. To do so, follow the setup instructions on the [React native reference](/sdks/web/for-react-native) page.

Once your application is setup, you can follow the [tutorial for your first web app](/getting-started/build-your-first-react-native-app) as it uses the exact same components/modules and API for the starter app.


# Introduction

The entire in-depth documentation of the Mitter.io platform

Mitter.io provides a clean, easy-to-use API messaging solution that allows developers to quickly build applications around messaging. Along with a documented and defined API, **Mitter.io** also distributes SDKs for popular mobile and web platforms with a rich API to endlessly customize their app's behavior.

To get started, [sign-up](https://mitter.io/auth#/) for a free account and create a new application.


# Concepts

Core concepts of the mitter platform

## Application

From Mitter's perspective, an application is an entity that defines a strict data boundary. All API calls are constrained by this condition of tenancy and an application identifier (or proof of authenticity) must be provided to perform an operation. A Mitter.io application would ideally map to an actual application (that you are building). If you have an application ported on multiple platforms (say, Android and the Web), you must continue using the same Mitter.io application so that your users can seamlessly communicate across these platforms.

### User

A User is a special entity that executes specific operations, with the most significant being when acting as a sender of messages. A User is strictly namespaced within an Application, and permissions regarding viewing and participation are tied at a user level.

### User Locator

A User Locator is a globally-unique identifier used to identify a User, for instance, an 'email' or a 'phone number'. User locators are used as look-up keys for users across a variety of user-related APIs. Whenever expressing a user locator, it needs to be prefixed by the type of locator that is being used unless the user locator is provided as an entity.

The standard prefix for emails is `email` and for phone numbers is `tele`. Emails are expected to be as per the format specified in *RFC 822* and phone numbers must be provided in the *E164* format, always prefixed by a country-code specifier.

### Channel

A Channel, simply put, is a collection of Messages, Timeline Events (see below) and Users. A User associated with a Channel is called a 'participant'. There are multiple types of Channels that are supported, with support coming soon for custom Channels types as well. Every channel that is created is assigned a `ruleset`. The ruleset defines the constraints of the channel. An example ruleset would be `io.mitter.ruleset.chats.DirectMessage`. This ruleset enforces the following constraints:

1. This Channel can have exactly two participants at any given time.
2. All participants have to be provided at the time of creation of the Channel.
3. No participant's status can be changed to any status other than `Active`.
4. No participants can be added/removed/replaced.

Other rulesets provide for other common use-cases around channels. In the near future, `mitter.io` will be providing for ways to define such rules in an expressive syntax, which will allow for such rules to be set by the developer and create custom rulesets.

### Message

A Message is simply a communication sent from a user (called a sender) to a Channel. A message may be of multiple types - images, text, video etc., and even custom payloads. A Message can be sent by a sender to a Channel if and only if the sender is a participant in the channel.

A message in `mitter.io` in terms of the model and handling is a complex entity. Do refer to the [reference documentation](/platform-reference-1/messages) on messages for a more detailed description of messages within Mitter.io.

### Timeline Event

A Timeline Event denotes some event having occurred on an entity. Currently, Timeline Events can be attached to Messages and Channels. They simply contain a label and timestamp. You can use Message Timeline Events to implement Message receipts (sent, read, delivered, etc.) and Channel Timeline Events to implement Channel State (User left/joined channel, etc.)

### Profile

A Profile is a set of key-value pairs that can be associated with entities. You can define custom attributes and some rules around them (like `canBeEmtpy`, `allowedContentTypes`) and set values for them. For instance, you can create an attribute called `GravatarUrl` for Users, and you can then set a value for this attribute for a User with an API like:

```
POST /v1/users/{userId}/profile
{
    "key": "GravatarUrl".
    "value": "www.gravatar.com/avatar/<myemailhash>"
}
```

Currently, Profiles are supported only on Users, but will soon be extended to Channels as well.\
�


# Authorization and Access

Accessing mitter APIs

This section details the various ways the **Mitter.io** APIs can be accessed. There are three primary ways that these APIs can be accessed:

1. By making an anonymous call to a public API. Do note that some public APIs, even though they are public, return a different (or rather a more informative) response when called in an authenticated context.
2. Calling the API as an application. This is generally used for wide-user operations, creation and management of chats.
3. Calling the API as a user. This is generally used for managing user profile and messaging.

One of the main differences between methods 2 and 3 is that calling an API as a user can be done purely from a remote-client. A mobile app or a web app can directly make calls to the API as an authenticated user via federated authentication. In contrast, calling an API as an application must only be done from a secure server because this requires non-distributable credentials.

There is also another way, where APIs are called as a subscriber - but these APIs are currently marked internal; and while they can be called using the issued tokens, such usage is highly discouraged. There is a possibility of **Mitter.io** providing Subscriber-level authentication credentials for making API calls. These APIs generally give access to the provisioning and management of a subscriber's applications.

### Application Access Keys

You can request an access key for your application to make calls to the API authenticated as an application. Do note that there is a hard limit on the number of access keys that can be generated for an application (currently 3) and these credentials are expected to be kept securely with extremely limited distribution. As opposed to user-level tokens, a revocation is expected only to occur in the case of the credentials being compromised.

To get a new Access Key, go to your application panel, and click on the 'Access Keys' tab, followed by the 'New access key' button.

![](https://94728489-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LLZR00Qt6hZ5Vke2l2g%2F-LOHncKBcP4ISJmf9ubL%2F-LOHnlUlktmPLN5IX4ij%2Fsui_access_keys_panel.png?alt=media\&token=e741510f-9e6a-4f0c-904b-af67ad4b634c)

Once you do, you will be provided with the following dialog:

![](https://94728489-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LLZR00Qt6hZ5Vke2l2g%2F-LOHncKBcP4ISJmf9ubL%2F-LOHnqRAzOo-RZ0HYLAW%2Fsui_new_access_key.png?alt=media\&token=684f5c12-6982-4cc3-b6b2-047ec4b6f992)

Do note that the `Secret` is presented to the user exactly this one time. Once the user closes the dialog, this information can never be retrieved. It is extremely critical for application security that this secret be never transmitted on the wire.

If you are using one of our SDKs, you can use the `AccessKeyTestClient` / `accessKeyTestClient` to run test methods to verify that the keys you have copied are valid.

#### Signing Algorithm

The secret is never transmitted over the wire for proving authenticity (as you would a password), but rather is used to prove the possession of such a password. For every request, a signing algorithm is followed, which imposes the following requirements on the request:

1. A method with a non-prescribed body is treated as if its content is an empty string (with reference Md5 hash (base64): 1B2M2Y8AsgTpgAmY7PhCfg==)
2. It must contain a Date header, with the value reflecting the time the request was made. A maximum clock skew of `25 seconds` is tolerated. Time values differing from the server time more than that duration are aborted immediately.
3. It must contain a randomly generated string against the header `Nonce`. This value cannot be repeated in any request executed in the last 35 seconds (calculated against server time). A value generated with sufficient entropy need not worry about this constraint - it is statistically guaranteed to have not repeated.

The following headers are mandatory:

1. *Date* (see above for value restrictions)
2. *Nonce* (see above for value restrictions)
3. *X-Mitter-Application-Access-Key* The Access Key that was generated
4. *Content-Type* The content type of the request
5. *Content-Md5* Base64 value of the MD5 hash of the payload of this data. For methods like GET, it is the hash of the empty string (1B2M2Y8AsgTpgAmY7PhCfg==)
6. *Authorization* A string `Auth <access-key>:<digest>` where `<access-key>` is the generated access key and `<digest>`

   is the computed digest as per the algorithm below.

Algorithm for computing the digest:

1. Construct a string which is the concatenation of the following fields with alternating newline characters (`0xB`):
   1. The HTTP method of the call in uppercase
   2. The Content-Type
   3. The Base64 of the Md5 hash of the payload.
   4. Value of the date header that will be supplied along with the request.
   5. The path of the request
   6. Value of the nonce that will be supplied along with the request.
2. Compute the `HmacSha1` digest of the string.
3. Get the Base64 representation of the digest.

An example of the algorithm in `node.js` is available here: <https://git.mitter.io/mitter-io/mitter-ts-node/blob/master/src/auth/AccessKeySigner.ts>

### User Tokens

User Tokens are simple JWTs issued to the user. These JWTs contain a session identifier that the server uses to identify the user. **Mitter.io** only allows signed JWTs to be used and hence, JWTs are to be used as-is from the server, and the server does not allow any choice of signing algorithm or construction of the JWT. For more details on fetching user tokens, refer to our docs on [Federated Authentication](/platform-reference-1/federated-authentication#accessing-the-federated-authentication-parameters).

Currently, user tokens can be created by the application (using an access key) from a server backend, or by using Federated Authentication (such as OAuth) if developing a serverless apps. Refer to the section [Federated Authentication](/platform-reference-1/federated-authentication#accessing-the-federated-authentication-parameters) for more details.

Any user-level operation MUST declare the application within which the operation is being performed. This is done by setting the HTTP header `X-Mitter-Application-Id` in the request with the application id as the value.

#### Sudo User Access

Sometimes it is useful to act on behalf of a user while still using Application credentials, rather than fetching user tokens and managing the whole token lifecycle. Usually, backend services that power your applications will often require to simulate actions as if they were performed by a user (especially if it is a bot or a machine-type user). To do so, follow the same process as the above to sign the request and add all the required headers. In addition to those, another header `X-Mitter-Sudo-User-Id` can also be provided to make the request behave as if it was performed by the user. For instance, given a user with user ID `7479a76c-a9db-47ff-871e-af6c1f7155e1`, the following request is the same as performing the request while using one of the user tokens generated by the user:

```
GET /v1/channels/my-channel/messages
X-Mitter-Application-Access-Key: <access-key>
.. other digest headers ..
X-Mitter-Sudo-User-Id: 7479a76c-a9db-47ff-871e-af6c1f7155e1
```

### Subscriber Access Keys

Similar to Application access keys, Mitter.io also provides a way to authenticate as a subscriber when making all API calls, giving you API access across all your applications and resources while maintaining a single access credential.

Unless you have a very specific use-case that might involve using a dynamic number of applications (the most common use case is when your product itself is tenanted and every client maps to a unique application on Mitter.io), avoid using these type of credentials as they give complete, unrestricted access to all of your subscriber resources.

> **NOTE** To prevent systematic abuse, subscriber access keys are only provided on a case-by-case basis. If you have identified a use for the same, please reach out to <contact@mitter.io> with your use case to request subscriber access keys.

When using subscriber access keys, all API calls must identify an application. A subscriber access key allows you to sudo to any application within your account. To do so, specify the `X-Mitter-Sudo-Application-Id` header. For example, a request to `GET /v1/channels/my-channel/messages` would now look like:

```
GET /v1/channels/my-channel/messages
X-Mitter-Subscriber-Access-Key: <subscriber-access-key>
X-Mitter-Sudo-Application-Id: <application-id>
```

If such a header is not provided, for any of the application-specific APIs, you will get an error with `403` status and error code `missing_context`.

The other headers, i.e., the computed digest in the `Authorization` header, `Date`, `Nonce`, `Content-MD5`, etc. must also be provided as specified in the Signing Algorithm section.

If required, you can further sudo to a particular user within this particular application:

```
GET /v1/channels/my-channel/messages
X-Mitter-Subscriber-Access-Key: <subscriber-access-key>
X-Mitter-Sudo-Application-Id: <application-id>
X-Mitter-Sudo-User-Id: 7479a76c-a9db-47ff-871e-af6c1f7155e1
```

You can also use subscriber access keys to create new applications via an API, but it is not publically supported for all accounts. When you apply for subscriber access keys, and if one is provisioned to you, a separate reference document will also be provided for dynamically creating applications (if your use case requires it).


# Calling the APIs

> If you are using the containerized version of mitter.io, you simply need to replace the host `api.mitter.io` with the host that you set, the default being `localhost:11901.` Refer the [containerization docs](/mitter.io-on-docker) for more information.

> **A quick note on SSL** The API endpoint `api.mitter.io` is available with only HTTPS. You might currently face issues with our certificate provider - letsencrypt (and its co-signer IdentTrust), as neither of them are trusted roots in the JDK installations prior to Java 8u101 and this problem might exist in other programming environments also. To use SSL with mitter.io APIs, you can either manually add them to the JDK keystore or upgrade to JDK 8u101 and similarly for other environments. We will soon be moving to more widely-trusted SSL providers.

At the heart of using **mitter.io** is understanding the semantics and conventions of the APIs that are provided. **mitter.io** is an API-first product and all related distributables borrow from the same convention and are built with extending those same semantics to different abstractions.

The complete API documentation is available [here](https://mitter.docs.apiary.io)

### Basic Conventions

Mitter exposes a RESTful API leveraging the usage of HTTP verbs and status codes to whatever extent possible. All data is exposed and consumed only using JSON as a serialization format, with one deviation when it comes to binary data (discussed further). A `Content-Type` header is absolutely mandatory in every single request (except multi-part) and no other content-type is supported nor is support for the same planned.

All top-level entities are called `Identifiable`s in mitter and CRUD operations require and return only the identifier of those entities.

### Basic Response Conventions

Mitter does not return objects with success-indicator fields. A non `2XX` or `3XX` status code denotes an error and contains an object with the signature:

```javascript
{
    "error": <a human readable error type>,
    "message": <a human readable error message>,
    "error_code": <a specific error code, that is enumerated along with each API>
}
```

All other APIs return objects of specific shape that is documented in the API documentation.

### Basic Request Conventions

Mitter accepts `POST/PUT` requests with the request a JSON with the shape of the entity that the verb acts on. `GET` queries are speciailised using query parameters and `PATCH` requests have separate entity-diff objects. For instance, a `PUT` request for application properties is as follows:

```
PUT /applications/{appId}/property

{
    "systemName": "google-oauth-credential",
    "instanceName": "default-oauth",
    ...
}
```

However, the `PATCH` request is simply:

```
PATCH /applications/{appId}/property/{systemName}/{instanceName}

{
    "makeDefault": boolean
}
```

#### Identifiers

All top-level entities are recognized as `Identifiable` by Mitter and hence are always associated by a unique ID. Despite being globally unique IDs, they are still namespaced by the application they are a part of, in the sense that there is no supported querying model that allows retrieval or operations upon these entities without requiring the application id.

When an identifier is part of the entity, it is supplied as a string-value of a field in the entity. For instance:

```
User {
    "userId": <the users uuid>,
    "screenName": {
       ...
    }
}
```

However, when identifiers are supplied independently of their entities, they are nested in a single-field object like:

```
{
    "identifier": <the identifier>
}
```

Even if an identifier for an entity is nested within another entity, it is still represented using the single-field value as mentioned above. For instance, the shape of a message is:

```
Message {
    "messageId": "id-of-the-message-as-a-string",
    ...
    "senderId": {
        "identifier": "id-of-the-sender-as-a-string"
    }
}
```

In the above example, the `identifier` of the sender is encoded in a single-field object. Instead, if the entity `sender` itself was nested within `message`, the shape would look like:

```
Message {
    "messageId": "id-of-the-message-as-a-string",
    ...
    "sender": {
        "senderId": "id-of-the-sender-as-a-string"
        ...
    }
```

Note that this is not an actual object, but is simply used demonstratively.

> **NOTE** Mitter will soon support the field `identifier` nested in all entity objects, such that a standalone identifier object will be a sub-object of the entity object. This will make it easy for clients to use object-merge and object-pick type operations.

#### Supplying Binary Data

Please refer to the ["Sending binary payloads"](/platform-reference-1/messages#sending-binary-and-image-messages) section in the reference documentation for more information on how to send binary payloads.

#### Displaying Images

When sending a Message as described above, the returned value will contain, as one of its `messageDatum` elements, a key called `link` which contains a URI to the image. The URI has a placeholder `$repr` which is to be replaced by the representation type that was originally requested. For more information refer to the [Image Messages](/platform-reference-1/messages#image-message) section in the reference document.

`representationType` for images is one of the following:

* `base` The image that was uploaded as-is.
* `standard` The image, processed and resized for full-size display with reasonable dimensions.
* `thumbnail` The image resized to be used as a thumbnail.

Mitter uploads all images to a CDN and the images are served from there. The endpoint above issues a `301 Moved Permanently` to point to the CDN. However, there might be a brief interval when the image is not available on the CDN. During this time the endpoint will return a 200 OK with the image data which is stored in an intermediate temporary storage till the upload to S3 takes place.

> **NOTE** The media hosted URL from the mitter server requires standard authentication, but the CDN URIs are public. This allows anyone to fetch images using the CDN URL if it is shared. We will soon be allowing developers to specify their own CDN endpoints (for instance, their own S3 buckets) on which they can set permissions as required.


# Users

A User is identified within mitter.io as an acting entity. Every action that is performed in an Application always has an associated User with it, and a User is referenced by a unique identifier. This identifier is customizable and can be overriden by the sender. 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-_@$#]*`

### Reserved and System Users

When any mitter.io API is called using an Application accesskey/token, the User id is set to `.system`. This user is an all-powerful user with the following (non-overridable) configuration:

1. `.system` cannot send messages to, join, or remove itself from any channel.
2. `.system` cannot login and cannot issue a token for itself.

When a mitter.io API is called without using any credentials, the user id is set to `.anonymous`. This user has absolutely no privileges and cannot call any API without it erroring.

### The User Model

Here is the shape of the User model:

```
{
    "userId": "XwS12-aG2F5-AAL23-1Kl7D",
    "screenName": {
        "screenName": "rylai"
    },
    "userLocators": ["email:crystalmaiden@gmail.com", "tele:+919876543210"],
    "systemUser": false,
    "audit": {
        "createdAt": 1420070400,
        "updatedAt": 0
    }
}
```

Every User must have a screen name assigned to the object that could be used by clients to represent this user visually, but it is recommended that you either use User Profiles for this purpose. The `screenName` field is intended to be a visual aid for developmental purposes.

### User Locators

A User Locator is a globally-unique identifier used to identify a User, for instance an 'email' or a 'phone number'. User locators are used as look-up keys for users across a variety of user-related APIs. Whenever expressing a user locator, it needs to be prefixed by the type of locator that is being used unless the user-locator is provided as an entity.

Currently mitter.io supports two types of locators:

1. email (ex: <email@mitter.io>)
2. tele (ex: +91-1234567890)

When using a user locator to make API calls, there are two ways it can be used:

1. When it is passed as an object, in a request body. In this case, the structure is defined for each locator, and the first key of this strcture must be `@type` mentoning the type of locator it encodes (`email` or `tele`). The structure for an email is:

   ```
   {
    "@type": "email",
    "email": "test@domain.com",
    "verificationStatus": "VerificationPending"
   }
   ```

   and for a phone number:

   ```
   {
    "@type": "tele",
    "phoneNumber": "+915748395939",
    "verificationStatus": "VerificationPending"
   }
   ```
2. When using it in a place where only strings are allowed, like a URI-part or a request parameter, it must follow the format `@type:<serialized-format>`. Each locator type defines its own serialized format. For the email locator, it is as is defined in the *RFC 822* format and for a phone number it must be in the *E164* format. Examples of this would be:

   `email:test@domain.com` `tele:+911234567780`

### User Profile

Along with storing User Locators and their identifiers, mitter.io also supports storing of supplemental information regarding a User in *User Profiles*.

A User Profile, very simply put is a list of `attributes` and to each attribute an associated `value`. There are no restrictions on the name or type of the attribute, and they can be fixed depending upon the use case of your application. For instance, one would wish to store the 'First Name' and 'Last Name' of a user in their profile, so the two attributes the application could use is `firstName` and `lastName`.

All attributes that can be used in an application must be defined before using them, and all user profile attributes are available to all users across the Application. An attribute-def is made up of:

1. Type - This identifies the the type of the attribute key. This is also equivalent to a "system name" for this attribute definition. In our example above, the `type` would be `firstName`.
2. Allowed content types - A list of MIME types which are allowed as values against this attribute definition. In our example, we would only need text values, so we'll specify this as \[`text/plain`].
3. Allowed content encodings - Internally, and on an API level, only strings are supported, so if any binary data is to be stored (for instance an image avatar), then we need to use a content encoding to encode

   that data into a string. For an image avatar, that could be base64 as an example. In our case, we do not need to do any content encoding, so we'll just specify this as \[`identity`].
4. Can be empty - If an empty string (after truncating leading and trailing whitespace) can be set as a value against this attribute or not.

Our request to create a new attribute def would now look like:

```
POST /v1/attributedef

{
    "type": "firstName",
    "allowedContentTypes": ["text/plain"],
    "canBeEmpty": false,
    "allowedContentEncodings: ["identity"]
}
```

Similarly, we would create another attribute def with the type as `lastName`. Once these two attribute defs are created, users can now set their profile data. To set the users first name and the last name:

```
POST /v1/users/80947f0d-2f54-4d69-8990-e281d431eaca/profile/firstName
{
    "contentType": "text/plain",
    "contentEncoding": "identity",
    "value": "Mirana"
}

POST /v1/users/XwS12-aG2F5-AAL23-1Kl7D/profile/lastName
{
    "contentType": "text/plain",
    "contentEncoding": "identity",
    "value": "Nightshade"
}
```

To fetch the profile for a user, a simple GET call would suffice:

```
GET /v1/users/XwS12-aG2F5-AAL23-1Kl7D/profile/firstName

{
    "contentType": "text/plain",
    "contentEncoding": "identity",
    "value": "Mirana"
}
```

However, when it comes to fetching a profile, it is usually the case to fetch the entire profile for display:

```
GET /v1/users/XwS12-aG2F5-AAL23-1Kl7D/profile
[
    {
        "type": "firstName",
        "contentType": "text/plain",
        "contentEncoding": "identity",
        "value": "Mirana"
    },
    {
        "type": "lastName",
        "contentType": "text/plain",
        "contentEncoding": "identity",
        "value": "Nightshade"
    }
]
```

or if you wish to fetch only a specific list of profile values:

```
GET /v1/users/XwS12-aG2F5-AAL23-1Kl7D/profile/firstName,lastName
```

### User Presence

User presence is simply a short description on the availability of the User. Common presence values include 'Online', 'Away', 'Busy' etc. mitter.io allows presence to be set at a user level, and also specify an auto-expire for the presence at which point it can fallback to another presence. This is escpecially useful for a presence like 'Online', which if it hasn't been updated in the last certain time interval, must be auto-set to 'Away' (or some other status).

To set the presence for a user:

```
POST /v1/users/XwS12-aG2F5-AAL23-1Kl7D/presence
{
    "timeToLive": 0,
    "type": "Away"
}
```

A `timeToLive` represents a static presence. This User's presence will now not be changed unless an explicit API call is made. To get the presence of a user:

```
GET /v1/users/XwS12-aG2F5-AAL23-1Kl7D/presence
{
    "type": "Away"
}
```

Let's take an example of a user whose presence is to be set to 'Online'. The client, whenever the user has the application opened, would make a call to the platform every 5 seconds, setting the user's profile as 'Online'. At the same time, if the application stops making this call, the presence should automatically fall back to 'Away'. To do this we can set a `timeToLive` and an `expiresToPresence`:

```
POST /v1/users/XwS12-aG2F5-AAL23-1Kl7D/presence
{
    "timeToLive": 10,
    "type": Online,
    "expiresTo": {
        "timeToLive" 0,
        "type": Offline
    }
}
```

When the client closes the application, these persistent calls will no longer be made, and after 10 seconds since the last call, mitter.io will automatically set the user's presence to 'Offline. These `expiresTo` objects can be chained to any degree. For instance, if you wanted that when a user is inactive for 10 seconds, change the status to 'Away', if for 20, then change it to 'Inactive' and for any time more than 30, set it to 'Offline'

```
POST /v1/users/XwS12-aG2F5-AAL23-1Kl7D/presence
{
    "timeToLive": 10,
    "type": Online,
    "expiresTo": {
        "timeToLive": 10,
        "type": "Away",
        "expiresTo": {
            "timeToLive": 10,
            "type": "Inactive",
            "expiresTo": {
                "type": "Offline",
                "timeToLive": 0
            }
        }
    }
}
```

Do note that `timeToLive` for a presence is the time the presence will stay active from the time it is set. So in the above case, all the nested `expiresTo` call have a `timeToLive` of 10 seconds, since the time is counted from the instant they are set as the users active presence.

### User Tokens and Authentication

A User is authenticated in an HTTP request by checking for an issued token. mitter.io issues revokable tokens to users which can be revoked at-will or expire after 24 hours of inactivity. The token is a signed JWT that merely holds the value of a token identifier held by mitter.io. A JWT token infrastructure is currently utilized for applications to provide a safe way to store certain verifiable user data while their users directly interact with mitter.io in future APIs.

A user token has the following components:

1. A signed token - The actual token that must be provided to execute authenticated operations.
2. A token id - This id can be used to revoke a token or for display/debugging purposes. Only an authenticated user can revoke a token with the token id, so this id is easily shareable.
3. A TTL - A UNIX timestamp denoting the time when this token will expire (if the user were to perform no additional actions).

To access the mitter.io APIs, the following two headers need to be set:

```
X-Issued-Mitter-User-Authorization: <the-signed-token>
X-Mitter-Application-Id: <the-application-id>
```

### User API Reference

The overall operations on a user can be categorized as:

1. Operations on Users
2. Managing User authentication and tokens
3. Managing User metadata

#### Operations on Users

**Creating a User**

To create a user, we make a POST call to `/v1/users`. A user can only be created using an application access key/secret.

```
POST /v1/users
{
    "screenName": {
        "screenName": "rylai"
    },
    "userLocators": [
        {
            "@type": "email",
            "email": "rylai@example.com"
        }
    ]
}
```

And the response is a standard mitter.io identifier for the User:

```
200 OK
{
    "userId": {
        "identifier": "XwS12-aG2F5-AAL23-1Kl7D"
    }
}
```

In the above example, we could also not pass any user locator. User locators can be added later as well. If a `userId` is not supplied, one will be generated.

**Deleting a User**

To delete a user make a `DELETE` call with the user-id in the URI. This API can be called only with a application access key/secret.

```
DELETE /v1/users/XwS12-aG2F5-AAL23-1Kl7D
```

The response being:

```
204 No Content
```

**Fetching a User**

By default, all methods of fetching a User can be performed by any authenticated User within the Application. This behavior can be modified using ACLs.

To fetch a user **given an id**, you can make a `GET` call as below:

```
GET /v1/users/XwS12-aG2F5-AAL23-1Kl7D
```

This returns:

```
200 OK
{
    "userId": "XwS12-aG2F5-AAL23-1Kl7D",
    "userLocators": [ .. ],
    ..
}
```

> **NOTE** All APIs above can also be called by an authenticated user to fetch details for themselves by using `me` as the user identifier. These API calls are not allowed for `.system` and `.anonymous`.

To fetch a list of users given a **list of locators**, make the same call but with the serialized locators passed in a CSV-format with the request parameter `locators`:

```
GET /v1/users?locators=email:rylai@example.com
```

This would fetch a respone similar to the above (it would return an array of User objects). To prevent dictionary-based scrubbing mechanisms to discover users, this API will not return any values if any of the passed locators do not match.

If your application is sandboxed, then you can also use your application credentials to get a list of sandboxed users. This API cannot be called by any other user, even in sandbox mode.

```
GET /v1/users?sandboxed=true
```

**APIs for screen name of a user**

To get the screen name of a user:

```
GET /v1/users/0b604184-7abd-453c-b258-7e4425b31e7f/screenname
```

The above API returns a list of screen names, as this API supports fetching the screen names for multiple users. Pass the user identifiers in a CSV format. The screen names are returned in the same order as the user identifiers passed in the request. An example response would be:

```
{
  "screenNames": [
    {
      "screenName": "rylai"
    }
  ]
}
```

To modify a screen name, make a `PUT` request:

```
PUT /v1/users/0b604184-7abd-453c-b258-7e4425b31e7f/screenname
{
    "screenName": "crystal-maiden"
}
```

On successful execution, it returns:

```
204 No Content
```

#### Managing User Authentication and Tokens

All authenticated users can get additional tokens issued against them and revoke any tokens that are issued to them. The first initial token required for them to authenticate can only be issued by the `.system` user, i.e. using the application access key/secret.

No tokens can be issued for the reserved system users.

Currently, user tokens are valid for 24 hours, after which the User will need to re-authenticate, i.e., `.system` will have to issue a token. However, a User can extens the `ttl` before the expiry time.

**Getting a New User Token**

To get a new user token,

```
POST /v1/users/XwS12-aG2F5-AAL23-1Kl7D/tokens
```

Which returns:

```
{
  "userToken": {
  "signedToken": "eyJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJtaXR0ZXI... truncated",
  "supportedHeaders": [
    "X-Issued-Mitter-User-Authorization"
  ],
    "tokenId": "V4lKpqsbZK9C587R"
  }
}
```

The `signedToken` is the token that is to be set as an HTTP header whenever making requests on behalf of this User. The name of the header(s) which can hold this value is provided in the `supportedHeaders` field. In addition to this, a `tokenId` is already returned which can be later used to revoke this user's tokens.

If a User is already authenticated, they can instead also use the following request to get additional tokens:

```
POST /v1/users/me/tokens
```

**Revoking a User Token**

To revoke a user token, make a `DELETE` call on the `tokenId`:

```
DELETE /v1/users/XwS12-aG2F5-AAL23-1Kl7D/V4lKpqsbZK9C587R
```

This operation can only be performed by the `.system` user, or by any other user for themselves.

If a user is already authenticated, they can instead also use:

```
DELETE /v1/users/me/V4lKpqsbZK9C587R
```

If the user wants to revoke the token they are currently authenticating with, they can use:

```
GET /v1/users/me/logout
```

Do note that this request uses the `GET` method and not the `DELETE` method so as to allow this to be a clickable link that can logout users that can be handled by browsers.

**Listing All Tokens**

To list all tokens that are issued to a user:

```
GET /v1/users/XwS12-aG2F5-AAL23-1Kl7D/tokens
```

This would return:

```
200 OK
[
  {
    "token": {
      "token": "s3ee4bg1tctbj32igm15ipk0ta"
    },
    "ttl": 85962,
    "tokenId": "V4lKpqsbZK9C587R"
  }
]
```

Do note that the `signedToken` is not returned again. This is an API purely for reference purposes. The `ttl` gives the approximate number of seconds left before this token expires.

Any user can list their own tokens, and only the `.system` user can fetch tokens for other users. For an authenticated users, they could also:

```
GET /v1/users/me/tokens
```

This call is not permitted for `.system` or `.anonymous`.


# Channels

A Channel acts as a delivery target for Messages. A Channel also associates certain users to it called `participants`. A `participant` for a given channel has special semantics in certain contexts, for example, by default only a `participant` can send messages to a Channel (with ACLs this behavior can be overriden). Fetching a list of participants for a channel is also a common use-case for most of the applications. A channel is uniquely identified by an identifier and can be overriden by the creator of the channel. 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-_@$#]*`

### Reserved Channels

While these APIs and features are planned for a future release, certain channel ids are employed for specific purposes and are reserved:

1. `.broadcast` Channel used for an application wide broadcast.
2. `.control` Channel used for control messages to co-ordinate front-end clients.

### The Channel Model

The model of a Channel looks as below:

```
{
    "channelId": "f8cc57d8-af38-4313-85e8-cbe62a2ebf23",
    "defaultRuleSet": "io.mitter.ruleset.chats.DirectMessage",
    "participation": [
        {
            "participantId": "0b604184-7abd-453c-b258-7e4425b31e7f",
            "participationStatus": "Active"
        },
        {
            "participantId": "87d1ab9b-40e8-4017-a4f1-2deae7e05d74",
            "participationStatus": "Active"
        }
    ],
    "systemChannel": false,
    "audit": {
        "createdAt": 144656562,
        "updatedAt": 144666852
    }
}
```

A quick overview of the relevant fields:

1. The `channelId` contains the unique identifier for the channel.
2. The `defaultRuleSet` specifies the default rule set that is applied to the Channel. A ruleset constraints the operations that can be performed on the Channel. Refer to the section on *Rulesets* in this page for more details.
3. The `participation` is a list of `ChannelParticipation` objects which contain the user identifier of the participant and the status of the participant. Refer to the *Participation* section in the page for more details.
4. The `systemChannel` defines whether this is a system channel or not. While one can create system channels, this is ideally used for messages that are for communication between machine/software components and are not enlisted when fetching a list of channels (unless explicitly requested for).
5. 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.

### Rulesets

A ruleset defines the constraints on a Channel in terms of messaging, participation etc. mitter.io frontend SDKs also use the ruleset identifier for directing behavior (or in some cases the default behavior) on these Channels. As of now, Channels can only select from a set list of rulesets and the ability to define and set custom rulesets is planned for the near future.

The rulesets provided out of the box are:

1. `io.mitter.ruleset.chats.DirectMessage`
2. `io.mitter.ruleset.chats.GroupChat`
3. `io.mitter.ruleset.chats.SystemChannel`
4. `io.mitter.ruleset.chats.SingleParticipantChannel`

#### Direct Message

**TYPE** `io.mitter.ruleset.chats.DirectMessage`

A direct message channel has the following constraints:

1. This channel must have exactly two distinct participants at any given point of time.
2. Participants cannot be added/removed/replaced at any point of time in this channel.
3. Only the involved participants can send messages to this channel.
4. Both the participants in this channel must be provided and set at the time of creation

This type of a channel is used to facilitate a communication between two people, as is the use-case of instant messaging applications.

#### Group Chat

**TYPE** `io.mitter.ruleset.chats.GroupChat`

A group chat has the following constraints/provisions:

1. It can have any number of participants, even zero.
2. Participants can be added/removed to it at any point of time.
3. All participants of this Channel can send Messages to this Channel.
4. This Channel can be deleted, at which point all participations are also revoked.
5. All participants of this channel can read a Message from this channel.
6. No User that is not a participant of this channel can send a message or read from this Channel.

#### System Channel

**TYPE** `io.mitter.ruleset.chats.SystemChannel`

This kind of a channel has the following constraints/provisions:

1. At the time of creation, exactly one participant must be provided.
2. The application-level user `.system` is automatically added to this channel.
3. Neither of the two participants can be deleted/replaced.
4. The user (other than `.system`) may or may not have access to the messages from the channel (to use to send upstream-only messages).

This is used generally for control-type messages to be pushed to users from a central co-ordination system. Messages on this channel are not intended to be read by humans, i.e. they are not intended to be in a human readable format.

#### Single Participant Channel

**TYPE** `io.mitter.ruleset.chats.SingleParticipantChannel`

This channel has the following constraints/provisions:

1. At the time of creation exactly one user can be provided.
2. This user cannot be removed/replaced.
3. No new participants can be added to this channel.

This channel is also not intended to contain human readable messages. This channel is intended to be used by different endpoints (devices) used by the same user to co-ordinate information amongst each other.

### Participation

A participation defines an association of a user with a channel. A participation also contains a status of the participation that may take upon one of the three values `Active`, `ReadOnly` and `Disabled`. This status while mainly intended for application developers to build the logic on, mitter.io centrally uses it to establish the following semantics:

1. When fetching a list of participants, by default only `Active` participants are returned.
2. Messages sent to a channel are delivered only to `Active` and `ReadOnly` participants.
3. When using your own ACLs, a `Disabled` or a `ReadOnly` participant is essentially the same as any other participant, and it acts purely as a way to narrow down your ACL rules by participation classes. This is an effect of mitter not assigning any default ACLs when you are using your custom rules.

Participants to a channel can be added/removed after channel creation if the ruleset permits that operation.

### Channel API Reference

The APIs around channels can be categorized as:

1. Operations on Channels
2. Operations on Channel participation

#### Operations on Channel

**Creating a Channel**

To create a channel, make a `POST` request. By default, any authenticated user can create a Channel. This behavior can be overriden using ACLs.

```
POST /v1/channels
{
  "defaultRuleSet": "io.mitter.chats.GroupChannel",
  "participation": [
    {
      "participantId": "0b604184-7abd-453c-b258-7e4425b31e7f",
      "participationStatus": "Active"
    }
  ]
}
```

Note that the audit field, if sent, will be ignored.

On successful creation, the server returns the identifier of the Channel.

```
200 OK
{
  "identifier": "c36cc2eb-f500-4a85-a8e6-2090cd68254c"
}
```

**Deleting a Channel**

To delete a Channel, make a `DELETE` call. By default only `.system` can delete channels. In the future the defaults will be enforced by the ruleset. This behavior can also be modified using ACLs.

```
DELETE /v1/channels/c36cc2eb-f500-4a85-a8e6-2090cd68254c
```

On successful deletion the server returns a `204`

```
204 No Content
```

#### Get a Channel

To get a channel, make a `GET` call.

```
GET /v1/channels/c36cc2eb-f500-4a85-a8e6-2090cd68254c
```

The server returns the channel object.

```
{
  "channelId": "c36cc2eb-f500-4a85-a8e6-2090cd68254c",
  "defaultRuleSet": "io.mitter.chats.GroupChat",
  "participation": [],
  "systemChannel": false
}
```

Do note that the GET call **DOES NOT** populate the participants of a channel. It always returns an empty array.

#### Timeline events <a href="#timeline-events-2" id="timeline-events-2"></a>

**Adding a timeline event to a channel**

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

```
POST /v1/channels/f8cc57d8-af38-4313-85e8-cbe62a2ebf23/timeline

{
    "type": "mitter.mtet.ReadTime",
    "eventTimeMs": 1506968364492
    "subject": "a6097f2f-b5cf-4afc-b246-019da17c281b"
}
```

Unlike the timeline event operations for messages, timeline events on channels do not support adding timeline events to multiple channels in a single request. In the URI only one channel id can be specified.

On successful execution, the server returns a `204`

```
204 No Content
```

#### Getting Timeline Events for a Channel <a href="#getting-timeline-events-for-a-message-1" id="getting-timeline-events-for-a-message-1"></a>

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

```
GET /v1/channels/f8cc57d8-af38-4313-85e8-cbe62a2ebf23/timeline
```

This returns a list of `ChannelTimelineEvent` objects

```
200 OK

​[
    {
        "channelId": "a609f",
        "timelineEvent": { ... }
    },
    {
        "channelId": a609f",
        "timelineEvent": { ... }    
    },
    {
        "channelId": "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/timeline
    ?eventTypeFilter=my.channelEvent
```

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

### Participation APIs

#### Add a Participant to a Channel

To add a participant to a channel, make a POST call. By default, any authenticated user can add themselves to a channel and `.system` can add any user as a participant to any channel. This behavior can be modified using ACLs.

```
POST /v1/channels/c36cc2eb-f500-4a85-a8e6-2090cd68254c/participants
{
    "participantId": "aab1a003-7648-4900-926a-94fda15649b6",
    "participationStatus": "Active"
}
```

On successful execution, the server returns a `204`

```
204 No Content
```

#### Remove a Participant from a Channel

To remove a participant from a channel, make a DELETE call. By default, any authenticated User can remove themselves as a participant from a Channel and `.system` can remove any user.

```
DELETE /v1/channels/c36cc2eb-f500-4a85-a8e6-2090cd68254c/participants/aab1a003-7648-4900-926a-94fda15649b6
```

The parameter after `/participants/` is the user identifier of the participant that is to be removed. On successful execution, the service returns a `204`

```
204 No Content
```

#### Get all Participants for a Channel

To get all participants for a Channel, make a `GET` call. By default any participant for a Channel can fetch the list of participants and `.system` can do so for any Channel.

```
GET /v1/channels/c36cc2eb-f500-4a85-a8e6-2090cd68254c/participants
```

The server returns a list of `ChannelParticipation` objects:

```
200 OK
[
    {
      "channelParticipationId": "11c9b595-d6dc-4a47-97aa-c98b96bc01d0",
      "participantId": {
        "identifier": "0b604184-7abd-453c-b258-7e4425b31e7f"
      },
      "participant": {
        "userId": "0b604184-7abd-453c-b258-7e4425b31e7f"
      },
      "participationStatus": "Active",
      "channelId": {
          "identifier": "c36cc2eb-f500-4a85-a8e6-2090cd68254c"
      }
    }
]
```

By default the server returns the entire object of participants. If you want the identifiers, you can set the `expandParticipants` query paramater:

```
GET /v1/channels/c36cc2eb-f500-4a85-a8e6-2090cd68254c/participants?expandParticipants=false

200 OK
[
    {
      "participantId": {
          "identifier": "0b604184-7abd-453c-b258-7e4425b31e7f"
      },
      "participationStatus": "Active"
    }
]
```

#### Get all Channels the User is a Participant in

To get all the Channels in which a user is a participant, make a `GET` call. By default any user can fetch their own participating Channels and `.system` can do so for any User. This behavior can be modified by using ACLs.

```
GET /v1/users/0b604184-7abd-453c-b258-7e4425b31e7f/channels
```

On successful execution, the server returns a list of `ParticipatedChannel` objects.

```
200 OK
[
    {
        "participationStatus": "Active",
        "channel": {
            "channelId": "c36cc2eb-f500-4a85-a8e6-2090cd68254c",
            "defaultRuleSet": "io.mitter.chats.GroupChat",
            "participation": [],
            "systemChannel": false,
            "audit": {
                "createdAt": 144656562,
                "updatedAt": 144666852
            }
       }
    }
]
```


# Channel Streams and Typing Indicators

In addition to messages, Mitter.io also supports sending small packets of information meant to provide additional, real-time information that is not significant enough to be persisted and/or queried. Mitter.io supports this using a feature called 'Channel Streams'. Every channel can have multiple channel streams mapped to it and custom channel streams can be created for Mitter.io applications. Example use-cases include:

1. Typing indicators - Channel streams can be used to send information about when a user is typing. \*\*
2. Pointer tracking data - For example when making a collaborative drawing board channel streams can be used to send information regarding the location of the mouse pointer of individual users.
3. Chunks in a multimedia stream - When streaming audio/video, individual chunks can be sent over channel streams.

*\*\* There is separate reserved channel stream for sending typing indicators. Refer to the section below on*  [*Typing indicators*](/platform-reference-1/channels/channel-streams-and-typing-indicators#typing-indicators) *for more information.*

### Criteria for using channel streams

In the examples above, channel streams are the best fit over messages for the following reasons:

1. Real-time significance only - The significance of sending the data is only when it is instantaneous. Both of them would not be required to be queried or to be displayed a trail of.
2. Context-free - Messages are not context free because conversation builds a context. Both the examples do not require a strict ordering or the context within other similar items that are sent over the channel stream.
3. Recoverable state and loss tolerance - If building state using stream data, if there is a provision to recover state then channel streams are a good example. For instance, if every 20 stream datum sent an absolute mouse pointer location was sent or if a keyframe for the multimedia stream was sent then the state at that point in time can be recovered using it. For use-cases that are tolerant to loses in communication and the required state can be recovered then it is a good use-case for channel streams

### Reserved channel streams

There is only one channel stream that is reserved and is automatically created for every channel:

1. `.typing-indicator-stream` Used to send a signal when a user has started typing. Refer to the section on **typing indicators** for more information.

### Models

#### Channel Stream

A channel stream has a very simplified model:

```javascript
{
    "streamId": "56gil-asLHk-j3pKl-ndYQn",
    "type": "mitter.streams.BroadcastObjectStream",
    "supportedContentTypes": ["application/json"]
}

```

The `type` could be overridden to anything you want, internally it is not processed.

#### Context Free Message (Stream data)

The data that is sent to a channel stream is called a `ContextFreeMessage`. The model of the same is:

```javascript
{
    "contentType": "application/json",
    "context": "pointer-location-update",
    "senderId": "puRF7-QXnH5-A268z-kx7qq",
    "data": {
        "hello": "world"
    }
}

```

The difference of a `ContextFreeMessage` from a regular message is that a context-free message is never  persisted, does not contain an identifier and cannot be retrieved. A context-free message is delivered purely on a best-effort basis and there are no guarantees on delivery of the same. As opposed to messages context free messages are delivered much faster and are only delivered on messaging pipelines. For clients using HTTP polling, context free messages or messages from a stream cannot be fetched.

A `context` is provided to give a categorization to the type of message that was sent. For example, if on a stream you were sending two kind of messages - one containing only the differences in the mouse pointer from the last location and one where an absolute location was provided, then these two type of messages can be coded as two different contexts.

#### Pipeline payload

Context free messages or stream data can be received only over messaging pipelines like FCM, APNs, WebSockets etc. On a client the message is delivered confirming to the following model:

```javascript
{
    "channelId": "1CkkG-1qMig-5FsQD-9Tz45",
    "streamId": "56gil-asLHk-j3pKl-ndYQn",
    "streamData": {
        "contentType": "application/json",
        "context": "pointer-location-update",
        "senderId": "puRF7-QXnH5-A268z-kx7qq",
        "data": {
            "hello": "world"
        }
    }
}

```

### Typing indicators

To support typing indicators, whenever a user is typing a stream data is to be sent to the stream `.typing-indicator-stream` for the channel. It should follow the structure:

```javascript
POST /v1/channels/1CG-1s/streams/.typing-indicator-stream
{
    "contentType": "text/plain",
    "context": "mitter.ssd.UserTyping",
    "senderId": "puRF7-QXnH5-A268z-kx7qq"
}
```

Clients should debounce this call to send out typing indicators and we recommend that they are sent out only once in about 5 seconds.

On receiving a payload of this type (refer to the documentation for the SDK you are using for receiving messaging pipeline payloads), the receiving client considers the user specified in the `senderId` field to be in typing status for a specified amount of time (we recommend this time to be 7 seconds). If during this time interval (before the user is reset to a non-typing state) if another channel stream datum is received for the same user, this timer should be reset.

### Channel Stream API reference

#### Create a new channel stream

You can create a new channel stream for your purposes if the reserved channel streams do not suffice. To create a channel stream the `create_channel_stream` privilege is required, which is always granted to `.system` (i.e. when using an application access key/secret).

```javascript
POST /v1/channels/{channelId}/streams
{
    "streamId": ".. your stream id ..",
    "type": "mitter.streams.BroadcastObjectStream",
    "supportedContentTypes": ["application/json"]
}

```

On a successful call the server returns the `Stream` object back, populating an identifier if you did not provide one.

```javascript
200 OK
{
    "streamId": "1CkkG-1qMig-5FsQD-9Tz45",
    "internalId": "X6uCD-OLyuO-TUQnJ-J0kWV",
    "type": "mitter.streams.BroadcastObjectStream",
    "supportedContentTypes": ["application/json"]
}

```

**NOTE** You can omit `supportedContentTypes` to make the stream accept data in any type.

#### Send stream data

Data is sent to a channel stream by sending a context free message. To send message to a stream the client needs to have `send_to_channel` privilege (if the sender id is the same as the user making the call) or the `send_as_other_to_channel` (if the sender id is different than the user making the call). By **default** all users have the `send_to_channel` privilege granted to them and `.system` **always** has `send_as_other_to_channel` granted to it. Do note that this is the same privilege that is checked for sending messages to a channel

**NOTE** We currently do not support access control over specific streams i.e. a user granted `send_to_channel` can send messages to all streams in that channel. We are currently working on having stream specific privileges.

```javascript
POST /v1/channels/{channelId}/streams/{streamId}
{
    "contentType": "application/json",
    "context": ".. your context ..",
    "senderId": "puRF7-QXnH5-A268z-kx7qq",
    "data": {
        "hello": "world"
    }
}
```

On a successful call the server returns the same message back with any defaults populated (do note that context free messages do not have an identifier

```javascript
200 OK
{
    "contentType": "application/json",
    "context": ".. your context ..",
    "senderId": "puRF7-QXnH5-A268z-kx7qq",
    "data": {
        "hello": "world"
    }
}
```

#### Receiving stream data

Receiving stream data is not supported via HTTP and stream data can be received only via messaging pipelines. To receive stream data over a channel stream all users with `read_from_channel` privilege are marked as delivery recipients. For the shape of the received payload, refer to the section [**pipeline payload**](/platform-reference-1/channels/channel-streams-and-typing-indicators#pipeline-payload)**.**


# Messages

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
{
    "payloadType": "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": "Standard",
    "payloadType": "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 <a href="#getting-timeline-events-for-a-message" id="getting-timeline-events-for-a-message"></a>

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.


# Delivery Endpoints (Push Notifications)

Delivery endpoints identify end-user devices where a message is delivered as opposed to the model of a user requesting for messages via HTTP. A deliver endpoint is specific to the delivery mechanism being used. mitter.io currently supports FCM delivery endpoints for both the web and mobile and WebSockets for Web. A delivery endpoint is assigned to a user and one user can have multiple delivery endpoints of differing types.

When a message is sent to a channel, a list of users that have access to the message and should be receiving it is computed. Then the message is delivered to all the delivery endpoints that are registered against that user. mitter.io internally handles retrying failed messages and other implementation details of the delivery mechanisms, for instance, it automatically udpates the device registration token whenever the FCM upstream server instructs that the old token has expired and a new one is to be used. Developers can continue using the same semantics for message delivery that is provided by mitter.io at higher levels of abstractions like users, messages, channels etc.

### The Delivery Endpoint Model

The base model of a delivery endpoint looks rather simple, it consists of a serialized format of the endpoint and the type:

```
{
    "serializedEndpoint": "fcm:dXA1kcigCPw:APA...",
    "endpointType": "fcm"
}
```

The serialized endpoint if it contains multiple parts to it must be stored in a canonical format such that it can be used as a lookup key for further operations.

For an FCM delivery endpoint, the model used is:

```
{
    "endpointType": "fcm",
    "registrationToken": "dXA1kcigCPw:APA..."
}
```

Note that the registration token does not contain the `fcm` prefix. This is a device token that is issued by both the web and mobile firebase SDKs. You will also need to setup Firebase credentials in your application for the messages to deliver. For more information refer to the LINK(Setting up firebase) section in the reference documentation.

### Delivery Entities

Not only messages, but certain other entities, like `TimelineEvents` are also pushed to the relevant users to their endpoints. All entities differ slightly in their signature from their mitter counterparts.

#### Messages

A `NewMessagePayload` is delivered to a user that the user should be receiving as a part of their participation in a channel. A message model that is delivered looks like:

```
{
    "@type": "new-message-event",
    "message": {
        "messageId": "<message-id">,
        .. rest of the message object ..
    },
    "channelId": "<channel-id-of-the-message>"
}
```

#### Channel

A `NewChannelPayload` is delivered to a user whenever a user should be notified that a new channel is created. This is computed if a user happens to have read access to the channel, even if the user is not a participant. The model looks like:

```
{
    "@type": "new-channel-event",
    "channel": {
        "channelId": "<channel-id>",
        .. rest of the channel object ..
    }
}
```

#### Timeline Event

A `NewMessageTimelineEvent` is sent to a user with the same semantics as that of receiving a message. If a user is a target for receiving a message, they are also a target for every timeline event on that message. The model of that:

```
{
    "@type": "new-timeline-event",
    "timelineEvent": {
        "type": "mitter.mtet.SentTime",
        .. rest of the timeline event object
    },
    "messageId": "<message-id>"
}
```

### FCM Delivery Specifics

The raw message that is sent over FCM confirms to the data shape that FCM requires:

```
{
    "data": "{\"@type\":\"new-message-event\", \"message\":{ .. } }",
    "registration_ids": ["device-token-a0", .. ],
    "notification": {
        "body": "notification body",
        "icon": "notification icon",
        "title": "notification title"
    }
}
```

Do note that the `data` field is a String, which means that the value of the field needs to be deserialized to one of the messaging pipeline payloads. The type of the sent payload is mentioned in the `@type` field of the deserialized value of the `data` field.

For the `notification` field, which is used by FCM to automatically display notifications on the user device, refer to the next section.

#### Cloud Notifications

FCM (and other delivery mechanisms) supports sending push notifications if the message that is sent to the device populates certain fields. In the case of FCM, the object `notification` needs to be set with values for `body`, `title` and `icon`. Since mitter messages do not offer direct access to it, you can use specific messageDatum objects with the dataType as `cloud-notification` to denote to the FCM delivery facilitator to push the data as a notification. For future mechanisms, this API will continue to be supported with a similar model.

To send a notification to a user, one can use a message body like:

```
{
    "payloadType": "mitter.mt.Text",
    "textRepresentation": "Axe is back!",
    "messageDatum": [
    {
        "dataType": "cloud-notification",
        "data": {
            "body": "Axe is back!",
            "icon": "axe-icon.png",
            "title": "Axe!"
        }
    }
}
```

All other mechanisms for delivery will receive this as a regular message, but on FCM and other push-based mechanisms will convert this to the appropriate format for sending out a push notification. In the case of FCM, this would look like:

```
{
    "data": "{\"@type\":\"new-message-event\",\"message\":{ .. } }",
    "registrationIds": [ .. ],
    "notification": {
        "body": "Axe is back!",
        "icon": "axe-icon.png",
        "title": "Axe!"
    }
}
```

### Operations on Delivery Endpoints

#### Adding a New Endpoint

To register a new delivery endpoint, you can make a `POST` call. By default, only the user can register an endpoint for themselves and `.system` can do it for any user. This behavior currently cannot be overridden.

```
POST /v1/users/a1c222c7-94c1-4046-8e91-e6f45d9490b2/delivery-endpoints
{
    "endpointType": "fcm",
    "registrationToken": "dXA1kcigCPw:APA..."
}
```

On successful execution, this method returns a `204`

```
204 No Content
```

For an authenticated user, they can register a device endpoint for themselves by calling `/v1/users/me/delivery-endpoints`.

#### Deleting an Endpoint

To delete an existing delivery endpoint, you can make a `DELETE` call. By default, only the user can register an endpoint for themselves and `.system` can do it for any user. This behavior currently cannot be overridden.

```
DELETE /v1/users/a1c222c7-94c1-4046-8e91-e6f45d9490b2/delivery-endpoints/dXA1..
```

On successful execution, the server returns `204`

```
204 No Content
```


# Federated Authentication

Currently, **mitter.io** supports only OAuth as a federated authentication mechanism and we are slowly rolling out the features, we currently only support Google as the OAuth provider. Soon, we will be rolling out multiple provider support out of the box and also the ability to use any arbitrary OAuth provider for authentication.

To set up Google Federated Authentication, you need to first to get OAuth credentials from Google. To do so,

1. Go to the [Google Developer Console API Credentials page](https://console.developers.google.com/apis/credentials) and

   select your project from the menu on the top-bar.
2. Click on *Create Credentials* and select *OAuth Client Id*.
3. In the next page select *Web Application*.
4. Use any name you want for the credential. In the *Authorized redirect URIs* enter the URI from the **mitter.io** subscriber panel. To do so, go to the subscriber panel and go to *Hosted Services*. Under the *Google Authentication Service* tile, there will be a field called *Google Redirect URI*. Copy the value shown here.

   ![](https://94728489-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LLZR00Qt6hZ5Vke2l2g%2F-LOHncKBcP4ISJmf9ubL%2F-LOHoRmgSwnzvNhXmiu0%2Fsui_hosted_services_gauth.png?alt=media\&token=d3310272-21ea-4ca1-82b1-7fa00adcdfac)
5. Save the credential. When you do so, Google will display a *Client Id* and a *Client Secret*. Keep this available with you.
6. Go back to the Subscriber UI and go to *Properties*. Go to *New Property > Google > Credentials > OAuth Credentials*.
7. In the dialog that shows up, enter the values that you got from the Google Developer console.
8. The *instance-name* is for your record-keeping purposes, so you can use anything you want. The project number and the application name does not matter for OAuth, but it is recommended you use the project number and the application name the same as that of your Google project.
9. Save these details.

![](https://94728489-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LLZR00Qt6hZ5Vke2l2g%2F-LOHncKBcP4ISJmf9ubL%2F-LOHoBM_NvhqHatiDM2E%2Fsui_new_goauth_property.png?alt=media\&token=b29cd585-02d2-4ead-aeeb-9917e808fe8a)

So far, this is the setup:

1. Mitter now provides a URI that you can use for sending your users to the Google OAuth page. This directly links to the Google OAuth service.
2. Google on authenticating the service, will forward the user to a **mitter.io** hosted page, where mitter will create a new user (or link an existing user using the email) and verify the tokens with Google.
3. Mitter will automatically log-in the user, and generate a user authentication token for the user.

![](https://94728489-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LLZR00Qt6hZ5Vke2l2g%2F-LOHncKBcP4ISJmf9ubL%2F-LOHoEAslXm_AQSdWGoi%2Fsio_new_mitter_oauth_property.png?alt=media\&token=e3df25b9-1be7-495f-8ffc-c8a7fafcf448)

However, currently, mitter cannot communicate with your application about all these operations. To allow it to do so, go to the subscriber panel, *Properties* tab and go to *New Property > Mitter > OAuth Integration*. Enter the following information:

1. \*instance-name' This can be anything you prefer, it is for your record-keeping purposes.
2. *OAuth redirect URI* The URI mitter should redirect you to once it verifies with the authenticating service (in this case, Google) or in the case of an error.
3. *require state signing* This feature is currently not supported, but when in use, mitter will only accept

   authentication requests which need to be signed using one of the issued access keys. If this is selected, no frontend client can make authentication requests without a backend, as they would not be in possession of an access key for the application.

Once all these steps are done, the Google Authentication service in the *Hosted Services* panel will show up as **ACTIVE**.

![](https://94728489-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LLZR00Qt6hZ5Vke2l2g%2F-LOHncKBcP4ISJmf9ubL%2F-LOHoWtwvjcW2dv14k3N%2Fsui_hosted_services_gauth_active.png?alt=media\&token=17c9b625-9da3-42b9-bf3d-81befb5cf919)

To test it once, visit the URI shown in *Google OAuth endpoint* and the Google authentication service should show up, you should be able to log-in and be redirect to the URI you provided.

### Accessing the Federated Authentication parameters

When the user is redirected to the URI as entered in the *OAuth Integration* config, the following query parameters are sent with it:

1. *federatingService* The service used to authenticate the service. In this case, it will always be

   *GoogleFederationService*.
2. *linkCandidates* The candidates found for auto-linking to the user. If there are multiple users, no user will be linked, and your app must make the decision of which user to link (generally by user input) using the linking API. For the GoogleFederatingService, there is guaranteed to be exactly one user in this list.
3. *autoLoginToken* If the user was logged in, a token that can be used to authenticate the user.
4. *federatedUserId* An identifier used to identify the federated user. This is **NOT THE SAME** as the user id.
5. *linkedUser* The user id of the user which is linked to this federated user.
6. *externalServiceTokens* The tokens returned by the federating service. In the case of google authentication service, this will contain the access token as provided by Google. If state signing is used, the refresh token will also be given. Both tokens will be delimited by a comma (,).


# Basic Permissions and Privileges

Not all applications would require the usage of ACLs to finely tune the permission model for each of the entities in their application. At a default level, the ACLs are defined on each entity within your mitter.io application such that a basic permission model is provided in terms of access and grants to an acting user.

Throughout the document we have covered the permissibiltiy of each operation depending on the actor and this page merely collects all that information in one place. In this document `.system` referes to the user that is resolved when accessing the APIs using an application access key/secret.

### Channel

For a channel:

1. **Creating**
   1. Any `authenticated user` can create a channel
   2. `.system` can create a channel
2. **Deletion**
   1. Only `.system` can delete a channel
3. **Adding a participant**
   1. Any `authenticated user` add themselves to a channel
   2. `.system` can add any user to any channel
4. **Removing a participant**
   1. Any `authenticated user` can remove themselves from a channel.
   2. `.system` can remove any participant from any channel
5. **Getting a list of participants**
   1. Any `participant` of a channel can get a list of all participants in that channel.
   2. `.system` can get all participants for any channel.
6. **Getting a channel object**
   1. Any `participant` of a channel can access the channel object.
   2. `.system` can get the channel object for any channel.

### Message

For a message:

1. **Sending**
   1. Any `participant` of a channel can send a message to a that channel with themselves as the sender.
   2. `.system` can send a message to any channel with any user as the sender.
2. **Reading**
   1. Any `participant` of a channel can read and will receive all messages that are sent to a channel.
   2. `.system` can read any message on any channel, but it won't `receive` any messages as `.system` cannot assign delivery endpoints to itself.
3. **Deleting**
   1. Any `authenticated user` can delete a message that was sent by them.
   2. `.system` can delete any message.

### User Operations

1. **Creating**
   1. Only `.system` can create users
2. **Authentication**
   1. Only `.system` can get tokens issued, revoked or listed for any user, except itself.
   2. Any `authenticated user` can get additional tokens for themselves.
   3. Any `authenticated user` can revoke any token that has been issued to them.
   4. Any `authenticated user` an list all token ids for tokens that have been issued to them.
3. **Deletion**
   1. Only `.system` can delete users.
4. **Metadata**
   1. Any `authenticated user` can fetch or patch metadata for themselves.
   2. `.system` can fetch and patch metadata for any user, except itself.


# ACLs and Advanced Permission Model

ACLs (Access Control Lists) allow you to fine tune the permission model to suit the use cases of your applications. ACLs are also resolved and applied on message delivery so ACLs can also be used to manage message routing. The core concepts of the ACL subsystem are:

1. Privilege : A privilege denotes the ability of an actor to perform a certain operation. An example would be the `read` privilege on messages that allow anyone holding that privilege to read the message.
2. Accessor : Any actor to which privileges can be granted. Within the mitter.io context, all accessors are users.
3. Accessor selector : A selector denotes a class of accessor. An example of a selector is `participant(channel-id:status)` which denotes the set of all users that are participants in `channel-id` holding `status` as their Participation status.
4. ACL : An ACL is a list of privilege-selector pairs.
5. ACL Entity : Any entity to which ACLs can be applied. `Message`, `Channel`, `User`, `TimelineEvent` are all ACL entities.

### Example

To illustrate how ACLs function within mitter.io, let us take an example of a `Message` with id `msg` sent to a channel with id `chnl` by the user `axe`. A `Message` is an ACL entity to which the following privileges can be applied:

1. ReadMessagePrivilege() `read_message`
2. DeleteMessagePrivilege() `delete_message`

From the section *Basic permissions*, we know that:

1. Any participant of a channel can read a message from the channel.
2. Any authenticated user can delete a message that they sent to a channel.
3. Any authenticated user can send a message that they sent to a channel.

Both \[2] and \[3] apply even if the user is no longer a participant on the channel. To implement this, the given message has the following ACLs applied to it:

```
Message(id=msg)
    acls = [+read:participant(chnl), +delete:user(axe), +read:user(axe)]
```

When we send a message these ACLs are applied by default and this is how we implement the basic permission model that is present even for applications that don't use ACLs.

If `axe` now wanted to send a message to the channel that only `rylai` could read, he would send the message with his custom ACLs:

```
Message(id=msg)
    acls = [+read:user(rylai), +read:user(axe), +delete:user(axe)]
```

Whenever you supply a list of ACLs, the default ACLs are no longer applied. So any behavior you wish to be retained must be reflected by the ACLs you set. When this message is sent, mitter.io will even compute the delivery targets based on these ACLs and the message will be sent only to `rylai`s device and only she will see it when she fetches messages via HTTP.

Now take the example where `axe` wants to send a message to everyone in the channel except `rylai` because `rylai` has been acting way too cool recently. To do so, he can set the ACLs as:

```
Message(id=msg)
    acls = [-read:user(rylai), +read:participant(chnl), +read:user(axe), +delete(axe)]
```

When this message is received, mitter.io will not send messages to any of rylais delivery endpoints nor will she be able to fetch it via HTTP calls.

### ACL Computation

An ACL is applied over two lists, the `p-list` an the `m-list`. The `p-list` consists of all ACLs that have a `+` permission and similarly the `m-list` consists of all ACLs that have a `-` permission. To check whether a given user has access to a privilege on an entity, we check if:

1. At least one of the accessors that resolve to the user is in the p-list.
2. None of the accessors that resolve to the user in the m-list.

Both these conditions need to be true for the ACL to pass as positive and the privilege to be considered granted. This model has certain limitations, but in the current phase of development, this is the model that allows us to maintain performance while still providing feature rich ACLs.

The limitations manifest in certain ways, for instance:

1. `+read:user(axe)`, `-read:participant(chnl)` would still result in the `read` privilege not being granted to `axe`
2. A empty `p-list` results in no privileges being granted to any user. This however is not permitted in the system due to default ACLs and sticky ACLs kicking in, which is covered further below.

### Sticky ACLs and Default ACLs

As we saw in the previous section, `.system` has complete access of all the data of the application. Moreoever, certain privileges, like `-revoke_tokens_for_self` is a privilege that a user is always granted and this cannot be overriden. To keep these constants in place, there are two ACL lists that are maintainted for each entity:

1. Default ACLs : These are the ACLs that are applied to the entity if no ACLs were found on the entity.
2. Sticky ACLs : These ACLs are always implicitly present on the entity and cannot be overriden.

For instance, one of the sticky ACLs on channel is `+add_participant:user(.system)` which means that you can always add a participant to any channel using an application key/secret.

### Available Accessor Selectors

The following selectors are available:

1. `user(user-id)` Represents a single user having the provided id.
2. `participant(channel-id:status)` Represents the group of users which are a participant in the channel with the given channel id, with a particular participation status.
3. `any_user()` Represents any authenticated user.

Do note that the ACLs `user(.system)` and `user(.anonymous)` are forbidden.

> **COMING SOON** Soon, users can be assigned an `aclTag` and all users belonging to an acl tag can be selected using `acltag(tag-name)`.

### ACL Entitys and Privilege List

In the following section we will be listing all the ACL entities and the permissions that are available on them. Do note that mitter.io currently only supports setting custom ACLs on messages with support for `Channel` and `User` coming up in the next release. We will also be releasing partial access to ACLs on `Application` via certain configuration options in the subscriber panel.

#### Channel

A channel can have the following privileges on it:

1. `join_channel` Join the channel (add oneself)
2. `add_participant_to_channel` Add any other user as a participant to the channel
3. `list_participants` List the participants of the channel
4. `remove_participant` Remove any other user as a participant to the channel
5. `remove_self` Remove oneself from the channel
6. `delete_messages_from_channel` Delete messages from the channel
7. `read_from_channel` Read the channel object and send messages to it
8. `send_to_channel` Send messages to the channel with themselves as the sender
9. `send_as_other_to_channel` Send messages to the channel with anyone else as the sender

For instance, if you wanted to introduce a role called `admin` for the channel, who alone can add participants to the channel, but anyone could remove themsleves, you would assign the following privileges to channel:

```
Channel(chnl) acls = [
    +add_participant_to_channel:user(admin), +remove_participant:user(admin),
    -join_channel:any_user(), +remove_self:any_user(),
    +read_from_channel:participant(chnl), +send_to_channel:participant(chnl)
]
```

> **NOTE** The actual accessor selector for a participation selector is `participant(chnl:status)`, but for the sake of brevity we have omitted it. Similarly, the actual privileges on messages are `read_message` and `delete_message`.

The default ACLs for a channel are:

1. `+read_from_channel`**:**`participant(channel-id:Active)`
2. `+send_to_channel`**:**`participant(channel-id:Active)`
3. `+list_participants`**:**`participant(channel-id:Active)`
4. `+join_channel`**:**`any_user()`
5. `+remove_self`**:**`any_user()`

The sticky ACLs for a channel are:

1. `+read_from_channel`**:**`user(.system)`
2. `+send_as_other_to_channel`**:**`user(.system)`
3. `+remove_participant`**:**`user(.system)`
4. `+add_participant`**:**`user(.system)`
5. `+list_participants`**:**`user(.system)`
6. `-join_channel`**:**`user(.system)`

#### Message

A Message can have the following privileges assinged to it:

1. `read_message` Read the message
2. `delete_message` Delete the message

These ACLs can be used to selectively send messages to a message from a user that only certain people in the channel can receive. Do note that if a user is assigned an ACL for read for a message sent to a channel, but the user is not a participant in the channel, the user will not be able to read the message unless the user also has the `read_from_channel` privilege on the channel the message was sent to.

The default ACLs for a message sent to a channel with id `channel-id` by `sender-id` are:

1. `read_message`**:**`participant(channel-id:Active)`
2. `read_message`**:**`user(sender-id)`
3. `delete_message`**:**`user(sender-id)`

The sticky ACLs for a message sent to a channel with id `channel-id` by `sender-id` are:

1. `read_message`**:**`user(.system)`
2. `delete_message`**:**`user(.system)`

#### Application

Application ACLs are different only in the sense that they define certain privileges for users at a global level. They contain privileges on the creation of users and channels and control default aspects of the application in general.

> **NOTE** Certain privileges on the application will soon be made available on the user object itself, as the user will be defined as an ACL entity. At that point in time, these privileges will no longer be available on an application.

Access to these ACLs will be made available, but via a controlled manner via the application panel. There is no plan to ever support modifying the direct ACL list of the application.

The following privileges can be assigned to an application:

1. `create_channel` - Create a channel
2. `create_message` - Create a message
3. `create_user` - Create a user
4. `list_channels` - List all the channels in the application
5. `list_user_data` - List the user data for a user (other than oneself)
6. `write_user_credentials` - Revoke/Issue tokens for a user (other than oneself)

The second permissions is a little special as it only applies for `OutOfBand` messages and is currently not applied or in use anywhere. The privilege `list_channels` is required to list the channels in an application, but only those channels will be returned for which the user has a `read_from_channel` privilege. The subtle difference that manifests is that if the user does not have that privilege on any channel, this call will return an empty list, but if the user does not have `list_channels` privilege, it will return a `403 Forbidden` (with a `missing_privileges` error code). A user can not have the `list_channels` privilege but still send and receive messages to a channel for which they have the appropriate `send_to` and `read_from` privileges.

The default ACLs for an application are:

1. `create_channel`**:**`any_user()`
2. `list_participants`**:**`any_user()`

The sticky ACLs for an application are:

1. `create_channel`**:**`user(.system)`
2. `create_message`**:**`user(.system)`
3. `create_user`**:**`user(.system)`
4. `list_channels`**:**`user(.system)`
5. `list_user_data`**:**`user(.system)`
6. `write_user_credentials`**:**`user(.system)`

Do note that despite not having explicit ACLs, the following behavior is ALWAYS true and cannot be overridden in any way:

1. A user can always revoke their own tokens.
2. A user can always request for additional tokens.
3. `.system` cannot list user data for itself or issue user tokens for itself.

### Fine-grained Control Over ACLs

Introduced in v0.4, you can now not only specify ACLs for an entity up-front, but also modify later according to your specific use-cases.

To modify an ACL, there are two modes that are supported:

1. PatchType.Set
2. PatchType.Diff

A `PatchType.Set` operation overwrites all ACLs for a given entity as is specified in the payload. A `PatchType.Diff` operation on the other hand specifies the specific ACLs that are to be added/removed for an entity.

Let's take an example of a channel that we just created:

```
POST /v1/channels
{
    "channelId": "my-channel",
    ... common fields ..,
    "appliedAcls": [
        "-join:any_user()"
    ]
}
```

What this does is, it creates the channels, adds any participants that were specified in the request, but will not allow any new user to join as `-join:any_user()` revokes the `join` privilege from every user. Do note that a user can still add some other participant to the channel, since the `add_participant` privilege is still available to them. Ideally, we should have revoked both the permissions, but for the sake of brevity we will continue with this example with the single privilege.

Now, after some time, we want to allow users to join the channel. There are two ways of doing this:

1. Either we remove the `-join:any_user()` rule from the channel.
2. We reset the ACLs to the default state i.e. empty ACLs where the default ACLs for channel will kick-in.

To do this using the first strategy, we will use a `Diff` type of ACL modification:

```
PATCH /v1/channels/my-channel/acls

{
    "patchType": "Diff",
    "addAcls": [],
    "removeAcls": [
        "-join:any_user()"
    ]
}
```

This will return a response which will contain two channel objects, one before the ACL modification and one after it:

```
{
    "oldEntity": { "channelId": .... },
    "newEntity": { "channelId": .... }
}
```

We can also use a `Set` type operation and instead do:

```
PATCH /v1/channels/my-channel/acls

{
    "patchType": "Set",
    "setAcls": {}
}
```

A Set ACL operation performs a complete set of all ACL records for a given entity. So if we specify an empty object, it will set the entities ACLs to an empty ACL list, which means that for any operations henceforth, it will result in the default ACLs for that entity to kick in.

ACLs for entities can be patched except for Applications. For data-integrity purposes, we currently do not allow any ACLs on `Application` to be modified (or specified up-front for that matter).


# Metadata

The Metadata reference

## Introduction

mitter.io allows attaching metadata to certain entities. Currently, **Channels** and **Users** are supported.

The examples below demonstrate operations with Channels, but the same can be performed on Users as well (by replacing "channels" in the URLs with "users", and providing the `userId` instead of the `channelId`)

Here is a sample API call to `POST` metadata:

```
POST /v1/channels/my-awesome-channel/metadata
{
    "type": "awesome-channel",
    "maxParticipants": 256,
    "meetingLocation": {
        "latitude": 12,
        "longitude": 12
    }
}
```

You can query Channels by metadata like so:

```
GET /v1/channels?metadata={type:"awesome-channel"}
```

NOTE: The URL in the example is not encoded for readability's sake, but it must be when making actual calls.

## Behaviour

Metadata can be any valid JSON and adheres to the following rules:

1. The top level values of the JSON (in the above case, "awesome-channel", 256, and the location JSON) can only be string, numeric or JSON. It cannot be boolean.
2. Only top level keys can be used to query objects. For instance, in the above example, you can query Channels by `type, maxParticipants` and `meetingLocation,` but **NOT** by `meetingLocation.latitude.`
3. If you `POST` metadata with a key that already exists, it will be overridden. Keys with JSON values will not be merged.
4. When querying entities, you can specify multiple JSON, and all entities that match **ALL** queries will be fetched, i.e., if you provide multiple queries, the system will do an AND operation. For instance, if you execute `GET /v1/channels?metadata={type: "awesome-channel", "age": 2}`, then you will NOT get the above channel. Similarly, if you have two channels:

```
{
    "channelId": "channel-one",
    ...
    "metadata": {
        "type": "business",
        "area": "IN"
    }
}

{
    "channelId": "channel-two",
    ...
    "metadata": {
        "type": "business",
        "area": "EU"
    }
}
```

and you try to execute `GET /v1/channels?metadata={type: "business"}`, then the response will be `channel-one`


# Android

Mitter provides an Android SDK which you can use to quickly integrate Mitter to your apps. Learn more about how to setup and use the SDK here.


# Getting Started

Welcome to the reference section for Mitter’s Android SDK.  In this section, you'll mostly find a detailed explanation of how the SDK works and how to harness it best.

### What can you do with this SDK?

Mitter Android SDK gives you access to the entire Mitter.io platform from a **User**'s perspective. The SDK does all the hard work behind the scenes while you focus on adding your business logic and getting your app ready in record time.

Currently, the Android SDK supports the following actions:

* Sign-in as a user using either **Google Sign-In** or **Auth Token**
* Create/remove channels
* Send/receive messages & timeline events
* Auto-updating your own presence and receiving other users’ presence
* Updating your user profile

### Setting up the SDK

All right, let’s get started by grabbing the SDK from **jCenter**.

#### Adding the SDK to your project

Adding the SDK to your project is pretty straightforward. Just open up your `build.gradle` file in the **app** module and paste this line within the `dependencies` block.

```groovy
implementation 'io.mitter.android:core:0.1.7'
```

Once you’re done and just a Gradle sync and sit back while Gradle completes syncing your project.

#### Let’s do some basic configuration

Now that you’ve got the SDK added, the next step is to add some initial configuration for the SDK to work.

The central point of access to everything that the API has to offer is through the `Mitter` object. To start working, you need to configure this object with your application details that you can access from Mitter Dashboard.

A very good place to initialise and configure this object is within the `onCreate()` of your app’s `Application` class.

Let’s get started by defining the `Mitter` object as a global variable in your `Application` class.

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
lateinit var mitter: Mitter
```

{% endtab %}

{% tab title="Java" %}

```java
private Mitter mitter;
```

{% endtab %}
{% endtabs %}

Now, within `onCreate()` you need to configure this object to connect with your application on the Mitter.io platform. Additionally, you can put down some extra configuration as to how the SDK should behave locally.

The first thing we need is a `UserAuth` object which will specify the user you want the SDK to log in as. The `UserAuth` object takes a User ID and a User Auth Token as its parameters.

You need to get your user’s credentials from a backend server that connects to Mitter.io to manage your application. Alternatively, you can use a federated authentication system such as Google Sign-in to get the job done, which will be discussed in a later section. For brevity, let’s continue with the former approach:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
val userAuth = UserAuth(
    userId = "089771b6-6002-43db-bdc5-81e6ef7b6ef9",
    userAuthToken = "eyJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJtaXR0ZXItaW8iLCJ1c2VyVG9rZW5JZCI6Imc3QXYzYjR4VWJleGNsTTIiLCJ1c2VydG9rZW4iOiJyMHVsa2Jmc2ZtaWY5dTVscXNwaDVobzFpNCJ9.jnvR74f_GUBiH_9Z5FEWK7fLEnerDU_gCdPZeykKrJk5X4pOlhogVDG5PdeCyraz9FXV-G1sojlovpKuti7GTA"
)
```

{% endtab %}

{% tab title="Java" %}

```java
UserAuth userAuth = new UserAuth(
    "089771b6-6002-43db-bdc5-81e6ef7b6ef9",
    "eyJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJtaXR0ZXItaW8iLCJ1c2VyVG9rZW5JZCI6Imc3QXYzYjR4VWJleGNsTTIiLCJ1c2VydG9rZW4iOiJyMHVsa2Jmc2ZtaWY5dTVscXNwaDVobzFpNCJ9.jnvR74f_GUBiH_9Z5FEWK7fLEnerDU_gCdPZeykKrJk5X4pOlhogVDG5PdeCyraz9FXV-G1sojlovpKuti7GTA"
);
```

{% endtab %}
{% endtabs %}

Here, we configure the `Mitter` object with the User ID of `089771b6-6002-43db-bdc5-81e6ef7b6ef9` and its respective auth token which is nothing but a JWT.

Once that’s in place, you can do some application & local SDK configuration with the help of the `MitterConfig` object.

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
val mitterConfig = MitterConfig(
    applicationId = "ee421c5f-7a93-4b89-bf2a-4823c6e4fe42",
    loggingLevel = LoggingLevel.FULL
)
```

{% endtab %}

{% tab title="Java" %}

```java
MitterConfig mitterConfig = new MitterConfig(
    "ee421c5f-7a93-4b89-bf2a-4823c6e4fe42",
    LoggingLevel.FULL,
    null
);
```

{% endtab %}
{% endtabs %}

Here, you just need to punch in your application ID which can be retrieved from the Mitter Dashboard. Additionally, if you want to configure how the SDK prints out logs you can do that by specifying one of either:

* `LoggingLevel.NONE` - Prints nothing
* `LoggingLevel.BASIC` - Prints only basic operation success/error messages
* `LoggingLevel.FULL` - Prints out everything including object data. Not a good idea to use this level for production usage

#### Using the SDK with containerised Mitter.io

If you're using the Mitter.io docker container, then you need to *override* the default API endpoint in the SDK, as follows:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
val mitterConfig = MitterConfig(
    applicationId = "ee421c5f-7a93-4b89-bf2a-4823c6e4fe42",
    loggingLevel = LoggingLevel.FULL,
    apiEndpoint = MitterApiEndpoint(
        "http://localhost:11901",
        "http://localhost:11901"
    )
)
```

{% endtab %}

{% tab title="Java" %}

```java
MitterConfig mitterConfig = new MitterConfig(
    "ee421c5f-7a93-4b89-bf2a-4823c6e4fe42",
    LoggingLevel.FULL,
    new MitterApiEndpoint(
        "http://localhost:11901",
        "http://localhost:11901"
    )
);
```

{% endtab %}
{% endtabs %}

Once you have got the `UserAuth` and `MitterConfig` in place, constructing the `Mitter` object is simple. You can do as follows:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
mitter = Mitter(this, mitterConfig, userAuth)
```

{% endtab %}

{% tab title="Java" %}

```java
mitter = new Mitter(this, mitterConfig, userAuth);
```

{% endtab %}
{% endtabs %}

To sum everything up, your `Application` should look something like this:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
class MessageApp : Application() {
    lateinit var mitter: Mitter

    override fun onCreate() {
        super.onCreate()

        val userAuth = UserAuth(
            userId = "089771b6-6002-43db-bdc5-81e6ef7b6ef9",
            userAuthToken = "eyJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJtaXR0ZXItaW8iLCJ1c2VyVG9rZW5JZCI6Imc3QXYzYjR4VWJleGNsTTIiLCJ1c2VydG9rZW4iOiJyMHVsa2Jmc2ZtaWY5dTVscXNwaDVobzFpNCJ9.jnvR74f_GUBiH_9Z5FEWK7fLEnerDU_gCdPZeykKrJk5X4pOlhogVDG5PdeCyraz9FXV-G1sojlovpKuti7GTA"
        )
        val mitterConfig = MitterConfig(
            applicationId = "ee421c5f-7a93-4b89-bf2a-4823c6e4fe42",
            loggingLevel = LoggingLevel.FULL
        )
        mitter = Mitter(this, mitterConfig, userAuth)
}
```

{% endtab %}

{% tab title="Java" %}

```java
class MessageApp extends Application {
    private Mitter mitter;
​
    @Override
    public void onCreate() {
        super.onCreate();
        
        UserAuth userAuth = new UserAuth(
            "089771b6-6002-43db-bdc5-81e6ef7b6ef9",
            "eyJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJtaXR0ZXItaW8iLCJ1c2VyVG9rZW5JZCI6Imc3QXYzYjR4VWJleGNsTTIiLCJ1c2VydG9rZW4iOiJyMHVsa2Jmc2ZtaWY5dTVscXNwaDVobzFpNCJ9.jnvR74f_GUBiH_9Z5FEWK7fLEnerDU_gCdPZeykKrJk5X4pOlhogVDG5PdeCyraz9FXV-G1sojlovpKuti7GTA"
        );
        MitterConfig mitterConfig = new MitterConfig(
            "ee421c5f-7a93-4b89-bf2a-4823c6e4fe42",
            LoggingLevel.FULL,
            null
        );
        mitter = new Mitter(this, mitterConfig, userAuth);
    }
}
```

{% endtab %}
{% endtabs %}

That’s it, you’re all set to use the SDK to connect with the Mitter.io platform.

#### Getting access to Mitter.io API objects

The Mitter Android SDK segregates all the APIs into three broad categories:

* Users
* Channels
* Messages

As a result, you get access to APIs that fall within these buckets through their specific objects.

This is done especially to have a structured access to all the APIs without any additional overhead.

You can easily create an object for each of these categories through the `Mitter` object that you created in the previous step.

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
var users: Mitter.Users = mitter.Users()
var channels: Mitter.Channels = mitter.Channels()
var messaging: Mitter.Messaging = mitter.Messaging()
```

{% endtab %}

{% tab title="Java" %}

```java
Mitter.Users users = mitter.new Users();
Mitter.Channels channels = mitter.new Channels();
Mitter.Messaging messaging = mitter.new Messaging();
```

{% endtab %}
{% endtabs %}

### Creating your first channel

Before you start sending out messages, you need to create a channel with some participants in it. You’re free to define your channel however you want. Nevertheless, Mitter.io provides a list of default rulesets for channels that you can use to effortlessly create a channel.

#### What are channels?

Channels are nothing but containers for your messages. Think of it as a logical grouping for your messages. It defines who sees your messages that you send out in your application.

You can learn more about [channels](https://docs.mitter.io/platform-reference-1/channels) in our reference section. For now, let’s just focus on creating some basic channels.

#### Creating a basic channel

Although the SDK has convenience methods for creating channels of all the kinds mentioned in the reference, for simplicity, we’ll focus on creating a one-to-one communication channel (also termed as a `DirectChannel`) here.

A channel needs participants to whom Mitter could deliver messages. Thankfully, creating participants is easy. Let’s define two participants for our channel:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
val john = Participant("089771b6-6002-43db-bdc5-81e6ef7b6ef9")
val lucy = Participant("473d4f4f-0dc6-480b-ae94-7042b37f09e8")
```

{% endtab %}

{% tab title="Java" %}

```java
Participant john = new Participant(
    "089771b6-6002-43db-bdc5-81e6ef7b6ef9",
    ParticipationStatus.Active
);
Participant lucy = new Participant(
    "473d4f4f-0dc6-480b-ae94-7042b37f09e8",
    ParticipationStatus.Active
);
```

{% endtab %}
{% endtabs %}

Now that we have the participants in place, we can hook them up with a new `DirectMessageChannel`.

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
channels.createDirectMessageChannel(
    listOf(john, lucy),
    object : Mitter.OnValueAvailableCallback<Identifier> {
        override fun onValueAvailable(value: Identifier) {
            Log.d("Mitter", "Created Channel ID: ${value.identifier}")
        }

        override fun onError(error: ApiError) {
            Log.d("Mitter", "Channel Creation - ApiError: $error")
        }
    }
)
```

{% endtab %}

{% tab title="Java" %}

```java
channels.createDirectMessageChannel(
    Arrays.asList(john, lucy),
    new Mitter.OnValueAvailableCallback<Identifier>() {
        @Override
        public void onValueAvailable(Identifier identifier) {
            Log.d("Mitter", "Created Channel ID: " + identifier.getIdentifier());
        }

        @Override
        public void onError(ApiError apiError) {
            Log.d("Mitter", "Channel Creation - ApiError: " + apiError);
        }
    }
);
```

{% endtab %}
{% endtabs %}

Once a channel has been created, you get a callback with the newly created channel’s identifier. You need to use this ID to send messages or get all messages on the channel.

#### Creating a channel with advanced properties

As already said, although the SDK provides you convenience methods to easily create a channel with a pre-defined ruleset, it doesn’t lock you down from customising your channel.

You can easily create a totally customised channel by using the `Channel` object and the `createChannel()` method.

A typical `Channel` model looks like this:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
val channel = Channel(
    channelId = "f8cc57d8-af38-4313-85e8-cbe62a2ebf23",
    defaultRuleSet = "io.mitter.ruleset.chats.DirectMessage",
    participation = listOf(
        ChannelParticipation(
            participant = User().setUserId("0b604184-7abd-453c-b258-7e4425b31e7f"),
            participationStatus = ParticipationStatus.Active
        ),
        ChannelParticipation(
            participant = User().setUserId("ea4d2fe1-0d8d-42f0-afd3-8d8067ccbea1"),
            participationStatus = ParticipationStatus.Active
        )
    ),
    systemChannel = false
)
```

{% endtab %}

{% tab title="Java" %}

```java
Channel channel = new Channel(
    "f8cc57d8-af38-4313-85e8-cbe62a2ebf23",
    "",
    "io.mitter.ruleset.chats.DirectMessage",
    Arrays.asList(
        new ChannelParticipation(
            new User().setUserId("0b604184-7abd-453c-b258-7e4425b31e7f"),
            ParticipationStatus.Active,
            null,
            null,
            null
        ),
        new ChannelParticipation(
            new User().setUserId("ea4d2fe1-0d8d-42f0-afd3-8d8067ccbea1"),
            ParticipationStatus.Active,
            null,
            null,
            null
        )
    ),
    false,
    new EntityMetadata(),
    new EntityProfile(IdUtils.of(channelId, Channel.class), new ArrayList<Attribute>()),
    new ArrayList<TimelineEvent>(),
    new AppliedAclList(
        new ArrayList<AppliedAcl>(),
        new ArrayList<AppliedAcl>()
    ),
    null
);
```

{% endtab %}
{% endtabs %}

After you’ve constructed your `Channel` object, you can now swiftly create a new channel by calling the `createChannel` method:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
channels.createChannel(
    channel,
    object : Mitter.OnValueAvailableCallback<Identifier> {
        override fun onValueAvailable(value: Identifier) {
            Log.d("MAC", "Created Channel ID: ${value.identifier}")
        }

        override fun onError(apiError: ApiError) {
            Log.d("MAC", "Channel Creation - ApiError: $apiError")
        }
    }
)
```

{% endtab %}

{% tab title="Java" %}

```java
channels.createChannel(
    channel,
    new Mitter.OnValueAvailableCallback<Identifier>() {
        @Override
        public void onValueAvailable(Identifier identifier) {
            Log.d("MAC", "Created Channel ID: " + identifier.getIdentifier());
        }

        @Override
        public void onError(ApiError apiError) {
            Log.d("MAC", "Channel Creation - ApiError: " + apiError);
        }
    }
);
```

{% endtab %}
{% endtabs %}

It’s very similar to the `createDirectMessageChannel()` method, except you have full control over the channel parameters, including the channel ID.

**When to use what?**

Just remember this principle when creating channels with the SDK:

* **Quick and easy** - Use `createDirectMessageChannel()` or `createGroupMessageChannel()`
* **Full customisation** - Use `createChannel()`

### Sending your first message

Like all other operations, sending messages is as easy as it gets. The SDK provides various methods of sending messages of [pre-defined types](https://docs.mitter.io/platform-reference-1/messages#payload-types) which you can learn more about in the reference section.

Nonetheless, you still get to create fully customised messages with custom payloads with the `sendMessage()` method.

#### What are messages?

Messages are the smallest unit of information in the Mitter.io platform. Don’t think of messages as just text or image messages, they’re more than that.

You can translate almost any real-world action into an act of sending a particular type of message. You can learn more about [messages](https://docs.mitter.io/platform-reference-1/messages) in the reference section.

#### Sending your first message

Let’s say **John** wants to send a text message to **Lucy** on the channel that you just created. To achieve this, a simple method call with the channel ID and the message will suffice.&#x20;

Let’s see how:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
messaging.sendTextMessage(
    channelId,
    "Hi, Lucy!",
    object : Mitter.OnValueUpdatedCallback {
        override fun onSuccess() {
            Toast.makeText(this@MainActivity, "Yay! Message sent!", Toast.LENGTH_LONG).show()
        }

        override fun onError(error: ApiError) {}
    }
)
```

{% endtab %}

{% tab title="Java" %}

```java
messaging.sendTextMessage(
    channelId,
    "Hi, Lucy!",
    new AppliedAclList(
        new ArrayList<AppliedAcl>(),
        new ArrayList<AppliedAcl>()
    ),
    new Mitter.OnValueUpdatedCallback() {
        @Override
        public void onSuccess() {
            Toast.makeText(getApplicationContext(), "Yay! Message sent!", Toast.LENGTH_LONG).show();
        }

        @Override
        public void onError(ApiError apiError) {}
    }
);
```

{% endtab %}
{% endtabs %}

That’s all you need to do get your plain text message delivered in a channel.

#### Fetching messages in a channel

Currently, the SDK provides two ways to receive messages in a channel:

* The **Pull** approach - Calling `getMessagesInChannel()`
* The **Push** approach - Listening to push messages through FCM

While the latter is more intuitive and probably the one you’ll end up using in most cases, for now, we’ll focus on the former and keep the Push approach for the next section.

Let’s say you want to fetch all the messages in a channel between John and Lucy. You can achieve that with the following piece of code:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
messaging.getMessagesInChannel(
    channelId = "f8cc57d8-af38-4313-85e8-cbe62a2ebf23",
    onValueAvailableCallback = object : Mitter.OnValueAvailableCallback<List<Message>> {
        override fun onValueAvailable(value: List<Message>) {
            //We've got the messages
            //Populate the messages in a list
        }

        override fun onError(apiError: ApiError) {}
    }
)
```

{% endtab %}

{% tab title="Java" %}

```java
messaging.getMessagesInChannel(
    "f8cc57d8-af38-4313-85e8-cbe62a2ebf23",
    new FetchMessageConfig(),
    new Mitter.OnValueAvailableCallback<List<Message>>() {
        @Override
        public void onValueAvailable(List<Message> messageList) {
            //We've got the messages
            // Populate the messages in a list
        }

        @Override
        public void onError(ApiError apiError) {}
    }
);
```

{% endtab %}
{% endtabs %}

By default, this call fetches the last **10** messages in the channel. You can raise this limit to a maximum of **50** messages by passing a `FetchMessageConfig` object to the method.

The code for the same would be:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
val fetchMessageConfig = FetchMessageConfig(25)
messaging.getMessagesInChannel(
    channelId = "f8cc57d8-af38-4313-85e8-cbe62a2ebf23",
    fetchMessageConfig = fetchMessageConfig,
    onValueAvailableCallback = object : Mitter.OnValueAvailableCallback<List<Message>> {
        override fun onValueAvailable(value: List<Message>) {
            //We've got the messages
            //Populate the messages in a list
        }

        override fun onError(apiError: ApiError) {}
    }
)
```

{% endtab %}

{% tab title="Java" %}

```java
FetchMessageConfig fetchMessageConfig = new FetchMessageConfig(25, null, null);
messaging.getMessagesInChannel(
    "f8cc57d8-af38-4313-85e8-cbe62a2ebf23",
    fetchMessageConfig,
    new Mitter.OnValueAvailableCallback<List<Message>>() {
        @Override
        public void onValueAvailable(List<Message> messageList) {
            //We've got the messages
            // Populate the messages in a list
        }

        @Override
        public void onError(ApiError apiError) {}
    }
);
```

{% endtab %}
{% endtabs %}

When you need to cross the max limit of **25** you can opt for fetching messages in a paginated way, which will be discussed in a later section of this documentation.

#### Customising your message

As you’ve already done with your channel, you can also fully customise your messages. The SDK provides a `sendMessage()` method which accepts a `Message` object totally constructed by you.

Let’s see how it works. First, we need to create a `TimelineEvent`:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
val sender = User().setUserId("0b604184-7abd-453c-b258-7e4425b31e7f")

val sentTimelineEvent = TimelineEvent(
    type = StandardTimelineEventTypeNames.Messages.SentTime,
    eventTimeMs = System.currentTimeMillis(),
    subject = sender
)
```

{% endtab %}

{% tab title="Java" %}

```java
Identifiable<User> sender = new User().setUserId("0b604184-7abd-453c-b258-7e4425b31e7f");

TimelineEvent sentTimelineEvent = new TimelineEvent(
    UUID.randomUUID().toString(),
    "",
    StandardTimelineEventTypeNames.Messages.SentTime,
    System.currentTimeMillis(),
    sender,
    null
);
```

{% endtab %}
{% endtabs %}

A `TimelineEvent` is like a categorised timestamp on your messages. Here, we’re specifying a `SentTime` event which is mandatory when creating a `Message` object. You can learn more about **Timeline Events** [over here](https://docs.mitter.io/platform-reference-1/messages#timeline-events).

Now that you have your `TimelineEvent` set, the only thing’s left is to create your customised `Message` object. Here’s how you can do that:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
val message = Message(
    messageId = "ad75460e-97c3-4785-9db7-20bcdaee93d9",
    messageType = StandardMessageType.Standard,
    payloadType = "com.acme.messages.MyCustomMessage",
    senderId = sender,
    textPayload = "Check this out!",
    timelineEvents = listOf(sentTimelineEvent),
    messageData = listOf(
        MessageDatum(
            dataType = "com.acme.data.MyCustomMessageData",
            data = jacksonObjectMapper.valueToTree(messageData)
        )
    )
)
```

{% endtab %}

{% tab title="Java" %}

```java
Message message = new Message(
    "ad75460e-97c3-4785-9db7-20bcdaee93d9",
    "",
    StandardMessageType.Standard,
    "com.acme.messages.MyCustomMessage",
    sender,
    "Check this out!",
    Arrays.asList(
        new MessageDatum(
            "com.acme.data.MyCustomMessageData",
            jacksonObjectMapper.valueToTree(messageData)
        )
    ),
    Arrays.asList(sentTimelineEvent),
    new AppliedAclList(
        new ArrayList<AppliedAcl>(),
        new ArrayList<AppliedAcl>()
    ),
    new EntityMetadata(),
    null
);
```

{% endtab %}
{% endtabs %}

The main part here is that you can supply a list of custom payloads in the `messageData` field. Use this field to define any custom buttons or additional styling information that you want to show up in the message.

For more clarity, please refer to the detailed guide on messages over [here](/platform-reference-1/messages).

All right, now that your `Message` object is set, it’s time that you send it. The sending part is really easy:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
messaging.sendMessage(
    channelId = channelId,
    message = message,
    onValueUpdatedCallback = object : Mitter.OnValueUpdatedCallback {
        override fun onSuccess() {
            Toast.makeText(this@MainActivity, "Yay! Message sent!", Toast.LENGTH_LONG).show()
        }

        override fun onError(apiError: ApiError) {}
    }
)
```

{% endtab %}

{% tab title="Java" %}

```java
messaging.sendMessage(
    channelId,
    message,
    new Mitter.OnValueUpdatedCallback() {
        @Override
        public void onSuccess() {
            Toast.makeText(getApplicationContext(), "Yay! Message sent!", Toast.LENGTH_LONG).show();
        }

        @Override
        public void onError(ApiError apiError) {}
    }
);
```

{% endtab %}
{% endtabs %}

And there you have it, your very own custom message with a custom payload.

#### Attaching images to your messages

Mitter.io lets you create a pre-defined type of message called `ImageMessage`. The main advantage of choosing this type is that Mitter.io automatically generates thumbnails for the attached image without any effort from your part. For more details, [see the reference](https://docs.mitter.io/platform-reference-1/messages#image-message).

Let’s see how you can create an `ImageMessage` with the SDK. It’s pretty similar to sending a text message, just with an added image file parameter:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
messaging.sendImageMessage(
    channelId = channelId,
    caption = "The London Eye",
    file = File("/storage/emulated/0/Download/london-eye.jpg"),
    onValueUpdatedCallback = object : Mitter.OnValueUpdatedCallback {
        override fun onSuccess() {
            Log.d("Mitter", "Image message sent!")
        }

        override fun onError(error: ApiError) {
            Log.d("Mitter", "Image Message Error: ${error.message}")
        }
    }
)
```

{% endtab %}

{% tab title="Java" %}

```java
messaging.sendImageMessage(
    channelId,
    "The London Eye",
    new File("/storage/emulated/0/Download/london-eye.jpg"),
    new AppliedAclList(
        new ArrayList<AppliedAcl>(),
        new ArrayList<AppliedAcl>()
    ),
    new Mitter.OnValueUpdatedCallback() {
        @Override
        public void onSuccess() {
            Log.d("Mitter", "Image message sent!");
        }

        @Override
        public void onError(ApiError apiError) {
            Log.d("Mitter", "Image Message Error: " + apiError.getMessage());
        }
    }
);
```

{% endtab %}
{% endtabs %}

Think of an `ImageMessage` as a regular image message that you see in every other messaging apps. You have an image with some text at the bottom, which is usually the caption.

You can do the same here. Just send a caption or whatever text you would prefer and a `File` object pointing to your image.

### Wrap up

In this section, you’ve learnt how to use the Mitter Android SDK to create various types of channels as well as sending different messages into those channels.

Now, we’ll be focusing on how you can set up an **FCM** push message receiver to receive messages in *real-time* in your app.


# Set up FCM

In the last section, Getting Started, you learnt how to successfully setup the Mitter Android SDK and was able to send a message across a newly created channel. It’s time to step up.

### Enabling push messaging

In this section, you’ll see how you can get FCM to work with the SDK to receive new messages in real-time via the **Push** approach we talked about in [the last section](https://docs.mitter.io/sdks/android/getting-started).

#### Integrating FCM with your app

First of all, you need to setup FCM in your Android project. The steps for this setup is beyond the scope of this documentation and since Google has done a pretty good job explaining the same, why don’t you go [check out their docs](https://firebase.google.com/docs/cloud-messaging/android/client) if you haven’t already added FCM to your project.

After you’re through with that, the only thing’s that left is to feed your FCM server key in your Mitter.io application inside the Dashboard.

You can do that by following these steps:

* Open up **Mitter Dashboard** and select your application from the list
* Go to the **Properties** tab
* Click on **New Property** -> **Google** -> **FCM** -> **FCM Property**
* You’ll get a modal where you need to fill out your app’s instance ID which can be easily retrieved from the [Google Cloud Console](https://cloud.google.com/resource-manager/docs/creating-managing-projects)
* Also, you need to feed in your **FCM server key**, which can be accessed from your FCM admin panel
* After you’re done feeding these data, click on **New FCM Configuration Property**

That’s it, Mitter.io can now get your messages delivered to your users in real-time.

#### Registering a delivery endpoint

Think of a [Delivery Endpoint](https://docs.mitter.io/platform-reference-1/delivery-endpoints) as an address for your app. By registering your delivery endpoint you’re telling Mitter.io to forward any message that might be of your concern to the device where your app is installed.

Let’s see how you can do that with the SDK. The process is pretty straightforward.

Once you’re done setting up FCM in your project you should have your custom implementation of `FirebaseMessagingService` as something like `MyFirebaseMessagingService` or whatever name you chose for your implementation during the setup.

In that class, you need to override a method called `onNewToken()` and get a reference to the `Mitter` object from your `Application` class or wherever you chose to initialise it.

Once you have a reference, it’s just a simple method call. Just add this piece of code in your `onNewToken()` method:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
token?.let {
    mitter.registerFcmToken(
        it,
        object : Mitter.OnValueAvailableCallback<DeliveryEndpoint> {
            override fun onValueAvailable(value: DeliveryEndpoint) {
                //Delivery endpoint registered
            }

            override fun onError(error: ApiError) {
                //Delivery endpoint failed to register, retry
            }
        }
    )
}
```

{% endtab %}

{% tab title="Java" %}

```java
if (token != null) {
    mitter.registerFcmToken(
        token,
        new Mitter.OnValueAvailableCallback<DeliveryEndpoint>() {
            @Override
            public void onValueAvailable(DeliveryEndpoint deliveryEndpoint) {
                //Delivery endpoint registered
            }
​
            @Override
            public void onError(ApiError apiError) {
                //Delivery endpoint failed to register, retry
            }
        }
    );
}
```

{% endtab %}
{% endtabs %}

#### Processing incoming FCM messages

Since FCM is a general purpose push messaging solution, you need to do a little setup for the SDK to actually make sense of the incoming messages.

There are just two simple steps involved:

* Check whether the message is from Mitter.io
* Pass the message to the SDK for processing

In a real-world production app, there’s a high chance that you’ll be using FCM for more than a single service. Therefore, it’s a wise choice to put a check to verify whether the incoming message should be processed by the Mitter SDK or by any other SDKs that you might use.

If the message is from Mitter.io, it needs to be forwarded to the SDK for processing.

The process is pretty simple. You just need to add this code in your `onMessageReceived()` method in your implementation of `FirebaseMessagingService` class:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
if (remoteMessage.data.isNotEmpty()) {
    val messagingPipelinePayload = mitter.parseFcmMessage(remoteMessage.data)

    if (mitter.isMitterMessage(messagingPipelinePayload)) {
        mitter.processPushMessage(messagingPipelinePayload)
    }
}
```

{% endtab %}

{% tab title="Java" %}

```java
if (!remoteMessage.getData().isEmpty()) {
    MessagingPipelinePayload messagingPipelinePayload = mitter.parseFcmMessage(remoteMessage.getData());
​
    if (mitter.isMitterMessage(messagingPipelinePayload)) {
        mitter.processPushMessage(messagingPipelinePayload, null);
    }
}
```

{% endtab %}
{% endtabs %}

Here, we’re initially checking if the message is non-empty by verifying the `RemoteMessage` object received from FCM.

After that is done, we proceed to parsing the message data and getting a `MessagingPipelinePayload` object in return. This object is in turn put to test by the `isMitterMessage()` method.

If it indeed is a valid message, we continue in processing this message by calling the `processPushMessage()` message and passing the previously obtained `MessagingPipelinePayload` object to it.

Now, the SDK will process all incoming messages and notify you of any relevant event. Speaking of getting notified, we need some mechanism to actually listen to any incoming events from the SDK.

This is exactly what you’ll be learning in the next section.

#### Listening to incoming events

All that you need to do to is to register some callbacks with the SDK and your app will always be notified of any relevant events.

To register a callback, you need to open up your `Application` class or wherever you’ve initialised the `Mitter` object. Then you need to register a callback on that object like this:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
mitter.registerOnPushMessageReceivedListener(object : Mitter.OnPushMessageReceivedCallback {
    override fun onChannelStreamData(
        channelId: String,
        streamId: String,
        streamData: ContextFreeMessage
    ) {
        //Called when there's some streaming data such as typing indicator
    }
​
    override fun onNewChannel(channel: Channel) {
        //Called when a new channel is created where the user is a participant
    }
​
    override fun onNewChannelTimelineEvent(
        channelId: String,
        timelineEvent: TimelineEvent
    ) {
        //Called when there's a new timeline event for a channel
    }
​
    override fun onNewMessage(
        channelId: String,
        message: Message
    ) {
        //Called when a new message has arrived for the user
    }
​
    override fun onNewMessageTimelineEvent(
        messageId: String,
        timelineEvent: TimelineEvent
    ) {
        //Called when there's a new timeline event for a message
    }
​
    override fun onParticipationChangedEvent(
        channelId: String,
        participantId: String,
        newStatus: ParticipationStatus,
        oldStatus: ParticipationStatus?
    ) {
        //Called when the user has joined a new channel or has been removed from one
    }
})
```

{% endtab %}

{% tab title="Java" %}

```java
mitter.registerOnPushMessageReceivedListener(new Mitter.OnPushMessageReceivedCallback() {
    @Override
    public void onNewMessage(
        String channelId,
        Message message
    ) {
        //Called when a new message has arrived for the user
    }
​
    @Override
    public void onNewChannel(Channel channel) {
        //Called when a new channel is created where the user is a participant
    }
​
    @Override
    public void onNewMessageTimelineEvent(
        String messageId,
        TimelineEvent timelineEvent
    ) {
        //Called when there's a new timeline event for a message
    }
​
    @Override
    public void onNewChannelTimelineEvent(
        String channelId,
        TimelineEvent timelineEvent
    ) {
        //Called when there's a new timeline event for a channel
    }
​
    @Override
    public void onParticipationChangedEvent(
        String channelId, String participantId,
        ParticipationStatus participationStatus,
        ParticipationStatus participationStatus1
    ) {
        //Called when the user has joined a new channel or has been removed from one
    }
​
    @Override
    public void onChannelStreamData(
        String channelId,
        String streamId,
        ContextFreeMessage contextFreeMessage
    ) {
        //Called when there's some streaming data such as typing indicator
    }
});
```

{% endtab %}
{% endtabs %}

Just add your own callbacks or send out events from the Mitter callbacks to notify various parts of your app about any respective events that they need to handle.

### Wrap up

Congratulations! Your app now works in real-time. In the next sections, you’ll be learning how to take full advantage of the SDK to spice up your app with presence updation, message read events and more.


# Presence and Timeline Events

Okay, now that you’ve laid a solid foundation for sending and receiving messages in your app, it’s time to step up the game and enrich your app with a lot more features Mitter.io has to offer.

### Setting user presence

User presence is a vital ingredient of any solid messaging platform. Mitter.io is no exception. The SDK gives you multiple ways to update your user’s presence without the hassle.

#### What is a presence?

Presence, as the name suggests, is the current availability of a user. It is usually a single word which tells whether the user is currently available to chat or is not available at the moment.

You can [learn more about presence](https://docs.mitter.io/platform-reference-1/users#user-presence) and how Mitter.io handles them in the reference section.

#### Exploring the bundled presence types

The Mitter Android SDK ships with a couple of default presence types which you can use to get started:

* Online
* Away
* Sleeping
* Missing
* Offline

Each of these presence types has a `timeToLive` field which indicates the time in seconds up to which a presence is valid. After the time has elapsed, the user presence automatically shifts to the next presence in line (if any).

#### Updating your user’s presence

The SDK allows you to update the current user’s presence in two ways:

* Update the presence manually
* Tell the SDK to keep updating the presence automatically

Regardless of which approach you choose, you need a `Presence` object, to begin with. Therefore, let’s build one:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
val presence = PresenceBuilder()
    .startWith(StandardUserPresenceTypes.online(15))
    .then(StandardUserPresenceTypes.away(30))
    .then(StandardUserPresenceTypes.sleeping(60))
    .then(StandardUserPresenceTypes.offline())
    .build()
```

{% endtab %}

{% tab title="Java" %}

```java
Presence presence = new PresenceBuilder()
    .startWith(StandardUserPresenceTypes.INSTANCE.online(15))
    .then(StandardUserPresenceTypes.INSTANCE.away(30))
    .then(StandardUserPresenceTypes.INSTANCE.sleeping(60))
    .then(StandardUserPresenceTypes.INSTANCE.offline(0))
    .build();
```

{% endtab %}
{% endtabs %}

Here, we’re using the handy `PresenceBuilder` to build a cascading presence in a fluent manner. In a nutshell, the user’s presence begins with `Online` then shifts to `Away` after **15 seconds** and so on.

Now that you have the `Presence` object, you can choose either the manual approach or the automatic one for updating. It’s recommended that you use the *automatic approach* as it’s less management work for you.

**Update presence automatically**

To start updating the current user’s presence automatically, all you need to do is call the `startAutoUpdateCurrentUserPresence()` method on the `Users` object:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
users.startAutoUpdateCurrentUserPresence(presence)
```

{% endtab %}

{% tab title="Java" %}

```java
users.startAutoUpdateCurrentUserPresence(presence, 5);
```

{% endtab %}
{% endtabs %}

What’s happening behind the scenes is that the SDK periodically polls on your behalf to update the current user’s presence as `Online`. Once the user exits your app, it stops polling and as a result, the presence starts expiring to the next status as discussed earlier.

> Note: Here, the second argument value is the polling interval (in secs). Therefore, this example sets polling every 5 seconds.

**Update presence manually**

If you prefer not to use the automatic approach, the SDK also allows you to update the current user’s presence in a single shot.

To update the current user’s presence you need to call the `setCurrentUserPresence()` method on the `Users` object. Something like this:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
users.setCurrentUserPresence(
    presence,
    object : Mitter.OnValueUpdatedCallback {
        override fun onSuccess() {
            Log.d("Mitter", "Presence updated!")
        }

        override fun onError(error: ApiError) {
            Log.d("Mitter", "User Presence - ApiError: $error")
        }
    }
)
```

{% endtab %}

{% tab title="Java" %}

```java
users.setCurrentUserPresence(
    presence,
    new Mitter.OnValueUpdatedCallback() {
        @Override
        public void onSuccess() {
            Log.d("Mitter", "Presence updated!");
        }

        @Override
        public void onError(ApiError apiError) {
            Log.d("Mitter", "User Presence - ApiError: " + apiError);
        }
    }
);
```

{% endtab %}
{% endtabs %}

> **Note**: This is a one-shot update call. If you don’t make this call periodically, the current user’s presence will shift to the next expiring presence after the `timeToLive` has elapsed, even if your app is open.

#### Fetching another user’s presence

Just as you to seamlessly set the current user’s presence, you can easily fetch any user’s presence by calling the `getUserPresence()` method on the `Users` object.

Just do the following:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
users.getUserPresence(
    userId = "debd7e00-1c3e-463c-92d1-dbb0e9e0e4ba",
    onValueAvailableCallback = object : Mitter.OnValueAvailableCallback<Presence> {
        override fun onValueAvailable(value: Presence) {
            Log.d("Mitter", "Presence: ${value.type}")
        }

        override fun onError(apiError: ApiError) {
            Log.d("Mitter", "User Presence - ApiError: $error")
        }
    }
)
```

{% endtab %}

{% tab title="Java" %}

```java
users.getUserPresence(
    "debd7e00-1c3e-463c-92d1-dbb0e9e0e4ba",
    new Mitter.OnValueAvailableCallback<Presence>() {
        @Override
        public void onValueAvailable(Presence presence) {
            Log.d("Mitter", "Presence: " + presence.getType());
        }

        @Override
        public void onError(ApiError apiError) {
            Log.d("Mitter", "User Presence - ApiError: " + apiError);
        }
    }
);
```

{% endtab %}
{% endtabs %}

Unlike setting a presence, the SDK doesn’t currently provide an automatic presence fetching mechanism. This means that you’ll have to poll this call with a short time interval let’s say every **5 seconds**, to continuously stay in sync with the other user’s presence.

A good place for doing this would be your chat screen. You can call this method every **5** or **10 seconds** on the chat screen for every other participant in the chat *except* the current user.

That’s all you need to know about handling presence for users. Now, we’ll move on to another interesting topic which is dealing with timeline events.

### Adding timeline events

In the previous sections, we’ve talked a lot about timeline events. You’ve also noticed that you need to add a `SentTime` timeline event to every message you sent.

You must be wondering, how are the useful exactly and if there are other types of timeline events that you can use. Good news, there are some other timeline events that you can use out the box while always having the ability to spin up a custom event of your own.

#### What are timeline events?

Think of timeline events as categorised timestamp for all activities by a user. Learn more [here](https://docs.mitter.io/platform-reference-1/messages#timeline-events).

#### Exploring the standard timeline events

Although you can create any custom type of timeline events, Mitter.io provides a set of event types out of the box. Currently, there are **4 standard** event types shipped by Mitter.io:

* `mitter.mtet.SentTime` - The time at which the message was sent
* `mitter.mtet.ReceivedTime` - The time at which the message was received by the server
* `mitter.mtet.DeliveredTime` - The time at which the message was delivered to the user
* `mitter.mtet.ReadTime` - The time at which the message was read by the user

While you can any of these standard events from the SDK, the SDK provides convenience methods for the `mitter.mtet.DeliveredTime` and `mitter.mtet.ReadTime` events where you don’t have to set these types explicitly. You’ll see how.

#### Adding a standard timeline event to a message

Taking an use case of adding a read receipt to a message, this can easily be done by calling the `addReadTimelineEvent()` method on the `Messaging` object. Here’s a quick demo:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
messaging.addReadTimelineEvent(
    channelId = "b8bcb84d-8ad2-4f8a-816e-92772b2b8055",
    messageIds = listOf(
        "89a3de83-769e-4b62-8fee-b50b197c09b4",
        "8e5ea0fe-82e1-4b12-b6ca-5f33d965c0fa"
    )
)
```

{% endtab %}

{% tab title="Java" %}

```java
messaging.addReadTimelineEvent(
    "b8bcb84d-8ad2-4f8a-816e-92772b2b8055",
    Arrays.asList(
        "89a3de83-769e-4b62-8fee-b50b197c09b4",
        "8e5ea0fe-82e1-4b12-b6ca-5f33d965c0fa"
    ),
    null
);
```

{% endtab %}
{% endtabs %}

In this example, we’re adding a read timeline event to **2 messages** at once without explicitly setting the timeline event type and putting in any timestamp. The SDK does the job for you.

You can, also, attach an optional callback to this method as the last parameter `onValueUpdatedCallback`, if you want to be notified after the operation has succeeded.

#### Adding a customised timeline event to a message

When you grow out the standard use cases, you might want to use the `addTimelineEvent()` method to add a timeline event to your messages. This method accepts a `TimelineEvent` object which you need to construct on your own, fully customised to your needs.

**Preparing the TimelineEvent object**

Before sending out any event, you need to construct a `TimelineEvent` object with some required parameters, as follows:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
val timelineEvent = TimelineEvent(
    type = "com.acme.events.GiftCardOpened",
    eventTimeMs = System.currentTimeMillis(),
    subject = User().setUserId("3764e344-5a53-4c02-8bfa-2b3bc11e7c0e")
)
```

{% endtab %}

{% tab title="Java" %}

```java
TimelineEvent timelineEvent = new TimelineEvent(
    UUID.randomUUID().toString(),
    "",
    "com.acme.events.GiftCardOpened",
    System.currentTimeMillis(),
    new User().setUserId("3764e344-5a53-4c02-8bfa-2b3bc11e7c0e"),
    null
);
```

{% endtab %}
{% endtabs %}

Here, the `type` is the type of the event that you’re sending, `eventTimeMs` is the timestamp you want to associate with the event and the `subject` is the user with whom this event should be associated.

For example, if you’re sending a read time type of event, the `subject` would the receiving user who has read the message.

**Sending the event**

Now that you have the event prepared with your necessary customisations, it’s time to actually send it out for Mitter.io to deliver it to the concerned user(s).

You can do the same by calling the `addTimelineEvent()` method and supplying the `TimelineEvent` object that you created in the previous step. This is how the code looks:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
messaging.addTimelineEvent(
    channelId = "b8bcb84d-8ad2-4f8a-816e-92772b2b8055",
    messageIds = listOf(
        "89a3de83-769e-4b62-8fee-b50b197c09b4",
        "8e5ea0fe-82e1-4b12-b6ca-5f33d965c0fa"
    ),
    timelineEvent = timelineEvent
)
```

{% endtab %}

{% tab title="Java" %}

```java
messaging.addTimelineEvent(
    "b8bcb84d-8ad2-4f8a-816e-92772b2b8055",
    Arrays.asList(
        "89a3de83-769e-4b62-8fee-b50b197c09b4",
        "8e5ea0fe-82e1-4b12-b6ca-5f33d965c0fa"
    ),
    timelineEvent,
    null
);
```

{% endtab %}
{% endtabs %}

As you can notice, it’s almost the same as the `addReadTimelineEvent()` method with the exception that it accepts a `TimelineEvent` object without assuming anything on it’s own.

This is how you can have full control over what events you want to send out.

### Wrap up

This section has dealt with some advanced properties of Mitter.io and how you can leverage them to work the way you want.

In the next section, you’ll learn how to update your user’s profile and adding certain locators that’ll help you find any user in your application.


# Profiles, Pagination and Locators

By now, you’re pretty well versed with how the SDK works and this level of knowledge with get you through most use cases. However, if you need more control, this is how you can do so.

### Managing user profiles

Mitter.io allows you to create your user profiles the way you want without assuming anything specific profile information fields. For example, if you’re building a sports chat app and you want to store a user’s rank, you have the control to do so.

#### How are user profiles handled?

As said already, Mitter.io works as a transparent medium between you and your users, which means that you’re totally free to customise your user’s experience the way you want.

You can have as many profile information fields you want and how you want them. There isn’t any restriction on that.

The standard process of storing a user profile attribute is *two-fold:*

* First, you define a custom attribute by specifying its type and name
* Then, you update that attribute for a user with actual data

There is, however, an option to skip the first part (defining your attribute) and move directly to store the value of the attribute. This is where Mitter.io standard attributes come to play.

You can learn more about [User Profiles ](https://docs.mitter.io/platform-reference-1/users#user-profile)in the reference section.

#### Exploring the bundled profile attributes

Mitter.io ships with a decent number of pre-defined attributes for user profiles, so that you can get started right away without defining your attributes first.

Currently, Mitter.io ships with:

* FirstName
* LastName
* AvatarUrl
* Mobile
* Dob
* Bio
* Gender
* Email
* Street
* City
* State
* Zip
* Country

If you need attribute outside the scope of this list, you need to define it first from your backend service. Currently, the SDK doesn’t provide a way to define your attributes. This can only be done from the backend controlling your Mitter.io application.&#x20;

> **Note**: We may at a later point change this behaviour.

#### Updating your user’s profile

Assuming that you’re using the bundled attributes to create your user’s profile, the SDK provides an easy and fluent API for the job.

Defining your user’s profile is as easy as this:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
val userProfile = UserProfile().Builder()
    .withFirstName("Rahul")
    .withLastName("Chowdhury")
    .withGender("Male")
    .withCountry("India")
    .build()
```

{% endtab %}

{% tab title="Java" %}

```java
UserProfile userProfile = new UserProfile().new Builder()
    .withFirstName("Rahul")
    .withLastName("Chowdhury")
    .withGender("Male")
    .withCountry("India")
    .build();
```

{% endtab %}
{% endtabs %}

This will provide you with a `UserProfile` object which you can pass to the `updateCurrentUserProfile()` method to create/update the currently logged-in user’s profile in a single shot.

This is how:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
users.updateCurrentUserProfile(
    userProfile,
    object : Mitter.OnValueUpdatedCallback {
        override fun onSuccess() {
            Log.d("Mitter", "User Profile updated!")
        }

        override fun onError(error: ApiError) {
            Log.d("Mitter", "ApiError: $error")
        }
    }
)
```

{% endtab %}

{% tab title="Java" %}

```java
users.updateCurrentUserProfile(
    userProfile,
    new Mitter.OnValueUpdatedCallback() {
        @Override
        public void onSuccess() {
            Log.d("Mitter", "User Profile updated!");
        }

        @Override
        public void onError(ApiError apiError) {
            Log.d("Mitter", "ApiError: " + apiError);
        }
    }
);
```

{% endtab %}
{% endtabs %}

**Updating custom defined attributes**

If you’ve already defined a custom profile attribute from your backend and want to update the value of the same for your currently logged-in user, there’s a slightly different approach for that.

In that case, you need to make use of the `addCurrentUserProfileAttribute()` method to set your user’s profile attribute.&#x20;

There are **2** variations of this method:

* You can provide just the attribute value and let the SDK define the content encoding and other properties for you
* You can construct the `Attribute` object yourself and pass it to the method

Since constructing the `Attribute` object is pretty straightforward, we’ll stick to using the approach where we provide just the value of the attribute.

Let’s update a custom profile attribute:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
users.addCurrentUserProfileAttribute(
    attributeType = "com.acme.user.attributes.Rank",
    attributeValue = "Grand Master",
    onValueUpdatedCallback = object : Mitter.OnValueUpdatedCallback {
        override fun onSuccess() {
            Log.d("Mitter", "User Profile updated!")
        }

        override fun onError(error: ApiError) {
            Log.d("Mitter", "ApiError: $error")
        }
    }
)
```

{% endtab %}

{% tab title="Java" %}

```java
users.addCurrentUserProfileAttribute(
    "com.acme.user.attributes.Rank",
    "Grand Master",
    new Mitter.OnValueUpdatedCallback() {
        @Override
        public void onSuccess() {
            Log.d("Mitter", "User Profile updated!");
        }

        @Override
        public void onError(ApiError apiError) {
            Log.d("Mitter", "ApiError: " + apiError);
        }
    }
);
```

{% endtab %}
{% endtabs %}

#### Fetching a user’s profile

There are two methods to get a user’s profile:

* `getCurrentUserProfile()` - Get the currently logged-in user’s profile
* `getUserProfile()` - Get another user’s profile

Let’s see how you can get the currently logged-in user’s profile:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
users.getCurrentUserProfile(object : Mitter.OnValueAvailableCallback<EntityProfile> {
    override fun onValueAvailable(value: EntityProfile) {
        value.attributes.forEach {
            Log.d("Mitter", "${it.key}: ${it.value}")
        }
    }

    override fun onError(apiError: ApiError) {
        Log.d("Mitter", "ApiError: $apiError")
    }
})
```

{% endtab %}

{% tab title="Java" %}

```java
users.getCurrentUserProfile(
    new Mitter.OnValueAvailableCallback<EntityProfile>() {
        @Override
        public void onValueAvailable(EntityProfile entityProfile) {
            for (Attribute attribute : entityProfile.getAttributes()) {
                Log.d("Mitter", attribute.getKey() + ": " + attribute.getValue());
            }
        }

        @Override
        public void onError(ApiError apiError) {
            Log.d("Mitter", "ApiError: " + apiError);
        }
    }
);
```

{% endtab %}
{% endtabs %}

By calling this method, you get an `EntityProfile` object which contains a list of `Attribute` objects that has been set for the user. You can loop through the list to get the entire profile information.

For any other user, the method structure is similar, the difference being that you need to pass the user ID for the user you want to fetch the profile. This is how:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
users.getUserProfile(
    userId = "ae200062-f54a-4b6e-a791-afb178d1389f",
    onValueAvailableCallback = object : Mitter.OnValueAvailableCallback<EntityProfile> {
        override fun onValueAvailable(value: EntityProfile) {
            value.attributes.forEach {
                Log.d("Mitter", "${it.key}: ${it.value}")
            }
        }

        override fun onError(apiError: ApiError) {
            Log.d("Mitter", "ApiError: $apiError")
        }
    }
)
```

{% endtab %}

{% tab title="Java" %}

```java
users.getUserProfile(
    "ae200062-f54a-4b6e-a791-afb178d1389f",
    new Mitter.OnValueAvailableCallback<EntityProfile>() {
        @Override
        public void onValueAvailable(EntityProfile entityProfile) {
            for (Attribute attribute : entityProfile.getAttributes()) {
                Log.d("Mitter", attribute.getKey() + ": " + attribute.getValue());
            }
        }

        @Override
        public void onError(ApiError apiError) {
            Log.d("Mitter", "ApiError: " + apiError);
        }
    }
);
```

{% endtab %}
{% endtabs %}

### Managing channel profiles

Channel profiles are very similar to user profiles. They work in the same manner as the former. The only difference here, SDK-wise is that you need to call `addChannelProfileAttribute()` to update your channel profile data.

The SDK, currently, doesn’t have a fluent API for updating channel profile. You need to update each parameter as and when they’re required.

Also, there’s *isn’t* a `getChannelProfile()` method to fetch a channel’s profile information. You can easily get a channel’s profile information by calling the `getChannel()` method and accessing the `EntityProfile` object.

This is how you can do the same:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
channels.getChannel(
    channelId = "fa1fb538-05ae-4d21-980e-dff88ae379f4",
    onValueAvailableCallback = object : Mitter.OnValueAvailableCallback<Channel> {
        override fun onValueAvailable(value: Channel) {
            value.entityProfile?.attributes?.forEach {
                Log.d("Mitter", "${it.key}: ${it.value}")
            }
        }

        override fun onError(error: ApiError) {
            Log.d("Mitter", error.toString())
        }
    }
)
```

{% endtab %}

{% tab title="Java" %}

```java
channels.getChannel(
    "fa1fb538-05ae-4d21-980e-dff88ae379f4",
    new Mitter.OnValueAvailableCallback<Channel>() {
        @Override
        public void onValueAvailable(Channel channel) {
            for (Attribute attribute : channel.getEntityProfile().getAttributes()) {
                Log.d("Mitter", attribute.getKey() + ": " + attribute.getValue());
            }
        }

        @Override
        public void onError(ApiError apiError) {
            Log.d("Mitter", "ApiError: " + apiError);
        }
    }
);
```

{% endtab %}
{% endtabs %}

### Fetching messages in pages

To achieve pagination, first, you need to get a reference to the `MessagePaginationManager`.

The `Messaging` object has a method called `getPaginatedMessagesInChannel()` which hands over a manager to you. Refer to this code:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
val messagePaginationManager = messaging.getPaginatedMessagesInChannel(channelId)
```

{% endtab %}

{% tab title="Java" %}

```java
final MessagePaginationManager messagePaginationManager = messaging.getPaginatedMessagesInChannel(
    channelId,
    new FetchMessageConfig()
);
```

{% endtab %}
{% endtabs %}

Here, you provide a channel ID and get a manager to handle pagination for that particular channel. Once you’ve got that, you can easily paginate front and back in the list of messages present in the channel.

Here’s how:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
messagePaginationManager.fetchNextPage(object : PaginationManager.Callback<Message> {
    override fun onPageAvailable(items: List<Message>) {
        Log.d("Mitter", "Page Size: ${items.size}")
        items.forEach {
            Log.d("Mitter", it.textPayload)
        }

        messagePaginationManager.fetchPreviousPage(object : PaginationManager.Callback<Message> {
            override fun onPageAvailable(items: List<Message>) {
                Log.d("Mitter", "Previous: Page Size: ${items.size}")
                items.forEach {
                    Log.d("Mitter", it.textPayload)
                }

                messagePaginationManager.fetchPreviousPage(object : PaginationManager.Callback<Message> {
                    override fun onPageAvailable(items: List<Message>) {
                        Log.d("Mitter", "Previous - 2: Page Size: ${items.size}")
                        items.forEach {
                            Log.d("Mitter", it.textPayload)
                        }
                    }

                    override fun onError(apiError: ApiError) {
                        Log.d("Mitter", "Message Pagination - ApiError: ${apiError.message}")
                    }
                })
            }

            override fun onError(apiError: ApiError) {
                Log.d("Mitter", "Message Pagination - ApiError: ${apiError.message}")
            }
        })
    }

    override fun onError(apiError: ApiError) {
        Log.d("Mitter", "Message Pagination - ApiError: ${apiError.message}")
    }
})
```

{% endtab %}

{% tab title="Java" %}

```java
messagePaginationManager.fetchNextPage(new PaginationManager.Callback<Message>() {
    @Override
    public void onPageAvailable(List<? extends Message> list) {
        Log.d("Mitter", "Page Size: " + list.size());
        for (Message message : list) {
            Log.d("Mitter", message.getTextPayload());
        }

        messagePaginationManager.fetchPreviousPage(new PaginationManager.Callback<Message>() {
            @Override
            public void onPageAvailable(List<? extends Message> list) {
                Log.d("Mitter", "Previous: Page Size: " + list.size());
                for (Message message : list) {
                    Log.d("Mitter", message.getTextPayload());
                }

                messagePaginationManager.fetchPreviousPage(new PaginationManager.Callback<Message>() {
                    @Override
                    public void onPageAvailable(List<? extends Message> list) {
                        Log.d("Mitter", "Previous - 2: Page Size: " + list.size());
                        for (Message message : list) {
                            Log.d("Mitter", message.getTextPayload());
                        }
                    }

                    @Override
                    public void onError(ApiError apiError) {
                        Log.d("Mitter", "Message Pagination - ApiError: " + apiError.getMessage());
                    }
                });
            }

            @Override
            public void onError(ApiError apiError) {
                Log.d("Mitter", "Message Pagination - ApiError: " + apiError.getMessage());
            }
        });
    }

    @Override
    public void onError(ApiError apiError) {
        Log.d("Mitter", "Message Pagination - ApiError: " + apiError.getMessage());
    }
});
```

{% endtab %}
{% endtabs %}

In this example, you can also call `fetchPreviousPage()` instead of the initial `fetchNextPage()`, it’ll return the same list of messages because the pointer isn’t initialised, yet. As soon you make the *first* call, `fetchPreviousPage()` and `fetchNextPage()` behaves exactly as they should.

As you can see, you can go any level deep to fetch your messages. However, in ideal cases, you won’t be nesting this much. You would probably have a loop or a UI action that calls either `fetchPreviousPage()` or the `fetchNextPage()` resulting in a much flatter structure.

A typical use case for this type of paging is to have an infinite scrolling list. In the list, you can call `fetchPreviousPage()` as soon as the user tends to reach the top/bottom of the list based on your display logic.

> **Note**: You don’t need to maintain paging state anywhere, the SDK does all that for you.

### Locating users in your application

Mitter.io supports attaching additional pieces of information to a user known as **User Locators**. User Locators are simply look-up keys that allow you to search a particular user by that key. To learn more about locators, refer [to this section](https://docs.mitter.io/platform-reference-1/users#user-locators).

#### Using user locators

Currently, Mitter.io supports only **2 types** of user locators:

* `email` - For attaching email addresses
* `tele` - For attaching phone numbers

To use any of these locators while attaching them to a user or searching a user using a locator, you need to specify these keys. The SDK can smartly add in the keys for you while attaching a locator, judging from the locator value that you provide.

For example, if you provide a mobile number while attaching a locator, it’ll add the type automatically for you. The same goes for attaching email locators.

#### Attaching locators to a user

The SDK provides a method called `addCurrentUserLocator()` to attach a locator of type `email` or `tele` to the current user. Here’s how it works:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
users.addCurrentUserLocator("+911234567890")
```

{% endtab %}

{% tab title="Java" %}

```java
users.addCurrentUserLocator("+911234567890", null);
```

{% endtab %}
{% endtabs %}

This adds a locator of type `tele` to the current user, where the value of the locator is `+911234567890`. You can also add an email locator to the current user using the same method:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
users.addCurrentUserLocator("rahul@mitter.io")
```

{% endtab %}

{% tab title="Java" %}

```java
users.addCurrentUserLocator("rahul@mitter.io", null);
```

{% endtab %}
{% endtabs %}

> **Note**: You don’t need to explicitly specify any type, the SDK infers the type from the value you enter.

#### Searching for users using a locator

Now that you’ve attached a locator, you can easily search for a user using the user locators. You can get a list of users that matches your query parameter by calling the `getUserByLocators()` method on the `Users` object and passing a list of serialised locators.

Here’s how you can do this:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
users.getUserByLocators(
    locators = listOf(
        "email:rahul@mitter.io"
    ),
    onValueAvailableCallback = object : Mitter.OnValueAvailableCallback<List<User>> {
        override fun onValueAvailable(value: List<User>) {
            Log.d("Mitter", "Users: $value")
        }

        override fun onError(apiError: ApiError) {
            Log.d("Mitter", apiError.toString())
        }
    }
)
```

{% endtab %}

{% tab title="Java" %}

```java
users.getUserByLocators(
    Arrays.asList(
        "email:rahul@mitter.io"
    ),
    new Mitter.OnValueAvailableCallback<List<? extends User>>() {
        @Override
        public void onValueAvailable(List<? extends User> users) {
            Log.d("Mitter", "Users: " + users);
        }

        @Override
        public void onError(ApiError apiError) {
            Log.d("Mitter", apiError.toString());
        }
    }
);
```

{% endtab %}
{% endtabs %}

If your query matches any existing user in the scope of your application, you get a list of such users in the callback.

### Wrap up

That completes the advanced customisation and querying for users section. We can’t wait to see what you build with Mitter.io.


# Using the UI Framework

Learn how to use Mitter.io UI Framework to get a chat app running in record time.

The Mitter.io Core package provides easy access to the platform from your Android project. However, getting from zero to seeing chat messages appear in your app requires a bit of UI setup which is mostly boilerplate code.

The UI framework package aims to reduce the time taken to get a basic chat app running to the minimum by providing a lightweight framework using which you can add your custom UI elements like chat bubbles and more to your app without filling out too much boilerplate.

## Installation

Adding the UI framework package is similar to adding any library in Android. Just add the following line to your `build.gradle` file and sync your dependencies:

{% code title="build.gradle" %}

```groovy
implementation 'io.mitter.android:uiframework:0.1.5'
```

{% endcode %}

## Adding a ChannelWindow

Currently, the UI framework provides a `ChannelWindow` view which is a very thin wrapper on Android’s `RecyclerView`. The difference between `RecyclerView` and `ChannelWindow` is that the latter provides additional features like pagination out of the box.

You can add a `ChannelWindow` view to your `Activity` or `Fragment` XML layout where you want the messages to be displayed.

{% code title="activity\_chat.xml" %}

```markup
<io.mitter.android.ui.views.ChannelWindow
	android:id="@+id/channelWindow"
	android:layout_width="match_parent"
	android:layout_height="wrap_content"  
	app:layoutManager="android.support.v7.widget.LinearLayoutManager" />

```

{% endcode %}

## Creating producers

A `ChannelWindow` needs to be connected to a `ChannelWindowManager` to work. A `ChannelWindowManager` creates an internal `RecyclerView` adapter from a list of producers and displays your messages on the `ChannelWindow`.

### What are producers?

Producers are just simple view producers. You use a producer to define and create a single row of your channel list. In simple terms, this is where you control the look and feel of your message bubbles.

### Define a producer

To define a new producer, you need to implement the `ChannelWindowElementProducer` interface. This interface has 3 methods:

* `canProduceFor()` - Here, you need to write a condition which will match this view for a message type
* `getViewId()` - Here, you need to specify a layout ID which you want your message to be rendered on
* `produceView()` - This is where you get a `RecyclerView.ViewHolder` and your `Message`. You need to bind your message data to the UI using the supplied `ViewHolder`

A simple text message producer would look similar to the one below:

{% tabs %}
{% tab title="Kotlin" %}
{% code title="TextMessageProducer.kt" %}

```kotlin
class TextMessageProducer(
    val mitter: Mitter
) : ChannelWindowElementProducer {
    override fun canProduceFor(element: Any): Boolean {
        return element is Message && element.payloadType == StandardPayloadTypeNames.TextMessage
    }

    override fun getViewId(): Int = R.layout.item_text_message

    override fun produceView(holder: RecyclerView.ViewHolder, element: Any) {
        val message = element as Message

        holder.itemView.text.text = message.textPayload
        holder.itemView.senderName.text = message.senderId.domainId()
        holder.itemView.textMessageWrapper.gravity = when (mitter.getUserId()) {
            message.senderId.domainId() -> Gravity.END
            else -> Gravity.START
        }
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="TextMessageProducer.java" %}

```java
public class TextMessageProducer implements ChannelWindowElementProducer {
    private Mitter mitter;

    public TextMessageProducer(Mitter mitter) {
        this.mitter = mitter;
    }

    @Override
    public void produceView(@NotNull RecyclerView.ViewHolder holder, @NotNull Object o) {
        Message message = (Message) o;

        TextView messageText = holder.itemView.findViewById(R.id.text);
        TextView senderNameText = holder.itemView.findViewById(R.id.senderName);
        LinearLayout textMessageWrapperLayout = holder.itemView.findViewById(R.id.textMessageWrapper);

        messageText.setText(message.getTextPayload());
        senderNameText.setText(message.getSenderId().domainId());
        if (mitter.getUserId().equals(message.getSenderId().domainId())) {
            textMessageWrapperLayout.setGravity(Gravity.END);
        } else {
            textMessageWrapperLayout.setGravity(Gravity.START);
        }
    }

    @Override
    public boolean canProduceFor(@NotNull Object o) {
        return o instanceof Message && ((Message) o).getPayloadType().equals(StandardPayloadTypeNames.TextMessage);
    }

    @Override
    public int getViewId() {
        return R.layout.item_text_message;
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

Writing a producer is quite similar to writing a `RecyclerView` adapter. A producer, however, abstracts away boilerplate code and helps you write smaller and separated view binding logic.

You can create as many producers as you want depending on your incoming message types. Some common examples would be:

* TextMessageProducer
* ImageMessageProducer
* EmojiMessageProducer

And so on.

## Setting up a manager

As already discussed in the previous section, a `ChannelWindow` needs a `ChannelWindowManager` to work. A `ChannelWindowManager` in turn needs one or more producers to function.

Now that you’ve already created a producer, you can setup your manager by passing your producer and a channel ID for which you want the messages to be loaded on the app screen.

Before you can initialise a manager, you need to have an instance of `MitterUi`. You can define an instance of `MitterUi` inside your activity where you want to display your message list (or `ChannelWindow`).

{% tabs %}
{% tab title="Kotlin" %}
{% code title="ChatActivity.kt" %}

```kotlin
lateinit var mitterUi: MitterUi

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_channel_window_test)

        mitterUi = MitterUi(
            (application as MessageApp).mitter
        )
}
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="ChatActivity.java" %}

```java
private MitterUi mitterUi;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mitterUi =  new MitterUi(
        ((MessageApp) getApplication()).mitter
    );
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

> Here `MessageApp` refers to the global `Application` class for this project where the `Mitter` instance is defined. Change this to wherever you’ve defined your `Mitter` instance in your project, as shown in the previous articles of this guide.

Once you have that, you can easily get an instance of `ChannelWindowManager` as follows:

{% tabs %}
{% tab title="Kotlin" %}
{% code title="ChatActivity.kt" %}

```kotlin
val channelWindowManager = ChannelWindowManager(
            mitterUi,
            "channel-id",
            ChannelWindowConfig(producers = listOf(
                TextMessageProducer(mitterUi.mitter),
                StringProducer()
            )
)
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="ChatActivity.java" %}

```java
List<ChannelWindowElementProducer> producers = new ArrayList<>();
producers.add(new TextMessageProducer(mitterUi.getMitter()));

PaginationConfig paginationConfig = new PaginationConfig();
ChannelWindowManager channelWindowManager = new ChannelWindowManager(
    mitterUi,
    "channel-id",
    new ChannelWindowConfig(
        producers,
        paginationConfig
    )
);
```

{% endcode %}
{% endtab %}
{% endtabs %}

After that, you just need to hook your manager to your `ChannelWindow` view:

{% tabs %}
{% tab title="Kotlin" %}
{% code title="ChatActivity.kt" %}

```kotlin
channelWindowManager.attach(channelWindow)
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="ChatActivity.java" %}

```java
ChannelWindow channelWindow = findViewById(R.id.channelWindow);
channelWindowManager.attach(channelWindow);
```

{% endcode %}
{% endtab %}
{% endtabs %}

If you now run your app, you should be able to get messages populated on your device’s screen for the channel ID you mentioned over here. This assumes that you have properly setup `Mitter` with your application credentials as shown in the previous articles.

## Paginating messages

By default, the `ChannelWindow` will load **10** messages at a single go in the specified channel every time you scroll up. You can change this behaviour and also attach a callback to get notified when pagination happens by using the `PaginationConfig` parameter in the `ChannelWindowManager`.

Get started by defining your `PaginationConfig` first:

{% tabs %}
{% tab title="Kotlin" %}
{% code title="ChatActivity.kt" %}

```kotlin
val paginationConfig = PaginationConfig(
            itemsPerPage = 20,
            pageFetchCallback = object : PageFetchCallback {
                override fun onStart() {
                    Log.d("MitterUI", "Loading more messages")
                }

                override fun onEnd() {
                    Log.d("MitterUI", "Successfully loaded more messages")
                }
            }
)
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="ChatActivity.java" %}

```java
PaginationConfig paginationConfig = new PaginationConfig(
    20,
    3,
    new PageFetchCallback() {
        @Override
        public void onStart() {
            Log.d("MitterUI", "Loading more messages");
        }

        @Override
        public void onEnd() {
            Log.d("MitterUI", "Successfully loaded new messages");
        }
    }
);
```

{% endcode %}
{% endtab %}
{% endtabs %}

Once you have your `PaginationConfig` configured to your needs, you can pass it to your `ChannelWindowConfig` instance that you defined inside your `ChannelWindowManager` instance in the previous step to override the default pagination behaviour.

{% tabs %}
{% tab title="Kotlin" %}
{% code title="ChatActivity.kt" %}

```kotlin
val channelWindowManager = ChannelWindowManager(
            mitterUi,
            "channel-id",
            ChannelWindowConfig(
                producers = listOf(
                    TextMessageProducer(mitterUi.mitter),
                    StringProducer()
                ),
                paginationConfig = paginationConfig
            )
)
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="ChatActivity.java" %}

```java
ChannelWindowManager channelWindowManager = new ChannelWindowManager(
    mitterUi,
    "channel-id",
    new ChannelWindowConfig(
        producers,
        paginationConfig
    )
);
```

{% endcode %}
{% endtab %}
{% endtabs %}

Now, your manager will load up to **20** messages at a time instead of **10** and also you’ll start getting callbacks as the user starts scrolling up to fetch more messages.

This callback is useful when you want to show up a loader while fetching previous messages.

## Wrap up

That’s all you need to and can do with the UI framework right now. It’s in a beta stage. Your valuable feedbacks will allow us to improve the UI framework experience in the future releases on this package.


# iOS

The iOS SDK reference documentation

The iOS SDK for Mitter currently has very basic features like:

* Getting the current user information
* Creating a channel
* Send text messages
* Receive message via FCM (through APNs)


# Installation

The SDK is available via Cocoapods and can be installed as a regular pod.

Add this to your `Podfile`:

```
pod ‘Mitter’, :path => ‘../Mitter’
```

Before that, make sure you’ve Cocoapods [installed and setup](https://guides.cocoapods.org/using/using-cocoapods) for your project.

Then just navigate to your new project and run:

```
pod install
```


# Basic Setup

Before you can start communicating with Mitter using the SDK, it needs to be configured with your **application ID** and **user auth token ID**.

The best place to configure a global `Mitter` object is inside your `AppDelegate.swift` file.

Open up the `AppDelegate` file and declare an instance field for the `Mitter` object, like this:

```
var mitter: Mitter = Mitter(applicationId: "")
```

After you’ve done that, locate the function with `didFinishLaunchingWithOptions` signature and initialise your `Mitter` object with your application and user details like this:

```
mitter = Mitter(
            applicationId: "MZzf4-na9nL-O98wq-M1HxS",
            userAuthToken: "eyJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJtaXR0ZXItaW8iLCJ1c2VyVG9rZW5JZCI6IkhYbkZJSXIydUpQRHMzankiLCJ1c2VydG9rZW4iOiJhaHFtNTgzcjRwbzEwZmNqZTllaHE5dDV1NCIsImFwcGxpY2F0aW9uSWQiOiJNWnpmNC1uYTluTC1POTh3cS1NMUh4UyIsInVzZXJJZCI6ImNzckN5LVNKTDN1LThBS01ULVdxdjZ5In0.FTgn0GBgIQrA0NznQEUHyC7SN7rbN9O9cWlI5mejuDG466VSJHjwGWZF2DB3nsn8eoeCg5toIXXh5Sxz2MMU3w"
)
```

The user token is enough to help the SDK figure out the user ID. Therefore, you don’t need to explicitly add the user ID. You can get both these values from the [mitter.io dashboard](https://mitter.io/home).

#### Using the SDK with containerised Mitter.io

If you're using the Mitter.io docker container, then you need to *override* the default API endpoint in the SDK, as follows:

```
mitter = Mitter(
            applicationId: "MZzf4-na9nL-O98wq-M1HxS",
            userAuthToken: "eyJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJtaXR0ZXItaW8iLCJ1c2VyVG9rZW5JZCI6IkhYbkZJSXIydUpQRHMzankiLCJ1c2VydG9rZW4iOiJhaHFtNTgzcjRwbzEwZmNqZTllaHE5dDV1NCIsImFwcGxpY2F0aW9uSWQiOiJNWnpmNC1uYTluTC1POTh3cS1NMUh4UyIsInVzZXJJZCI6ImNzckN5LVNKTDN1LThBS01ULVdxdjZ5In0.FTgn0GBgIQrA0NznQEUHyC7SN7rbN9O9cWlI5mejuDG466VSJHjwGWZF2DB3nsn8eoeCg5toIXXh5Sxz2MMU3w",
            mitterApiEndpoint: "http://localhost:11901"
)
```


# Get the current user details

Now, go to your main `ViewController` which is the `ViewController.swift` file under your project.

Inside the `viewDidLoad()` function, get a reference to your `AppDelegate` instance by adding the following line:

```
let appDelegate = UIApplication.shared.delegate as! AppDelegate
```

Now, you can easily make a call to Mitter and see the currently authenticated user details by calling the `getCurrentUsers()` function on the `Mitter.Users` object.

This can be done like this:

```
                appDelegate.mitter.users.getCurrentUser {
                    result in
                    switch result {
                    case .success(let user):
                        print("Current User is: \(user)")
                    case .error:
                        print("Unable to get user!")
                    }
                }
```

Now, press `⌘R` to run your project in any iPhone simulator.

If everything’s setup according to the previous steps, you should be able to see the currently authenticated user details printed to your console log inside Xcode.


# Create a Channel

To create a new channel using the SDK, you need to create at least 2 `Participant` instances with the participant user IDs and pass them as an array to either to `createDirectMessageChannel()` or `createGroupMessageChannel()` function depending on the number of participants you have.

Let’s initialise two participants and create a direct message channel.

```
let stan = Participant(id: "csrCy-SJL3u-8AKMT-Wqv6y")
let rahul = Participant(id: "E3CAM-jjw8A-WeqDe-cWFe7")
```

Substitute the user IDs with your own user IDs here.

Now, call the function:

```
appDelegate.mitter.channels.createGroupMessageChannel(participants: [stan, rahul]) { result in
            switch result {
            case .success(let channelId):
                print("New channel created: \(channelId)")
            case .error:
                print("Couldn't create channel")
            }
        }
```

Run your project and now you should see a *New channel created* message in your console log along with the ID of the newly created channel.

Note down the ID because we’ll use it to send and receive some messages


# Messaging

### Send a plain Text Message

Sending a plain text message is really simple. Just call the `sendTextMessage()` function on the `Mitter.Messaging` instance and pass a channel ID and text payload.

```
                appDelegate.mitter.messaging.sendTextMessage(
                    forChannel: "rakfT-XPdJb-WsucS-Pxy4B",
                    "Hello from iOS!"
                ) { result in
                    switch result {
                    case .success:
                        print("Message sent!")
                    case .error:
                        print("Couldn't send message")
                    }
                } 
```

Here you can specify the channel ID that you got while creating a new channel in the previous step.

Run the project and it should send out a new message to the channel.

### Get messages in a channel

You can get messages sent to a particular channel by calling the `getMessagesInChannel()` and passing in the channel ID:

```
appDelegate.mitter.messaging.getMessagesInChannel("rakfT-XPdJb-WsucS-Pxy4B") {
            result in
            switch result {
            case .success(let messages):
                print("Messages: \(messages)")
            case .error:
                print("Couldn't fetch messages")
            }
        }
```

Run the project and you should be getting a list of messages printed to your console log.


# Push Messages

Before you can start receiving messages through FCM, you need to setup FCM in your project.

Firebase has a pretty comprehensive tutorial for setting up FCM. Follow the steps [over here](https://firebase.google.com/docs/cloud-messaging/ios/client) and you should be ready to receive messages via FCM.

Now that you’ve configured FCM in your project you can hook it up with Mitter using the following steps:

#### Register a delivery endpoint

Before receiving any messages from Mitter you need to register the device’s FCM token as a delivery endpoint with Mitter.

You can get and register the FCM token by calling the `registerFcmToken()` function on the `Mitter` object within the function which has the signature of `didRegisterForRemoteNotificationsWithDeviceToken` variable, like this:

```
InstanceID.instanceID().instanceID { (result, error) in
            if let error = error {
                print("Error fetching remote instange ID: \(error)")
            } else if let result = result {
                print("Remote instance ID token: \(result.token)")

                self.mitter.registerFcmToken(token: result.token) {
                    result in
                    switch result {
                    case .success(let deliveryEndpoint):
                        print("Endpoint is: \(deliveryEndpoint.serializedEndpoint)")
                    case .error:
                        print("Unable to register endpoint!")
                    }
                }
            }
        }
```

After that, you need to process incoming FCM messages by forwarding them to Mitter. Look for the function called `userNotificationCenter()` which has the variable named `willPresent` and then get the serialised data dictionary from the FCM notification dictionary, like this:

```
let messageString = userInfo["data"] as! String
```

Next, you need to convert this into a `MessagingPipelinePayload` object. This can be done by passing the serialised dictionary from the previous step, like this:

```
let messagingPipelinePayload = mitter.parseFcmMessage(data: messageString)
```

Now that you have the `MessagingPipelinePayload` object, you can check if the message is from Mitter by calling the function `isMitterMessage()` which returns `Bool`.

Next, you need to process the push message by passing the `MessagingPipelinePayload` object and hooking up the completion handlers, like this:

```
if mitter.isMitterMessage(messagingPipelinePayload) {
            let payload = mitter.processPushMessage(messagingPipelinePayload!)

            switch payload {
            case .NewMessagePayload(let message, let channelId):
                print("Received Message: \(message), for Channel: \(channelId)")
            default:
                print("Nothing to print!")
            }
        }
```

Here, `payload` is an enum which has various cases like `NewMessagePayload`, `NewChannelPayload`, etc.

> Do note, for FCM messages to work, you need to run the project in a physical iOS device.

&#x20;*Also, you need to include dataType as cloud-notification on any message that choose to send using a direct API call from Postman or any other REST client.*


# Javascript

The mitter.io web SDKs are a minimalist SDK that allows you to consume mitter.io services with just a few dependencies.

### Setup

> **NOTE** The following setup is for a web-setup only. Refer to the [page on node.js](/sdks/web/for-node-js) for information on using the SDKs with node.js, and the [page on react-native](/sdks/web/for-react-native) for information on using the SDKs with React Native

To setup the mitter-web SDK using yarn (or npm), simply add the `@mitter-io/web` package as a dependency. If you want to use the base models used by mitter, you can also add `@mitter-io/models` package, although that is optional.

```
yarn add @mitter-io/web @mitter-io/models
```

Or, with npm:

```
npm install --save @mitter-io/web @mitter-io/models
```

Typescript users do not need to add typings for these packages separately, they are both bundled in with the application itself. For those working on an IDE supporting typescript definitions (like Visual Studio Code), auto-complete and type checking will be enabled even for JavaScript users.

To be able to make API calls, you can either use the bundled API clients provided with the package or use an interceptor along with an HTTP library of your choice. Mitter npm packages ship with the interceptors for `fetch` and `axios`. They have been tested to work with both, the browser-bundled fetch and the `whatwg-fetch` polyfill. If you do want to use the mitter clients itself, you will need to add `axios` as a dependency

```
yarn add axios
```

Or, with npm:

```
npm install --save axios
```

### Usage and user authorization

To begin using the mitter.io sdk, you need to provide it with an application id and a user authorization:

{% code title="app.js" %}

```javascript
import { Mitter } from '@mitter-io/web'

const mitter = Mitter.forWeb({
    applicationId: 'fb70ff76-ea33-4bb0-bd59-90853f103202', /* provide your application id here */
    mitterApiBaseUrl: '<mitter-api-url>', /* look below for the values */
    weaverUrl: '<distributor-url>' /* look below for values */
}, {
    onTokenExpire: [] /* Your onTokenExpire functions */
})

```

{% endcode %}

For the `<mitter-api-url>`, use the following value:

* If you're using the cloud hosted solution, you can omit the `mitterApiBaseUrl` key in the config or explicitly set it to `https://api.mitter.io`
* If you're running it as a docker container set it to `http://localhost:<port>` where the port is port forwarded by docker for `11902`. To find out which port it is, run the following command `docker port $(docker ps --filter expose=11901-11903/tcp --format "")`

For the `<distributor-url>`, use the following value:

* If you're using the cloud hosted solution, you can omit the `weaverUrl` key in the config or explicitly set it to `https://weaver.mitter.io`
* If you're running it as a docker container set it to `http://localhost:<port>` where the port is port forwarded by docker for `11903`. To find out which port it is, run the following command `docker port $(docker ps --filter expose=11901-11903/tcp --format "")`

To provide it a user authorization, at any point in the application lifecycle, you can setup:

```
mitter.setUserAuthorization('eyJhbGciOiJIUz ... ')
```

The user authorization is usually fetched from your backend, which can verify your user's credentials and then fetch a token from mitter.io, federated authentication and/or from a hard-coded value fetched from the mitter.io dev panel (use this method only in dev environments for testing).

A sample workflow could be:

{% code title="login.js" %}

```javascript
function onLogin(username, password) {
    fetch('http://mybackend.example.com/login', {
        method: 'POST',
        data: {
            user: username,
            password: password
        }
    )
    .then(response => response.json())
    .then(auth => mitter.setUserAuthorization(auth.mitterAuthorization))
}

```

{% endcode %}

This call can be made multiple times, in case of a user logging out, or refreshing their authorization. The SDK will automatically reset all pipelines and credentials everywhere. Do note that this API is idempotent and multiple calls with the same user authorization will not have any effect (i.e. any pipelines that are setup will not be reset)

> **NOTE** Setting a user authorization does not check if the provided token was valid. If you wish to verify the token, you can make a call to `/v1/users/me` using either the provided API clients (documented below) or using a fetch interceptor

### Making API calls

To make API calls to the mitter service, there are two ways to do this: either using the inbuilt clients, or making HTTP calls using fetch/axios and enabling an interceptor.

#### Using the mitter clients

To access the mitter API clients, you can fetch a client set using the `mitter.clients()` call. On the returned object, then get the relevant client as you desire:

```javascript
const userClient = mitter.clients().users()
const userAuthClient = mitter.clients().userAuth()
const channelsClient = mitter.clients().channels()
const messagesClient = mitter.clients().messages()

```

Every method on this client maps directly to a  mitter.io API that follows exactly the same shape for request/response as the API. Since these are low-level clients, any errors are returned AS-IS with the status and data in the payload.

For example, to create a new channel with a specified id, but to ignore an error in case of a duplicate, one could:

{% code title="create-channel.js" %}

```javascript
channelsClient.newChannel({
    channelId: 'johns-personal-channel',
    defaultRuleSet: 'io.mitter.ruleset.chats.GroupChat',
    timelineEvents: [],
    participation: [],
    systemChannel: false,
    entityMetadata: {},
    entityProfile: {}
})
.then(channelId => personalChannelCreated(channelId)
.catch(error => {
    if (error.status === 409 && error.data.error_code === 'duplicate_entity') {
        // do nothing, or one can call personalChannelCreated() here again
    } else {
        errorCreatingPersonalChannel();
    }
})

```

{% endcode %}

If the above calls seems too wordy, you can use the provided models that adds in defaults for most of the items (this requires the `@mitter-io/models` package):

```javascript
import { Channel, StandardRuleSetNames } from '@mitter-io/models'

channelsClient.newChannel(new Channel(
    'johns-personal-channel',
    StandardRuleSetNames.GroupChat,
    [ mitter.me() ]
))

```

> **NOTE** The `mitter.me` returns a user-like object that can be used in any place in API calls which require a user identifier.

#### Using `fetch`

To directly make API calls using `fetch` one can use the `fetch-interceptor`

```javascript
mitter.enableFetchInterceptor()

fetch('https://api.mitter.io/v1/users/me')
    .then(response => response.json())
    .then(user => console.log('Hello, ' + user.userId))

```

The fetch interceptor, unlike the `axios` interceptor, intercepts all fetch requests globally. This is due to the fact that `fetch` as an object itself happens to be declared in a global scope. The interceptor simply adds the user authorization to your mitter.io api requests (as headers) and will only intercept requests that are made to either `https://api.mitter.io` or `https://api.staging.mitter.io`. If you do not wish to have a global interceptor, a simpler solution would be to add the header yourself:

```javascript
fetch('https://api.mitter.io/v1/users/me', {
    headers: {
        'X-Mitter-Issued-User-Authorization': 'eyJhbGciOiJIUz ... '
    }
)
    
```

If you do not want to keep a copy of the user authorization and just use the one mitter has with itself, you can use:

```javascript
mitter.getUserAuthorization().then(userAuthorization => {
    fetch('https://api.mitter.io/v1/users/me', {
        headers: {
            'X-Mitter-Issued-User-Authorization': userAuthorization
        }
    })
})

```

The interceptor can be disabled at any time using

```javascript
mitter.disableUserAuthorization()
```

#### Using axios

If you are using `axios` (and we recommend using it for making mitter.io API calls), then you can enable an interceptor on a specific axios instance

```javascript
import axios from 'axios'

const myAxios = axios.create({ .. config .. })

mitter.enableAxiosInterceptor(myAxios)

// If you are not using axios instances, but a global interceptor,
// you can still enable an interceptor globally on the axios global
// instance
mitter.enableAxiosInterceptor(axios)
```

Similar to the `fetch` interceptor, even the axios interceptor will intercept only the requests that start with `https://api.mitter.io` and `https://staging.api.mitter.io`.

### Consuming incoming messages

When you setup the basic `mitter` object, it automatically creates relevant pipelines to process incoming payloads. Mitter.io pushes different payloads to users on relevant activity. A few examples are:

1. When the user receives a new message
2. When the user is made part of a new channel
3. When there is any type of stream data in a channel that the user is a part of.

To subscribe to these messages, you can use the `subscribeToPayload` function:

```javascript
mitter.subscribeToPayload(payload => {
    if (payload['@type'] === 'NewMessagePayload') {
        if (!(payload.channelId.identifier in userData['messages'])) {
             userData['messages'][payload.channelId.identifier] = []
         }
         
         userData['messages'][payload.channelId.identifier].push(payload.message)
     }
 })
 
```

The package `@mitter-io/models` also bundles with a number of type-identifying functions that can be used instead of custom matching. Using that, the same code would now look like:

```javascript
import { isNewMessagePayload } from '@mitter-io/models'

mitter.subscriberToPayload(payload => {
    if (isNewMessagePayload(payload)) {
        ...
    }
})

```

Do note that these subscriptions stay active even when user-authorization switches. If your application switches between two users without reloading the page, the subscribed callbacks will still be called. To avoid that, use the `clearSubscriptions` method:

```javascript
mitter.clearSubscriptions()
```


# Using the UI framework (web only)

Mitter's standard component library (scl) makes it even easier to get started building your messaging apps. Unlike the platform and the SDK, the SCL takes a very opinionated approach to the various (often repeated) components of a messaging application.

Currently the SCL is in beta and is distributed for web platforms only. Since the SCL is primarily a UI framework, each edition of the framework is tied to a UI library. Currently one edition of the SCL is available, for React projects.

## Installation

To add the scl to your project, simply use `yarn` or `npm`

```
yarn add @mitter-io/react-scl
```

or

```
npm install --save @mitter-io/react-scl
```

Before you begin make sure that your base project is setup as described [here](/sdks/web#setup)

## Adding a managed Message list

In the getting started application we had the ability to send and receive messages. We were using a simple listener that would listen on any new messages coming to the channel and displaying them as a list of UI components. However, there are a few issues with the same:

1. All previous messages are lost if the page is reloaded
2. If we were to make a network call and get the previous messages that would be a sub-optimal experience as we would want to fetch only the messages that the user is interested in (preferably when they scroll up)
3. If were to do that as well, we still don't want to generate a DOM element for every single message that was loaded - a channel could have upto thousands of messages and it could make your application extremely slow and bloated.

For this purpose, the SCL bundles in a component `MessageListManager` that takes care of all of the above along with some more nifty features for fine-grained control over message rendering.

In the following example, we will continue modifying the `mitter-web-starter` and use a message list from `recat-scl` instead.

You can checkout the final completed app:

```
git clone git@github.com:mitterio/mitter-web-starter --branch with-scl
```

### Creating a message view producer

Before we can start using the `MessageListManager` we need to be able to tell the manager how to render individual messages. To do so, we'll start with creating a message component:

{% code title="src/ChannelComponent.js" %}

```javascript
import { createMessageViewProducer } from '@mitter-io/react-scl'

this.messageViewProducer = createMessageViewProducer(
    (message) => true,
    (message) => {
        const isSelfMessage =
              this.props.selfUserId === message.senderId.identifier

        return (
            <div className={ 'message' + (isSelfMessage ? ' self' : '')}>
                <div className='message-block'> 
                    <span className='sender' ></span>

                    <div className='message-content'>
                        {message.textPayload}
                    </div>
                </div>
            </div>
        )
    }
)

```

{% endcode %}

A message view producer takes two lambdas - the first one is a predicate which for a given message dictates if the current view will be used to render the message and the second returns the rendered component. In our case we are setting our predicate to always return `true` since we want to use the same producer for all messages.

> **NOTE** The above is code adapted from the `mitter-web-starter` package which you can use as a starting point to test out SCL.

### Displaying messages

With a message view producer in place, we can now use the `MessageListManager` component. An example is as shown below:

{% code title="src/ChannelComponent.js" %}

```javascript
renderMessages() {
    if (this.state.activeChannel === null) {
        return <div></div>
    }

    const activeChannelMessages =
            this.props.channelMessages[this.state.activeChannel]

    return (
        <MessageListManager
            messages={activeChannelMessages}
            defaultView={this.messageViewProducer.produceView}
            producers={[this.messageViewProducer]}
            onEndCallback={() => {}}
        />
    )
}
```

{% endcode %}

In the above example we are basically re-writing the `renderMessages` function. The various props passed to the component are:

`messages` This is a list of messages that are to be rendered. This can be a reference to the entire message list if required, or only a subset that is available. The `MessageListManager` uses a virtualized list that will create DOM elements for only the messages that are visible.

`defaultView` This is the view producer used when there is no other view available. Do note that we don't directly use the view producer, but instead use `.produceView` function on it. This is because `defaultView` does not perform any predicate checking and expects only rendering to be performed.

`producers` are a list of producers used to render messages. To render a particular message all producers are run (in insertion order) till one of their predicates returns `true`. If no such producer is found, then the `defaultView` is used.

An `onEndCallback` is a function called when new messages are to be fetched. We will shortly look into how to use this callback.

Till this point, everything would be working exactly like the `mitter-web-starter`. In terms of functionality we haven't really added anything else. Let's now add a `PaginationManager` to handle various events where the messages would be populated.

### Adding a Pagination Manager

A `PaginationManager` is a construct available in the `@mitter-io/core` module which makes it easy to consume paginated APIs (like `/v1/channels/{}/messages` , `/v1/channels`) etc.

For our message list we need to do the following:

1. Get the first page of messages whenever the component for a list of messages belonging to a particular channel is loaded.
2. Get the previous page from the scroll point that the user scrolls up to.

Since we are maintaining a list of messages in our parent component, we will initiate the process of fetching the first list of messages in it's `setChannels` method (which in turn is called by `componentDidMount`).

Let's first modify the state to store our `PaginationManager` (this is required because pagination managers maintain state regarding which page they are on and hence should be persisted across the component lifecycle):

{% code title="src/App.js" %}

```javascript
this.state = {
    channelMessages: {},
    channelMessageManagers: {}
}

```

{% endcode %}

Now when we get a list of channels, we want to create a pagination manager for the messages of each channel:

{% code title="src/App.js" %}

```javascript
participatedChannels.forEach((participatedChannel) => {
    channelMessages[participatedChannel.channel.identifier] = []
    
    // Add the code below
    channelMessageManagers[participatedChannel.channel.identifier] =
        this.props.mitter.clients().messages()
            .getPaginatedMessagesManager(participatedChannel.channel.identifier)
});

```

{% endcode %}

And then we'll write a function to fetch the first page of messages for a given channel:

{% code title="src/App.js" %}

```javascript
// Make sure you've done the following in the constructor:
//    this.fetchPreviousPage = this.fetchPreviousPage.bind(this)

fetchPreviousPage(channelId) {
    let { channelMessageManagers } = this.state
    channelMessageManagers[channelId].prevPage().then(messageList => {
        const _list = this.state.channelMessages[channelId].slice()

        for (let i=0; i < messageList.length; i++) {
            _list.unshift(messageList[i]);
        }

        this.setState({
            channelMessages: {
                [channelId]: _list
            }
        })
    });
}

```

{% endcode %}

And then we'll call it on the first page load:

{% code title="src/App.js" %}

```javascript
Object.keys(channelMessageManagers).forEach((channelId) => {
    this.fetchPreviousPage(channelId);
})

```

{% endcode %}

Try reloading the page now and you'll see that the latest few messages (by default 45) are automatically loaded in the message window.

![](https://94728489-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LLZR00Qt6hZ5Vke2l2g%2F-LXXXuK56-DHRcfFiFJK%2F-LXY6t4PmGDryKFXQaX8%2Freact-scl-vid0.gif?alt=media\&token=02387924-0551-46d0-8a3d-220711be1516)

However, when we scroll to the top, no new messages are loaded. For that we need to make simply make sure that `this.fetchPreviousPage` is called when the user scrolls to the top. To do so, pass on the method as a prop down to the `MessageListManager` :

In App.js,

{% code title="src/App.js" %}

```javascript
<ChannelComponent
    mitter={this.props.mitter}
    channelMessages={this.state.channelMessages}
    selfUserId={this.props.loggedUser}
    fetchPreviousPage={this.fetchPreviousPage}
/>
```

{% endcode %}

Check the last prop that was added. And then pass this function to the `onEndCallback` prop in `MessageListManager`

{% code title="src/ChannelComponent.js" %}

```javascript
<MessageListManager
    messages={activeChannelMessages}
    defaultView={this.messageViewProducer.produceView}
    producers={[this.messageViewProducer]}
    onEndCallback={() => {
        this.props.fetchPreviousPage(this.state.activeChannel)
    }}
/>
```

{% endcode %}

And that's all you have to do. Try scrolling up the window:

![](https://94728489-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LLZR00Qt6hZ5Vke2l2g%2F-LXXXuK56-DHRcfFiFJK%2F-LXY6wOkROmgQeH7ksED%2Freact-scl-vid1.gif?alt=media\&token=827f8227-0d11-44d9-8820-ce18cfa356ef)

### Adding a loader

If you tried out the application, you'd notice that when the user scrolls up there is a small delay before the previous messages are loaded. While this is happening, you might want to show a small loading icon/progress bar to the user. To do so, you have to set two props on the `MessageListManager`

{% code title="src/ChannelComponent.js" %}

```javascript
<MessageListManager
    messages={activeChannelMessages}
    defaultView={this.messageViewProducer.produceView}
    producers={[this.messageViewProducer]}
    onEndCallback={() => {
        this.props.fetchPreviousPage(this.state.activeChannel)
    }}
    isLoading={this.state.isLoading}
    loader={() => <Loader />}
/>

```

{% endcode %}

Whenever `isLoading` is true, the message list will display the component returned by `loader` in the top area where the new messages would be appended.


# For react-native

## Setup

To start developing apps with react native, there are two different phases of setup required:

1. Setting up the npm project
2. Setting up each target platform (currently only iOS and android are supported)

To make the setup easier and to get started quicker, you can also use the `react-native-starter-app` as your starting point. To do so, simply clone the repo from:

```
git clone https://github.com/mitterio/react-native-starter.git
```

And then run the following to install all the dependencies:

```
yarn install
```

Do note that you will still have to setup your credentials with APNs and FCM before you can start consuming messages.

### Setting up the npm project

To setup the npm project, you simply need to add the dependencies for your project using yarn or npm:

```
yarn install @mitter-io/react-native @mitter-io/models
```

Or using npm,

```
npm install --save @mitter-io/react-native @mitter-io/models
```

#### Setting up `react-native-firebase`

For messaging support the react-native sdk uses `react-native-firebase`, specifically its Cloud Messaging Module. To setup the same, follow the steps as outlined on <https://rnfirebase.io/docs/v5.x.x/installation/android>

**NOTE** If you are using the starter app as a base template, you can skip the section **Install Modules** in the link above.

### Setting up individual platforms

Since cloud messaging works at a lower level than most apps, setting up the Mitter.io SDK for react native requires further setup on each of the target platforms.

#### iOS

1. Initial setup <https://rnfirebase.io/docs/v5.x.x/installation/ios>
2. Cloud messaging setup <https://rnfirebase.io/docs/v5.x.x/messaging/ios>

**NOTE** If you are using the starter app as a base template, you can skip the section **Install Modules** in both of the above links.

#### Android

1. Initial setup <https://rnfirebase.io/docs/v5.x.x/installation/android>
2. Cloud messaging setup <https://rnfirebase.io/docs/v5.x.x/messaging/android>

### **Additional setup (for some optional components)**

The native-sdk uses `rn-fetch-blob` for managing file upload&#x73;**.**  Performing a link on the project is usually required before being able to use this package:

`react-native link rn-fetch-blob`&#x20;

Doing the above and adding permissions from <https://github.com/joltup/rn-fetch-blobshould> should just work for you.

If you are facing installation problems please refer to the **installation** section in the following repo:

{% embed url="<https://github.com/joltup/rn-fetch-blob>" %}

### **Additional setup (for the starter app)**

#### React Navigation

The setup for react navigation is given in the below link

{% embed url="<https://reactnavigation.org/docs/en/getting-started.html>" %}

#### React Native Elements

The starter app uses the community developed react-native-elements package as a UI toolkit

The setup for react-native-elements is given in the below link

{% embed url="<https://react-native-training.github.io/react-native-elements/docs/getting_started.html>" %}

## Usage

React native has the exact same usage as other Javascript SDKs apart from the fetching the mitter.io object:

```javascript
const mitter = Mitter.forReactNative(/* your application-id */)
```

#### Using the SDK with containerized Mitter.io

If you are using the Mitter.io docker container, you will have to override the API url:

{% code title="app.js" %}

```javascript
import { Mitter } from '@mitter-io/react-native'

const mitter = Mitter.forReactNative({
    applicationId: 'fb70ff76-ea33-4bb0-bd59-90853f103202', /* provide your application id here */
    mitterApiBaseUrl: '<mitter-api-url>' /* look below for the values */
}, {
    onTokenExpire: [] /* Your onTokenExpire functions */
})

```

{% endcode %}

For the `<mitter-api-url>`, use the following value:

* If you're using the cloud hosted solution, you can omit the `mitterApiBaseUrl` key in the config or explicitly set it to `https://api.mitter.io`
* If you're running it as a docker container set it to `http://localhost:<port>` where the port is port forwarded by docker for `11902`. To find out which port it is, run the following command `docker port $(docker ps --filter expose=11901-11903/tcp --format "")`

Once you have the mitter object for React Native, you can continue from the [Usage and user authorization](/sdks/web#usage-and-user-authorization) section in the Javascript reference.


# For node.js

### Setup

The node.js setup follows a very similar approach to setting up a js project for web. The differences between the node.js setup and browser setup are:

1. The node.js SDK is meant to be used with an application principal i.e. it is supposed to be used with an access key/access secret pair. It does not support user-based authorization for performing API calls.
2. The node.js SDK does not support any messaging pipelines and is only designed to provide a service layer to mitter.io since it is designed to be used for projects implementing an application backend.
3. Do note that with a websocket polyfill, you can still use `@mitter-io/web` in your node.js project, but this is currently not supported. In future versions we will be documenting the usage of the `@mitter-io/web` package within a node.js application.

To install the node.js SDK, add the following dependencies in your application

```
yarn add @mitter-io/node @mitter-io/models

```

or with npm

```
npm install @mitter-io/node @mitter-io/models
```

To initialize a mitter object, use the following:

```javascript
import { Mitter } from '@mitter-io/node'

const mitter = Mitter.forNode({
    accessKey: {
        accessKey: 'your-access-key',
        accessSecret: 'your-accesss-ecret'
    },
    applicationId: 'fb70ff76-ea33-4bb0-bd59-90853f103202', /* your application id */
    mitterApiBaseUrl: '<mitter-api-base-url>' /* look for values below */
}, {
    mitterInstanceReady: () => { /* your code here */ }
})

```

Do note that the access key object is not the direct JSON you get when you press on `Copy to clipboard` on the mitter.io panel. That follows a structure with an additional level of structuring and is used for file-based credential loading by other SDKs (for example, Java)

For the `<mitter-api-url>`, use the following value:

* If you're using the cloud hosted solution, you can omit the `mitterApiBaseUrl` key in the config or explicitly set it to `https://api.mitter.io`
* If you're running it as a docker container set it to `http://localhost:<port>` where the port is port forwarded by docker for `11902`. To find out which port it is, run the following command `docker port $(docker ps --filter expose=11901-11903/tcp --format "")`

Once you have the mitter.io object, most of the operations are similar to using the web package. For instance, a common use-case is to get tokens for a user in your application.

### Implementing a Token Server

An example project for this is located on our [public gitlab repository](https://git.mitter.io/mitter-io/mitter-showcase/mitter-token-server)

In our getting started sections we hard-coded our user authentication tokens directly in our code. It goes without saying that it is not a great practice for production applications. To implement a token service, what we first need is a repository of our users and optionally some mapping of their credentials. For this example, we'll use a simple JS dictionary:

```javascript
const Users = {
  'user-0001': {
    name: 'John Doe',
    credentials: {
      username: 'john',
      password: 'password'
    }
  },

  'user-0002': {
    name: 'BoJack Horseman',
    credentials: {
      username: 'bojack',
      password: 'passphrase'
    }
  }
}

```

What we also need the backend to do is create the user in mitter.io if it doesn't exist. To make this resilient, we'll model this around get-or-create semantics. What our service will have is:

1. Have a `/login` endpoint that accepts a request with parameters `{username: '', password: ''}` and authenticates it against the store.
2. If a user is authenticated, it creates a user in mitter.io. If the request to create a user fails due to the user already existing, we will ignore the error. Any other error will be reported in our request as a `500.`
3. We will then fetch a user token for the user and return this to the front-end along with the mitter.io user id we got from the previous step.

{% code title="index.js" %}

```javascript
const userAuthClient = mitter.clients().userAuth()
const userClient = mitter.clients().users()

router.post('/login', async function(req, res, next) {
  const { username, password } = req.body;

  const userFound = Object.keys(Users).find((userId) => {              // [1]
    const { username: targetUsername, password: targetPassword } =
        Users[userId].credentials;

    return username === targetUsername && password === targetPassword;
  })

  if (userFound) {
    const createUser = userClient.createUser({                         // [2]
      userId: userFound,
      userLocators: [],
      systemUser: false,
      screenName: {
        screenName: Users[userFound].name
      }
    })
    .catch((e) => {
      if (!(e.response.status === 409 &&
            e.response.data.errorCode === 'duplicate_entity')) {        // [3]
        throw e
      }
    })

    createUser.then(() => userAuthClient.getUserToken(userFound))        // [4]
        .then(token => {
          loginSuccessfulResponse(res, userFound, token.userToken.signedToken)
        })
        .catch(e => {
          console.error('Error executing request, setting 500', e)
          res.sendStatus(500)
        })
  } else {
    res.sendStatus(401);
    return Promise.resolve();
  }
});

```

{% endcode %}

An explanation of the code above:

1. We first iterate over our user mapping to check if a user with the given credentials exists. In a production application, this would connect to your database, ldap or any other user storage you might be using.
2. If the user is found, then we create a user with the same id in mitter.io. We don't have to specify a user id, or use the same user id at all, but usually having the same user id makes things simple. It is recommended that you map your domain ids directly as mitter.io ids or map them with some prefix. If a user is not found, we simply return a `401` saying that the user was not authenticated.
3. The create user request might fail in case the user was already created. Whenever any operation fails due to a unique-constraint violation, mitter.io will return a `409 CONFLICT` HTTP status and the response body will have a field `errorCode` set to `duplicate_entity`. Refer to the platform reference for a complete list of error scenarios and their respective error codes and HTTP status codes.
4. If we haven't encountered an error so far, it means that a user with the same id as was mapped to the username provided to our token server as a request exists in mitter.io and the provided credentials have matched. We then use the `userAuthClient` to fetch a user access token for the user and then send a successful login response to the frontend. The `loginSuccessfulResponse` could be any function that combines the the user information, the mitter.io user information and any additional data and passes on this data to the fronted. An example would look like:

```javascript

function loginSuccessfulResponse(res, userId, userToken) {
  res.status(200).send({
    mitterUserId: userId,
    mitterUserAuthorization: userToken
  })
}

```


# TSDocs / JSDocs

All `@mitter-io` packages publish generated documentation for their source. The links for the same are listed below:

**NOTE** `@mitter-io/web` package includes breaking changes that currently render all versions prior to AND including `0.5.10` in a non-working state. Please update your web package to `0.5.11-1` for all dependant packages.

| Package                                                                                     | Link to latest                                                                                                   | Other versions                                                                                                                                                                                                  |
| ------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **@mitter-io/core**[`npm🔗`](https://www.npmjs.com/package/@mitter-io/core)                 | [latest](https://s3.ap-south-1.amazonaws.com/mitter-sourcedocs/tsdocs/@mitter-io/core/latest/index.html)         | [0.5.10](https://s3.ap-south-1.amazonaws.com/mitter-sourcedocs/tsdocs/sdk-core/0.5.10/index.html), [0.6.32](https://s3.ap-south-1.amazonaws.com/mitter-sourcedocs/tsdocs/@mitter-io/core/0.6.32/index.html)\*   |
| **@mitter-io/web**[`npm🔗`](https://www.npmjs.com/package/@mitter-io/web)                   | [latest](https://s3.ap-south-1.amazonaws.com/mitter-sourcedocs/tsdocs/@mitter-io/web/latest/index.html)          | [0.5.11-1](https://s3.ap-south-1.amazonaws.com/mitter-sourcedocs/tsdocs/sdk-web/0.5.11-1/index.html), [0.6.32](https://s3.ap-south-1.amazonaws.com/mitter-sourcedocs/tsdocs/@mitter-io/web/0.6.32/index.html)\* |
| **@mitter-io/node**[`npm🔗`](https://www.npmjs.com/package/@mitter-io/node)                 | [latest](https://s3.ap-south-1.amazonaws.com/mitter-sourcedocs/tsdocs/@mitter-io/node/latest/index.html)         | [0.5.10](https://s3.ap-south-1.amazonaws.com/mitter-sourcedocs/tsdocs/sdk-node/0.5.10/index.html), [0.6.32](https://s3.ap-south-1.amazonaws.com/mitter-sourcedocs/tsdocs/@mitter-io/node/0.6.32/index.html)\*   |
| **@mitter-io/models**[`npm🔗`](https://www.npmjs.com/package/@mitter-io/models)             | [latest](https://s3.ap-south-1.amazonaws.com/mitter-sourcedocs/tsdocs/@mitter-io/models/latest/index.html)       | [0.5.10](https://s3.ap-south-1.amazonaws.com/mitter-sourcedocs/tsdocs/models/0.5.10/index.html), [0.6.32](https://s3.ap-south-1.amazonaws.com/mitter-sourcedocs/tsdocs/@mitter-io/models/0.6.32/index.html)\*   |
| **@mitter-io/react-native**[`npm🔗`](https://www.npmjs.com/package/@mitter-io/react-native) | [latest](https://s3.ap-south-1.amazonaws.com/mitter-sourcedocs/tsdocs/@mitter-io/react-native/latest/index.html) | [0.6.32](https://s3.ap-south-1.amazonaws.com/mitter-sourcedocs/tsdocs/@mitter-io/react-native/0.6.32/index.html)\*                                                                                              |
| **@mitter-io/react-scl**[`npm🔗`](https://www.npmjs.com/package/@mitter-io/react-scl)       | [latest](https://s3.ap-south-1.amazonaws.com/mitter-sourcedocs/tsdocs/@mitter-io/react-scl/latest/index.html)    | [0.6.32](https://s3.ap-south-1.amazonaws.com/mitter-sourcedocs/tsdocs/@mitter-io/react-scl/0.6.32/index.html)\*                                                                                                 |

&#x20;\* *Latest version*

### **Using the docs**

The `@mitter-io/models` contains the shape for all of the objects used across the SDK. When facing a method signature/class definition, any referring types usually can be found in the `models` package. For example, in `@mitter-io/core`, in the [`getMessages`](https://s3.ap-south-1.amazonaws.com/mitter-sourcedocs/tsdocs/@mitter-io/core/latest/classes/messagesclient.html#sendmessage) call the doc looks like:

![](https://94728489-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LLZR00Qt6hZ5Vke2l2g%2F-LOXU6qcGmHYHqEgT0Aw%2F-LOXcpfrndDtXqX3XTJD%2Fusing-docs-001.png?alt=media\&token=4d923bf7-b9d0-4be4-a79f-c9d0bfd79dec)

\
The referring type `ChannelReferencingMessage` can be found in the `@mitter-io/models` docs for [`ChannelReferencingMessage`](https://s3.ap-south-1.amazonaws.com/mitter-sourcedocs/tsdocs/@mitter-io/models/latest/classes/channelreferencingmessage.html).

Usually, you'd get an instance of type or an extension of `MitterBase` (as defined in `@mitter-io/core`) by using one of the static methods for your platform:

{% tabs %}
{% tab title="node.js" %}

```
import { Mitter } from '@mitter-io/node'

const mitter = Mitter.forNode('.. application id',
    {
        accessKey: '..your access key..',
        accessSecret: '..your access secret..'
    }
)

```

The documentation for the `forNode` function can be found [here](https://s3.ap-south-1.amazonaws.com/mitter-sourcedocs/tsdocs/@mitter-io/node/latest/globals.html#mitter)
{% endtab %}

{% tab title="web" %}

```
import { Mitter } from '@mitter-io/web'

const mitter = Mitter.forWeb('.. your application id ..')

```

The `forWeb` method is documented [here](https://s3.ap-south-1.amazonaws.com/mitter-sourcedocs/tsdocs/@mitter-io/web/latest/globals.html#mitter)
{% endtab %}

{% tab title="react-native" %}

```
import { Mitter } from '@mitter-io/react-native'

const mitter = Mitter.forReactNative('.. your application id ..')

```

The `forReactNative` method is documented [here](https://s3.ap-south-1.amazonaws.com/mitter-sourcedocs/tsdocs/@mitter-io/react-native/latest/globals.html#mitter)
{% endtab %}
{% endtabs %}

Both the methods as shown above return a platform specific extension of `MitterBase` which is defined in `@mitter-io/core`. The exact operations available based on the platform can be referred here:

1. For node.js - `MitterBase` [Reference TSDocs](https://s3.ap-south-1.amazonaws.com/mitter-sourcedocs/tsdocs/@mitter-io/core/latest/classes/mitterbase.html)
2. For web and react-native - `Mitter` [Reference TSDocs](https://s3.ap-south-1.amazonaws.com/mitter-sourcedocs/tsdocs/@mitter-io/core/latest/classes/mitter.html)

Specific clients can be fetched on both of these objects using the `Mitter.clients()` method which returns a `MitterClientSet` from which different clients can fetched for performing mitter.io API calls. You can refer the *methods* section in [this page](https://s3.ap-south-1.amazonaws.com/mitter-sourcedocs/tsdocs/@mitter-io/core/latest/classes/mitterclientset.html) to get a list of clients that are returned by the client set.

> **NOTE** We currently do not automatically hyperlink types across the different packages. We are working on a solution towards this problem and will try to publish the new docs with hyperlinks soon. We apologize for the inconvenience.


# For Typescript Users

mitter.io SDKs are compiled with typescript 3.0.3, but target typescript 2.7.+ Versions of typescript below 2.7 might work, but are not supported.

All mitter.io web/javacsript SDKs are written in typescript and as such are bundled with typings automatically. This section details on how to optimally use the mitter.io libraries if you are using typescript.

### Type-matching functions

If you are using type-matching functions to get the type of a payload for example, the functions are implemented as predicates, so typescript will automatically cast it within the branch. For example:

```javascript
mitter.subscribeToPaylod(payload => {
    if (payload['@type'] === 'NewMessagePayload') {
        // The next line will throw an error, since the type of
        // payload is MessagingPipelinePayload
        console.log('New message', payload.message.textPayload)
    } else {
        // The next will not throw an error, since the callback
        // argument is already typed to MessagingPipelinePayload
        console.log('New payload', payload.globalPipelinePayloadId)
    }
})

```

Instead, if you were to use the bundled type-matching functions:

```javascript
mitter.subscribeToPayload(payload => {
    if (isNewMessagingPayload(payload)) {
        // The next line is OK, since the isNewMessagingPayload is a type
        // predicate, which tells the compiler that if true, then the
        // argument (payload) was of type NewMessagingPayload
        console.log('New message', payload.message.textPayload)
    }
})
```

### API calls using clients

All bundled clients are typed and will throw an error when using incorrect request/response values

```javascript
mitter.clients().channels().newChannel({
    // ERROR. Missing property 'defaultRuleSet'
    channelId: 'my-new-channel'
})

mitter.clients().channels().newChannel(new Channel(...))
    // ERROR. No property `wrongProperty` on type Channel
    .then(x => console.log('New channel', channel.wrongProperty)
```

> **NOTE** The above does not apply if you are using fetch and/or axios to make API calls. The internal clients that are used with [restyped](https://github.com/rawrmaan/restyped) are exposed in case you wish to use them with your own axios clients. Do refer to the ts-docs bundled with `@mitter-io/core` on how to access these objects.


# Java (Backend)

The Java SDK for backends

### Introduction

The Java SDK is a backend SDK that can be used to communicate with Mitter.io. It provides the following high level functionalities:

1. Create and delete Users&#x20;
2. Create and delete Channels
3. Get and send Messages
4. Get and send Timeline Events

Being a backend SDK, the Java SDK connects with Mitter.io via HTTP only.

### Getting the SDK

The Java SDK is available on jcenter. Add this to you `build.gradle`:

```groovy
repositories {
    jcenter()
}

dependencies {
    compile group: 'io.mitter', name: 'java-sdk', version: '0.5.5'
}
```

### Configuring the Client

The main point of access is the `MitterCentralClientFactory`. Create the factory like so:

```java
import io.mitter.sdk.java.MitterCentralClientFactory;

MitterCentralClientFactory factory = new MitterCentralClientFactory(
        new MitterApplicationAccessKeyCredentials(
                "my-application-access-key", 
                "my-application-access-secret"
        )
);
```

This will authenticate you with an Application Principal. This is the most common type of credential you will need when you make calls from your backend.

You can also authenticate as:

1. The Subscriber (the entity you created you Mitter.io account with) using `MitterSubscriberApiAccessCredentials`
2. A User, using `MittterUserTokenCredentials`
3. Anonymously, using `MitterAnonymousCredentials`

Do note that each principal has restricted access to APIs. A Subscriber, for instance, cannot send Messages to a Channel, and an Application cannot create other Applications.

Refer to the Platform Reference docs for the full list of operations each Principal can perform.

### Access Mitter.io

You can access the different `clients` using `MitterCentralClientFactory`

**User Operations**

Users operations are done through the `MitterUsersClient`, as follows:

```java
mitterCentralClientFactory.usersClient()
        .newUser(
                new User(
                    "princess-carolyn", //the userId
                    new ScreenName()
                        .setScreenName("Princess Carolyn")
                )
        );
```

**Channel Operations**

Channel operations are performed using the `MitterChannelsClient`, which can be acquired and used as follows:

```java
mitterCentralClientFactory.channelsClient()
        .newChannel(
                new Channel(
                    "bojack-intervention", //the channelId
                    "io.mitter.ruleset.chats.GroupChat", //the group chat ruleset
                    Lists.newArrayList(
                        new ChannelParticipation(IdUtils.of("princess-carolyn", User.class)), //Converting a string to mitter Identifiable
                        new ChannelParticipation(IdUtils.of("mister-peanutbutter", User.class))        
                    )
                    false // whether systemChannel or not
                )
        );
```

**Message Operations**

Send Messages using the `MitterMessagesClient`:

```java
mitterCentralClientFactory.messagesClient()
        .sendMessageToChannel(
                //Channel id
                IdUtils.of("bojack-intervention", Channel.class),

                new Message(
                        //message id
                        "message-1",

                        //Sender id
                        IdUtils.of("intervention-coordinator", User.class),

                        //Text of the message
                        "Welcome to BoJack's Intervention. Grab some popcorn",

                        //A list of TimelineEvents. You can send as many as you want
                        List.newArrayList(

                                //mitter mandates that the SentTime MUST be sent
                                new TimelineEvent(
                                        "event-id-1",
                                        StandardTimelineEventTypeNames.Messages.SentTime,
                                        new Date().getTime(),
                                        IdUtils.of("intervention-coordinator", User.class)
                                )
                        )
                )
        )
```

Send custom payloads:

```java
mitterCentralClientFactory.messagesClient()
        .sendMessageToChannel(

                //Channel id
                IdUtils.of("bojack-intervention", Channel.class),

                new Message(
                        //message id
                        "message-2",

                        IdUtils.of("intervention-coordinator", User.class),


                        //A text representation is mandatory, even for custom payload messages
                        "Here's some entertainment meanwhile",

                        //Send custom payloads
                        Lists.newArrayList(
                                new MessageDatum(
                                        // Custom data type
                                        "embarassing-bojack-story",

                                        // Send any JSON
                                        new JsonNode()
                                ),
                                new MessageDatum(
                                        "baby-bojack-pics",
                                        new JsonNode()
                                )
                        ),

                        List.newArrayList(
                                new TimelineEvent(
                                        "event-id-1",
                                        StandardTimelineEventTypeNames.Messages.SentTime,
                                        new Date().getTime(),
                                        IdUtils.of("intervention-coordinator", User.class)
                                )
                        )
                )
        )
```

Get very specific messages with a query:

```java
mitterCentralClientFactory.getMessagesClient()
        .getMessagesFromChannel(channelId,

                // Fetch query for messages
                MitterMessagesHelper.messageQuery(
                    // No. of messages to fetch    
                    50,

                    // 'beforeId': Fetch only before this message
                    IdUtils.of("message-id-100", Message.class),

                    // 'afterId': Fetch only after this message 
                    IdUtils.of("message-id-50", Message.class),


                    // fetch messages with any of these payloadTypes only
                    Lists.newArrayList(
                            "intervention-time-update"
                    )
                )
        )
```

**Other Operations**

The SDK has a few other clients for operations on Timeline Events, User Presence, Channel Streams, etc.

The APIs are very similar to the ones shown above, and you should have no problem navigating through them.

**High Level Clients**

Certain APIs on Mitter.io are paginated, like Channels and Messages. So when you do something like `messagesClient.getMessagesFromChannel(channelId)` you will only get the default 50 messages, and to get more messages, you will have to build a query and call `getMessagesFromChannel`.

The SDK makes it easy to use these paginated APIs without worrying about the pagination scheme or maintaining the pagination tokens, by providing high level clients.

The `MitterMessagesHlcClient` provides the following convenience functions:

* `getAllExistingMessages(channelId)`
* `getAllMessagesAfter(channelId, messageId)`
* `getAllMessagesBefore(channelId, messageId)`

You don't need to worry about these functions being intensive operations, because they provide lazy lists.

High Level Clients return `Iterator`s, so more pages will not be fetched unless you iterate and reach the end.

### Notes

* The SDK is itself written in Kotlin, and fully supports most JVM languages
* To be able to send JSON in custom payloads, the SDK uses `JsonNode` from Jackson


