Milestone XProtect WebSockets Messaging API (1.0.0)

Introduction

Using the WebSockets Messaging API, you can publish and subscribe to messages sent across clients, servers and standalone services. To get started quickly with the WebSockets Messaging API, refer to our comprehensive quickstart quide. To write your own protocol integration, dive into the full API reference.

⚠️ The WebSockets Messaging API is currently in beta version. Future releases of this API might break backwards compatibility.

What is a message?

A message is an arbitrary JSON object that any client can publish to a topic.

What is a topic?

A topic is like a channel where clients to which clients can publish, subscribe and receive messages from. Each topic is identified by a string. All clients subscribed to a topic receive all messages published to the topic. If you subscribe and publish to a topic, you will also receive your own messages.

⚠️ You should use unique topics to avoid collisions with other plugins. You may use uuids or the following format: company-name.plugin-name.message-name.

Authorization

All clients with a valid IDP token can publish messages and subscribe to all topics.

Quickstart

Requirements

To use the WebSockets Messaging API, you must have:

  • A system running XProtect 2023 R3 or later. You are recommended to set up a small separate system for development. If you don't have one, install XProtect following the Get started guide.

  • A Basic user or Windows user. If you don't have one, go to Create basic users.

⚠️ User credentials, bearer tokens, and other sensitive data are transmitted in cleartext if you do not set up certificates and use HTTPS. Make sure that your XProtect system is set up with HTTPS in production.

C#

Follow this quickstart guide to publish and subscribe to messages with C#.

  1. Verify the requirements

    Check the initial Requirements.

  2. Check out the samples

    Clone the repository from GitHub and open PluginSamples.sln with your Visual Studio. The relevant project is ChatWithWebsockets.cproj

JavaScript

Follow this quickstart guide to publish and subscribe to messages with JavaScript.

  1. Verify the requirements

    Check the initial Requirements.

  2. Check out the samples

    Clone the repository from GitHub and open index.html with your browser.

API reference

Connect to the message communication service to start sending and receiving messages. To connect, start a secure WebSockets connection at wss://{api-gateway}/api/ws/messages/v1.

If you don't have certificates enabled, you can only use the unsecure endpoint: ws://{api-gateway}/api/ws/messages/v1.

If you had established a connection but it broke, reconnect using the sessionId and lastSeq query parameters.

Example

wss://test-01.example.com/api/ws/messages/v1?sessionId=359b13ac-2aa9-4924-8103-6bba01fecc19&lastSeq=123

Headers

Key Description
Authorization string (required)
An IDP token in bearer format: Bearer {token}

Example: Bearer eyJhbG...ZGhCy4rg

If you cannot set the Authorization header, you can also authenticate with the Auth command.

Query parameters

Key Description
sessionId uuid4
The id of the session that you received after connecting (see Hello). Use it to resume a session and receive all messages sent while you were not connected (up to a configurable time).

Example: 359b13ac-2aa9-4924-8103-6bba01fecc19
lastSeq int
The sequence number of the last message you processed. Use it to resume a session and receive all messages sent after the last one you processed (up to a configurable time). lastSeq must be greater or equal than the seq value you sent in the last Pulse.

Example: 123

Responses

Status Description
101 Upgrading to a websockets connection.
401 Invalid, expired or missing authorization token.
400 Invalid or expired session (messages cannot be recovered).

Session lifecycle

When you connect to the messaging service, a session is automatically initiated. You can disconnect from the service momentarily and reconnect using the same session id to recover all messages sent while you were offline.

Use sessions to handle both regular, forced and unexpected reconnections without loosing messages:

  • Regular disconnections happen at regular intervals (usually 1 hour)
  • Forced disconnections happen when the client does not send Pulse commands during two pulse periods or sends them but without acknowledging all the new messages
  • Unexpected disconnections can happen at any time due to external factors, such as network connectivity issues.

You should expect disconnects to happen normally and be ready to handle them.

Session lifecycle

  1. The client retrieves a token from the IDP
  2. The client starts a WebSockets connection
  3. The client receives the Hello command with the sessionId and pulsePeriodSeconds.
  4. The client starts periodically sending Pulse commands every pulsePeriodSeconds seconds.
  5. The server acknowledges all Pulse commands.
  6. The server sends messages to the client.
  7. Some messages are never received by the client after the connection is lost
  8. The client reconnects with with the session id and the seq of the last processed message.
  9. All lost messages are received upon connection, restarting the sequence from 0.

message sequence

Configuring sessions

The pulsePeriodSeconds is 15 seconds by default. Messages are preserved for up to 2 times the pulse period, and discarded after that if no successful reconnection attempt is performed.

A forced disconnect happens if you don't send a pulse command or you have not acknowledged old messages.

You can configure the pulse period in the Event Server configuration file. For manual testing (i.e. Postman) and debugging, it is useful to set it to a large value.

Hello

server → client Session id and pulse period received.

The server will send you a hello command when the connection is successful. It contains important information about your connection:

  • You MUST use the received pulsePeriodSeconds to start sending Pulse commands immediately.
  • You MAY use the received sessionId to reconnect in case of a broken connection.

Example

{
    "type": "hello.v1",
    "body": {
        "sessionId": "ecdad995-0869-49f7-a639-d47caa601f7f",
        "pulsePeriodSeconds": 15
    },
    "id": "3a564ea5-ef64-4215-9eba-9384f37489a6"
}

Command

Type

Key Description
type hello.v1

Body

Key Description
sessionId uuid4 (required)
The identifier of the Session. Use it to reconnect without losing messages.

Example: ecdad995-0869-49f7-a639-d47caa601f7f
pulsePeriodSeconds int (required)
The period (in seconds) in which you MUST send Pulse commands.

Example: 15

Id

Key Description
id uuid4 (required)
A unique identifier of the command.

Example: 3a564ea5-ef64-4215-9eba-9384f37489a6

Ack

server → client A command has been successfully processed by the server.

The server will answer every valid command you send with an ack command. Use it to make sure you have published, subscribed or unsubscribed successfully.

☑️ Due to the TCP timeout, it can be tricky to know whether a command was successfully processed before an eventual network connectivity issue. The ack command makes it easier for you.

Example

{
    "type": "ack.v1",
    "body": {
        "id": "3a564ea5-ef64-4215-9eba-9384f37489a6"
    },
    "id": "2aa04c78-15b0-49dc-967c-924b22deb25d"
}

Command

Type

Key Description
type ack.v1

Body

Key Description
id uuid4 (required)
The identifier of a command you sent.

Example: 3a564ea5-ef64-4215-9eba-9384f37489a6

Id

Key Description
id uuid4 (required)
A unique identifier of the command.

Example: 2aa04c78-15b0-49dc-967c-924b22deb25d

Message

server → client A new message has been published in a topic you are subscribed to.

When another client publishes a message to a topic you are subscribed to, you receive a message command.

Example

{
    "type": "msg.v1",
    "body": {
        "seq": 123,
        "topic": "my-company.my-plugin.my-topic",
        "data": {
            "someKey": "someValue",
            "anotherKey": [1, 2, 3, 4],
            "yetAnotherKey": {
                "for": "this value"
            }
        }
    },
    "id": "2aa04c78-15b0-49dc-967c-924b22deb25d"
}

Command

Type

Key Description
type msg.v1

Body

Key Description
seq int (required)
The sequence number of the message. Store the largest sequence number you have processed and send it in the next Pulse.

Example: 123
topic string (required)
The identifier of the Topic where the message was published. Use it when you are subscribed to more than one topic to understand the origin of each message.

Example: my-company.my-plugin.my-topic
data object?
An arbitrary JSON object, the payload of your message.

Example: {}

Id

Key Description
id uuid4 (required)
A unique identifier of the command.

Example: 2aa04c78-15b0-49dc-967c-924b22deb25d

Pulse

client → server Acknowledge received messages.

You MUST send period pulse commands to the server periodically every pulsePeriodSeconds seconds.

⚠️ If you do not send pulse commands acknowledging messages every pulsePeriodSeconds, the server will terminate your connection.

You can configure the pulse period in the Event Server configuration file. For manual testing (i.e. Postman) and debugging, it is useful to set it to a large value.

Example

{
    "type": "pulse.v1",
    "body": {
        "seq": 123
    },
    "id": "3a564ea5-ef64-4215-9eba-9384f37489a6"
}

Command

Type

Key Description
type pulse.v1

Body

Key Description
seq int (required)
The seq value of the last Message you have received and processed. If you have not received any messages yet, send -1.

Example: 123

Id

Key Description
id uuid4 (required)
A unique identifier of the command.

Example: 3a564ea5-ef64-4215-9eba-9384f37489a6

Publish

client → server Send a message to all clients subscribed to a topic.

Use the publish command to send a message to a topic. The message can contain any arbitrary JSON payload.

⚠️ You should use unique topics to avoid collisions with other plugins. You may use uuids or the following format: company-name.plugin-name.message-name.

Example

{
    "type": "pub.v1",
    "body": {
        "topic": "my-company.my-plugin.my-topic",
        "data": {
            "someKey": "someValue",
            "anotherKey": [1, 2, 3, 4],
            "yetAnotherKey": {
                "for": "this value"
            }
        }
    },
    "id": "3a564ea5-ef64-4215-9eba-9384f37489a6"
}

Command

Type

Key Description
type pub.v1

Body

Key Description
topic string (required)
The identifier of the Topic you want to publish the message in. All clients subscribed to this topic will receive your message, except yourself.

Example: my-company.my-plugin.my-topic
data object?
An arbitrary JSON object, the payload of your message.

Example: {}

Id

Key Description
id uuid4 (required)
A unique identifier of the command.

Example: 3a564ea5-ef64-4215-9eba-9384f37489a6

Subscribe

client → server Subscribe to all messages published to a topic.

Use the subscribe command to receive all messages that are published to a topic by other clients. The subscribe operation is idempotent.

Example

{
    "type": "sub.v1",
    "body": {
        "topic": "my-company.my-plugin.my-topic"
    },
    "id": "3a564ea5-ef64-4215-9eba-9384f37489a6"
}

Command

Type

Key Description
type sub.v1

Body

Key Description
topic string (required)
The identifier of the Topic you want to receive messages from.

Example: my-company.my-plugin.my-topic

Id

Key Description
id uuid4 (required)
A unique identifier of the command.

Example: 3a564ea5-ef64-4215-9eba-9384f37489a6

Unsubscribe

client → server Unsubscribe from a topic.

Use the unsubscribe command to stop receiving messages that are published to a topic. The unsubscribe operation is idempotent.

Example

{
    "type": "unsub.v1",
    "body": {
        "topic": "my-company.my-plugin.my-topic"
    },
    "id": "3a564ea5-ef64-4215-9eba-9384f37489a6"
}

Command

Type

Key Description
type unsub.v1

Body

Key Description
topic string (required)
The identifier of the Topic you want to stop receiving messages from.

Example: my-company.my-plugin.my-topic

Id

Key Description
id uuid4 (required)
A unique identifier of the command.

Example: 3a564ea5-ef64-4215-9eba-9384f37489a6

Error

server → client An invalid command or forced disconnect occurred.

Use the error command during debugging and developing to troubleshoot issues.

If you send an invalid JSON object, the error command will not be issued. These errors are only logged in the server.

Example

{
    "id": "7c5737a9-fb5e-4dda-8028-5ad30e1e54be",
    "type": "error.v1",
    "body": {
        "description": "No commands have been sent for more than the allowed pulse period or no pulse has been sent to acknowledge received messages",
        "invalidCommandId": null
    }
}

Command

Type

Key Description
type error.v1

Body

Key Description
description string (required)
A description of the error.

Example: No commands have been sent for more than the allowed pulse period or no pulse has been sent to acknowledge received messages
invalidCommandId uuid
The id of invalid command.

Example: 10bcd57a-7e57-446c-a31b-8eb1417fac09

Id

Key Description
id uuid4 (required)
A unique identifier of the command.

Example: 3a564ea5-ef64-4215-9eba-9384f37489a6

Auth

client → server Authenticate inside the channel.

Use the auth command to authenticate when you cannot set the Authorization header. For example, when you are using the browser WebSocket client.

If you send any other commands before authenticating, either through the headers or the auth command, your session will be automatically terminated.

Example

{
    "id": "7c5737a9-fb5e-4dda-8028-5ad30e1e54be",
    "type": "auth.v1",
    "body": {
        "token": "Bearer eyJhbG...ZGhCy4rg"
    }
}

Command

Type

Key Description
type auth.v1

Body

Key Description
token string (required)
A token in Bearer format issued by IDP.

Id

Key Description
id uuid4 (required)
A unique identifier of the command.

Example: 3a564ea5-ef64-4215-9eba-9384f37489a6