Conversations endpoints

Method HTTP request Description
Get all conversations GET /conversations Get all conversations
Get a conversation GET /conversations/:conversation_uuid Get a single conversation
Create a conversation POST /conversations Start a new conversation
Update participants PATCH /conversations/:conversation_uuid Add or remove participants
Update metadata PATCH /conversations/:conversation_uuid Change metadata values
Delete a conversation DELETE /conversations/:conversation_uuid Mark a conversation as deleted
Mark a Conversation Read POST /conversations/:conversation_uuid/mark_all_read Set all messages in a conversation read.

Get all conversations

Get the first page of conversations (most recent 100, by default)

Parameters:

Name Type Description
page_size integer (optional) Number of results to return; 100 is default and max
from_id string (optional) Get the Conversations logically sorted after this ID (by default, this corresponds to Conversations chronologically before this ID). Can be passed as a Layer URI layer:///conversations/uuid or simply a UUID
sort_by string (optional) Either created_at to sort by Conversation creation time (newest first), or last_message to sort by most recently sent message (newest first)

HTTP request:

GET/conversations

Example:

"sort_by=last_message&page_size=50&from_id=layer:///conversations/UUID"
curl -X GET \
     -H 'Accept: application/vnd.layer+json; version=3.0' \
     -H 'Authorization: Layer session-token="<TOKEN>"'' \
     -H 'Content-Type: application/json' \
     https://api.layer.com/conversations?sort_by=last_message&page_size=50&from_id=layer:///conversations/UUID

Possible responses:

Request successful | Status: 200 (OK)

[
    Conversation,
    Conversation
]

Discussion

The response will include a header indicating the total number of results available to page through:

Layer-Count: 4023

Note

Conversations in which you used to be a participant will be listed by this endpoint. You will only see messages and metadata up to the point where you stopped being a participant. The participants property will be empty ([]) if you are no longer a participant.

Sorting

The default sort is by created_at. This is done because this is a fixed ordering, so that paging can be done without Conversations moving around while paging. This means developers do not need to worry about missed Conversations and changes in ordering of already loaded results. However, the sort_by parameter allows you to request a sort by most recently active conversations instead. If a conversation does not yet have any messages, it is sorted using its created_at. This means that a Conversation without any messages can be sorted ahead of a Conversation that has not been active recently.

If you sort by last_message, the results can change while paging. We recommend the following to deal with this:

  • If you have a WebSocket connection and receive a message creation event for a Conversation, that Conversation is now the most recent.
    • If the Conversation is already loaded, move it to the top of your list. If it is not loaded, load it and insert it at the top of your list.
  • Alternatively, if you always need to maintain a correctly sorted list, you can listen for all update events on a WebSocket connection that change a Conversation’s last_message property.
    • If the Conversation isn’t yet loaded, load it. If it is loaded, the last_message may have changed, so it must be sorted into the correct position in the list.
  • Any Conversation creation events on a WebSocket connection will make the new Conversation the most recent, and it should be inserted at the top of the list. This is true even if the new Conversation doesn’t have any messages.

Get a conversation

Parameters:

This method takes no parameters.

HTTP request:

GET/conversations/:conversation_uuid

Example:

curl -X GET \
     -H 'Accept: application/vnd.layer+json; version=3.0' \
     -H 'Authorization: Layer session-token="<TOKEN>"'' \
     -H 'Content-Type: application/json' \
     https://api.layer.com/conversations/<conversation_uuid>

Possible responses:

Request successful | Status: 200 (OK)

Conversation

Create a conversation

Parameters:

Name Type Description
participants string[] Array of Identity IDs (layer:///identities/UserID) identifying initial participants
distinct boolean Whether this Conversation should be distinct
metadata object (optional) Arbitrary key/value pairs representing the initial metadata
id string (optional) UUID or Layer ID, used for deduplication

HTTP request:

POST/conversations

Example:

{
    "participants": [
        "layer:///identities/1234",
        "layer:///identities/5678"
    ],
    "distinct": false,
    "metadata": {
        "background_color": "#3c3c3c"
    }
}
curl -X POST \
     -H 'Accept: application/vnd.layer+json; version=3.0' \
     -H 'Authorization: Layer session-token="<TOKEN>"'' \
     -H 'Content-Type: application/json' \
     https://api.layer.com/conversations\
 -d '{"participants":["layer:///identities/1234","layer:///identities/5678"],"distinct":false,"metadata":{"background_color":"#3c3c3c"}}'

Possible responses:

Conversation created successfully | Status: 201 (Created)

Conversation

Duplicate conversation already exists | Status: 409 (Conflict)

{
    "id": "id_in_use",
    "code": 111,
    "message": "The requested Conversation already exists",
    "url": "http://docs.layer.com/reference/client_api#create-a-conversation",
    "data": Conversation
}

Matching distinct conversation | Status: 200 (OK)

Conversation

Partially matching distinct conversation | Status: 409 (Conflict)

{
    "id": "conflict",
    "code": 108,
    "message": "The requested Distinct Conversation was found but had metadata that did not match your request.",
    "url": "https://developer.layer.com/api.md#creating-a-conversation",
    "data": Conversation
}

Participant blocked | Status: 422 (undefined)

{
    "id": "particiant_blocked",
    "code": "110",
    "message": "The conversation could not be created because at least one participant is blocked",
    "url": "https://developer.layer.com/api.md#creating-a-conversation"
}

Websocket request:

{
    "type": "request",
    "body": {
        "method": "Conversation.create",
        "request_id": "fred.flinstone.3",
        "data": {
            "participants": [
                "layer:///identities/1234",
                "layer:///identities/5678"
            ],
            "distinct": false,
            "metadata": {
                "background_color": "#3c3c3c"
            }
        }
    }
}

Success

{
    "name": "Success",
    "body": {
        "type": "response",
        "counter": 16,
        "timestamp": "2015-01-19T09:15:43+00:00",
        "body": {
            "request_id": "fred.flinstone.3",
            "method": "Conversation.create",
            "success": true,
            "data": Conversation
        }
    }
}

Error

{
    "name": "Error",
    "body": {
        "type": "response",
        "counter": 18,
        "timestamp": "2015-01-19T09:15:43+00:00",
        "body": {
            "request_id": "fred.flinstone.3",
            "method": "Conversation.create",
            "success": false,
            "data": {
                "code": 110,
                "id": "particiant_blocked",
                "message": "The conversation could not be created because at least one participant is blocked",
                "url": "https://developer.layer.com/api.md#creating-a-conversation"
            }
        }
    }
}

Deduplication error

{
    "name": "Deduplication error",
    "body": {
        "type": "response",
        "counter": 18,
        "timestamp": "2015-01-19T09:15:43+00:00",
        "body": {
            "request_id": "fred.flinstone.3",
            "method": "Conversation.create",
            "success": false,
            "data": {
                "id": "id_in_use",
                "code": 111,
                "message": "The requested Conversation already exists",
                "url": "http://docs.layer.com/reference/client_api#create-a-conversation",
                "data": Conversation
            }
        }
    }
}

Discussion

Conversations can be specified as distinct at creation time.

When attempting to create a new Distinct Conversation, your request may match an existing Distinct Conversation, if one of the following is true:

  • The metadata property was not included in the new request
  • The metadata property was included with a value of null in the new request
  • The metadata property is identical to the current metadata of the matching distinct Conversation

If one of these is true, you will receive the existing Conversation. If none of these conditions are true, we will return an error. This allows you to determine how you want to handle the difference in the Conversation metadata.

If you do not have an Identity ID, one can be obtained from a User ID coming from your User Mananagement system using:

var identityId = "layer:///identities/" + encodeURIComponent(myUserId);

This can then be used within your participants array.

Update participants

Parameters:

Name Type Description
operation string The type of operation, one of add or remove
property string Specify participants
id string Identity ID of the user to add/remove

HTTP request:

PATCH/conversations/:conversation_uuid

Example:

[
    {
        "operation": "add",
        "property": "participants",
        "id": "layer:///identities/user3"
    },
    {
        "operation": "remove",
        "property": "participants",
        "id": "layer:///identities/user2"
    }
]
curl -X PATCH \
     -H 'Accept: application/vnd.layer+json; version=3.0' \
     -H 'Authorization: Layer session-token="<TOKEN>"'' \
     -H 'Content-Type: application/vnd.layer-patch+json' \
     https://api.layer.com/conversations/<conversation_uuid>\
 -d '[{"operation":"add","property":"participants","id":"layer:///identities/user3"},{"operation":"remove","property":"participants","id":"layer:///identities/user2"}]'

Possible responses:

Participants updated successfully
Status: 204 (No Content)
(Empty body)

Discussion

Note

Updating the participants of a Distinct Conversation will make it a Non-Distinct Conversation

Update metadata

You can change or delete metadata by key, or replace the entire structure

Parameters:

Name Type Description
operation string The type of operation, one of set, or delete
property string The property to change; can use . to access nested properties
value string object

HTTP request:

PATCH/conversations/:conversation_uuid

Example:

[
    {
        "operation": "set",
        "property": "metadata.a.b.count",
        "value": "42"
    },
    {
        "operation": "set",
        "property": "metadata.a.b.word_of_the_day",
        "value": "Aglet"
    }
]
curl -X PATCH \
     -H 'Accept: application/vnd.layer+json; version=3.0' \
     -H 'Authorization: Layer session-token="<TOKEN>"'' \
     -H 'Content-Type: application/vnd.layer-patch+json' \
     https://api.layer.com/conversations/<conversation_uuid>\
 -d '[{"operation":"set","property":"metadata.a.b.count","value":"42"},{"operation":"set","property":"metadata.a.b.word_of_the_day","value":"Aglet"}]'

Possible responses:

Metadata updated successfully
Status: 204 (No Content)
(Empty body)

Discussion

The example above uses a dot (.) to access nested keys. This example will create or set the count and word_of_the_day properties of b to “42”, and “Aglet”, respectively. It will create intermediate structures as needed to accomplish this (objects a and b will be created if they don’t exist).

Note

Only string values are allowed in metadata, so the value 42 must be passed as a string.

It is also possible to replace the entire metadata structure:

[
  { "operation": "set", "property": "metadata", "value": { "a": "b" }}
]

This increases the chance of conflicts if other clients are changing metadata at the same time. As a result, we recommend avoiding doing this.

Delete a conversation

Parameters:

Name Type Description
mode string Possible values are all_participants or my_devices
leave boolean Deleting a Conversation without leaving will cause the Conversation to be reinstated if more Messages are sent from other participants. Only applies to mode = my_devices

HTTP request:

DELETE/conversations/:conversation_uuid

Example:

curl -X DELETE \
     -H 'Accept: application/vnd.layer+json; version=3.0' \
     -H 'Authorization: Layer session-token="<TOKEN>"'' \
     -H 'Content-Type: application/json' \
     https://api.layer.com/conversations/<conversation_uuid>?mode=all_participants

Possible responses:

Conversation deleted
Status: 204 (No Content)
(Empty body)

No Conversation with that ID | Status: 404 (Not Found)

{
    "id": "not_found",
    "code": 102,
    "message": "A Conversation with the specified identifier could not be found.",
    "url": "https://developer.layer.com/reference/api#conversations"
}

The specified Conversation may have already been deleted, or never existed.

Access denied | Status: 403 (Forbidden)

{
    "id": "access_denied",
    "code": 101,
    "message": "You are no longer a participant in the specified Conversation.",
    "url": "https://developer.layer.com/reference/api#conversations"
}

The user is not currently a participant with permission to delete the Conversation. If the user was never a participant, a 404 will be returned instead.

Mark a Conversation Read

Parameters:

Name Type Description
position number (optional) The position of the latest message to be marked as read. That message and all earlier ones will be affected. If null or omitted, this defaults to the most recent message.

HTTP request:

POST/conversations/:conversation_uuid/mark_all_read

Example:

{
    "position": 102837410234
}
curl -X POST \
     -H 'Accept: application/vnd.layer+json; version=3.0' \
     -H 'Authorization: Layer session-token="<TOKEN>"'' \
     -H 'Content-Type: application/json' \
     https://api.layer.com/conversations/<conversation_uuid>/mark_all_read\
 -d '{"position":102837410234}'

Possible responses:

Conversation marked read successfully | Status: 202 (Accepted)

{
    "position": 102837410234
}

No Conversation with that ID | Status: 404 (Not Found)

{
    "id": "not_found",
    "code": 102,
    "message": "A Conversation with the specified identifier could not be found.",
    "url": "https://developer.layer.com/reference/api#conversations"
}

The specified Conversation may have already been deleted, or never existed.

Websocket request:

{
    "type": "request",
    "body": {
        "method": "Conversation.mark_all_read",
        "request_id": "fred.flinstone.3",
        "conversation_uuid": "fred.flinstone.3",
        "data": {
            "position": 102837410234
        }
    }
}

Success

{
    "name": "Success",
    "body": {
        "type": "response",
        "counter": 16,
        "timestamp": "2015-01-19T09:15:43+00:00",
        "body": {
            "request_id": "fred.flinstone.3",
            "method": "Conversation.mark_all_read",
            "success": true,
            "data": {
                "position": 102837410234
            }
        }
    }
}

Error

{
    "name": "Error",
    "body": {
        "type": "response",
        "counter": 18,
        "timestamp": "2015-01-19T09:15:43+00:00",
        "body": {
            "request_id": "fred.flinstone.3",
            "method": "Conversation.mark_all_read",
            "success": false,
            "data": {
                "code": 102,
                "id": "not_found",
                "message": "The Conversation could not be found.",
                "url": "https://developer.layer.com/"
            }
        }
    }
}

Discussion

When position is omitted or null, all messages in the conversation are marked as read. This can create a race condition if there are new messages on the server that the client hasn’t seen yet, so use an expliction position if that case concerns you.