Skip to content

API Service

Sebastian Willenborg edited this page Nov 4, 2022 · 4 revisions

External Service API

Overview

The external service APIs are the interface between Wire and external services. The APIs are classified as either inbound or outbound from the perspective of Wire in this document. On outbound APIs Wire acts as a client whereas on inbound APIs Wire acts as a server.

Credentials

Interaction between Wire and an external service involves usage of three different credentials:

  • Service Public Keys: The set of public keys that Wire accepts when establishing connections to the external service. The public keys are managed as part of the provider account.
  • Service Tokens: Shared secrets between Wire and the external service that are used in different contexts for authenticating or authorizing requests between Wire and the external service, in both directions. The service tokens are managed as part of the provider account.
  • Bot Tokens: Bot tokens are given to a service on-demand, as bots are requested from the service by users. Bot tokens are scoped to a single bot and conversation and are used by the bot to make inbound requests on the bot API.

Outbound

The following API requests are sent from Wire to a service, i.e. Wire acts as a client towards the service. Please note the TLS requirements for services when Wire acts as a client on the outbound APIs. All request paths are implied to be relative to a service's registered base URL, as it is shown in the service view of the provider account.


Authentication

Outbound requests towards a service use the primary API token that is assigned to the service in the provider account to authenticate requests towards a service. The token is sent in the form of an HTTP Authorization header using the Bearer scheme.


Create Bot (POST /bots)

Request

When a user adds a service to a conversation, Wire sends a POST request to the /bots endpoint of a service, asking it to prepare a new bot. The request contains a JSON payload with a top-level object having the following attributes:

  • id (String): The unique user ID for the bot.
  • client (String): The client ID for the bot.
  • origin (Object): The profile of the user who requested the bot, as it is returned from GET /bot/users.
  • conversation (Object): The conversation as seen by the bot and as returned from GET /bot/conversation.
  • token (String): The bearer token that the bot must use on inbound requests.
  • locale (String): The preferred locale for the bot to use, in form of an IETF language tag.

Upon receiving the request, a service should do the following:

  • Initialise the necessary cryptographic material and storage used for end-to-end encryption.
  • Store some or all of the information provided in the request for later use (e.g. to make requests on the inbound API).
  • Generate prekeys.

Response

On success the service must respond with a 201 Created status and include the following attributes in a JSON-encoded response body:

  • prekeys (Array): An array of prekeys.
  • last_prekey (Object): The last resort prekey. It must have the ID 65535.
  • name (String, Optional): The name to use for the bot's user profile. By default, the bot is assigned the name of the associated service profile.
  • accent_id (Number, Optional): A number between 1 and 7 that identifies one of the standard Wire profile colours to use for the bot's user profile. By default, the bot is assigned the accent colour associated with the service profile.
  • assets (Array, Optional): A list of user profile assets to attach to the bot's user profile. By default, the bot is assigned the assets associated with the service profile.

A prekey must be represented as a JSON object with the following attributes:

  • id (Number): The ID of the prekey. A number between 0 and 65535.
  • key (String): The base64-encoded prekey data.

A service should generate and respond with at least 8 times as many prekeys as there are other members in the conversation, since that is the maximum number of clients (i.e. devices) that may currently be present in the conversation (a bot only ever has 1 client though). Generating more prekeys than necessary is beneficial and not harmful, as long as they are not needlessly overwritten (see also how to update prekeys).

After sending a successful response, Wire completes the initialisation of the bot using the given data and then adds the bot to the conversation. The first message that the bot will receive is about a new member, namely the bot itself. This is the signal for the bot that it can start operating in the conversation.

If the service does not allow more bots to be added to the requested conversation (or in general), it must respond with a 409 Conflict status. The service may use the given conversation member list to check for the presence of other bots from the same service.

Example

> POST /bots
> Content-Type: application/json
>
> {
>    "id": "ce45c023-8e7c-4910-a383-6ed0a2537593",
>    "client": "a1b2c3d4e5f6a7b8",
>    "origin": {
>        "id": "13caae5a-3a36-4d57-bf18-18a58882fdc7",
>        "name": "Mr. User",
>        "handle": "user123",
>        "accent_id": 1
>    },
>    "token": "abc123...",
>    "conversation": {
>        "id": "5b3ec920-f783-46b1-ba32-9ed346498698",
>        "name": "Talk",
>        "members": [
>             {
>                "id": "13caae5a-3a36-4d57-bf18-18a58882fdc7",
>                "status": 0
>             },
>             {
>                "id": "a79846f9-6c95-416c-b3e5-7054d40ff04c",
>                "status": 0,
>                "service": {
>                    "id": "23172003-6be2-48a5-ba22-398c20e9529c",
>                    "provider": "fb010189-5107-4d00-9df5-f0ccc7d6eaf5"
>                }
>             }
>        ]
>    }
> }

< 201 Created
< Content-Type: application/json
<
< {
<    "last_prekey": {
<       "id": 65535,
<       "key": "<base64-prekey-data>"
<    },
<    "prekeys": [
<       {"id": 1, "key": "<base64-prekey-data>"},
<       {"id": 2, "key": "<base64-prekey-data>"},
<       {"id": 3, "key": "<base64-prekey-data>"}
<    ]
< }

New Message (POST /bots/:bot/messages)

Request

When there is activity in a conversation that the bot is eligible to receive, the service is informed by Wire through a message sent to /bots/:bot/messages. Thereby the recipient bot ID is included in the request path at the position indicated by :bot.

The message itself is included in a JSON request body and can be one of the following:

  • An end-to-end encrypted message.
  • A message about the conversation having been renamed.
  • A message about one or more users joining the conversation.
  • A message about one or more users leaving the conversation.

These messages have the following common JSON structure:

  • type (String): The message type, as defined below.
  • conversation (String): The conversation ID.
  • from (String): The user ID of the sender / originator.
  • time (String): A UTC server timestamp when the message was received, in ISO8601 format.
  • data (Object): The type-specific message data, as defined below.

The following message types are currently defined for bots and dictate the structure of the data payload:

conversation.otr-message-add

  • sender (String): The client ID of the sender.
  • recipient (String): The client ID of the intended recipient.
  • text (String): The end-to-end encrypted ciphertext of a generic message.
  • data (String, Optional): Opaque (usually encrypted) data originating from the sender. The ciphertext in text typically contains clues as to how this data should be processed (e.g. a symmetric key). Specifically, this attribute is frequently used by the sender in order to reduce his request payload size by encrypting a larger payload once, including it as data to be distributed by the server to all recipients, and only encrypting the smaller keys necessary for decryption in the ciphertext per recipient.

conversation.member-join

  • user_ids (Array): The list of user IDs that joined the conversation.

When a bot receives a conversation.member-join message, it should do one of the following per new user:

  • Verify that it has at least 8 prekeys left on Wire for the new user (besides the last resort prekey) and if necessary refresh its prekeys.
  • Fetch prekeys and initialise sessions with all clients of the new user, immediately followed by sending a message.

conversation.member-leave

  • user_ids (Array): The list of user IDs that left the conversation.

When a bot receives a conversation.member-leave message, it should check if the bot's own ID is among the user_ids. If so, the bot has been removed from the conversation and this message is the last message that the bot will receive. It should then delete its cryptographic material and any other state or data that the bot has acquired or collected during its membership in the conversation.

For any other user ID that has been removed from the conversation, the bot may delete any state, cryptographic or otherwise, that it maintains for that user.

conversation.rename

  • name (String): The new name of the conversation.

Response

On success the service must respond with a 200 OK or 201 Created status. If the service finds the bot to be no longer available, possibly due to a loss of relevant state, or simply wants the bot to be removed from the conversation and deleted, it must respond with a 410 Gone status.

Example

> POST /bots/c172e629-0bb4-46b0-aa34-c52843e0e6a3/messages
> Content-Type: application/json
>
> {
>    "type": "conversation.otr-message-add",
>    "conversation": "dc95b7fe-8b54-4248-82c0-84c68c91f971",
>    "from": "8fc957e6-17f0-420a-b0ae-546157f3eeb5",
>    "time": "2016-03-16T13:29:14.123Z"
>    "data": {
>       "sender": "a1b2c3d4e5f6a7b8",
>       "recipient": "e1f2c3d8e5f6a7b4",
>       "text": "<ciphertext>",
>       "data": "<data>"
>    }
> }

< 200 OK

Inbound

The following API requests can be sent from a service to Wire, i.e. the service acts as a client towards Wire in the context of a single bot.


Authentication

Inbound requests from services on the bot API must use the bot tokens given to the service as part of bot creation requests. The tokens must be included in the requests in the form of an HTTP Authorization header using the Bearer scheme.


Self

GET /bot/self

Fetch the bot's own user profile information. A bot's profile has the following attributes:

  • id (String): The bot's user ID.
  • name (String): The bot's name.
  • accent_id (Number): The bot's accent colour.
  • assets (Array): The bot's public profile assets (e.g. images).

Example:

> GET /bot/self

< 200 OK
< Content-Type: application/json
<
< {
<   "accent_id": 5,
<   "assets": [{"type": "image", "key": "3-1-cb21f0ce-6bd4-400e-9776-26ad5dd7cd62"}],
<   "name": "Otto",
<   "id": "a79846f9-6c95-416c-b3e5-7054d40ff04c"
< }

PUT /bot/self

Update the bot's own user profile information.

Not yet available

DELETE /bot/self

Delete the bot, thereby removing it from the conversation it is in.

The bot will receive a final conversation.member-leave message.

Example:

> DELETE /bot/self

< 200 OK

Client

GET /bot/client

Fetch the bot's own client information. The following attributes are returned:

  • id (String): The bot's client ID.
  • type (String): The bot's client type. This is currently always permanent.
  • time: (String): The UTC timestamp when the client was created.

Example:

> GET /bot/client

< 200 OK
< Content-Type: application/json
<
< {
<    "id": "abc123",
<    "type": "permanent",
<    "time": "2016-03-16T13:29:14.123Z"
< }

GET /bot/client/prekeys

List the bot's remaining prekey IDs.

Example:

> GET /bot/client/prekeys

< 200 OK
< Content-Type: application/json
<
< [ 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 65535 ]

POST /bot/client/prekeys

Update the bot's prekeys. The request body must contain a JSON object with the following attributes:

  • prekeys (Array): The list of prekeys to insert.

A single prekey must be represented as a JSON object with the following attributes:

  • id (Number): The ID of the prekey. A number between 0 and 65535.
  • key (String): The base64-encoded prekey data.

If a prekey is included with an ID that is still present on the server, the existing prekey is overridden. Overriding prekeys should be avoided as much as possible, to ensure correct operation of the end-to-end encryption protocol. See the details on refreshing prekeys as well as the API for listing remaining prekeys.

Example:

> POST /bot/client/prekeys
> Content-Type: application/json
>
> {
>    "prekeys": [
>       {"id": 1234, "key": "<base64-prekey-data>"},
>       {"id": 1235, "key": "<base64-prekey-data>"}
>    ]
> }

< 200 OK

Users

POST /bot/users/prekeys

Claim prekeys from one or more clients of one or more users.

The result is a nested JSON object structure where the first-level keys are user IDs and the second-level keys client IDs. The values paired with the client IDs are the base64-encoded prekeys.

Example:

> POST /bot/users/prekeys
> Content-Type: application/json
>
> {
>   "1571f70d-2f31-4d10-ae09-9f357becc762": [
>       "h7bd619af6ec2cb1",
>       "7bb613ad6ec6cba"
>   ],
>   "a735c69c-0025-4f86-859e-bde5618c4cd6": [
>       "11bd4af9ec2cb1"
>       "73bd618af4ecfca4"
>   ]
> }

< 200 OK
< Content-Type: application/json
<
< {
<    "1571f70d-2f31-4d10-ae09-9f357becc762": {
<       "h7bd619af6ec2cb1": "<base64-prekey-data>",
<       "7bb613ad6ec6cba": "<base64-prekey-data>"
<    },
<    "a735c69c-0025-4f86-859e-bde5618c4cd6": {
<       "11bd4af9ec2cb1": "<base64-prekey-data>",
<       "73bd618af4ecfca4": "<base64-prekey-data>"
<    }
< }

GET /bot/users

Fetch other user's profiles. A single query parameter is required:

  • ids: A comma-separated list of user IDs whose profiles to fetch.

A user profile as seen by a bot has the following attributes:

  • id (String): The user ID.
  • name (String): The user name.
  • handle (String): The user's nickname (handle)
  • accent_id (Number): The accent colour ID.

Example:

> GET /bot/users?ids=a79846f9-6c95-416c-b3e5-7054d40ff04c

< 200 OK
< Content-Type: application/json
<
< [
<   {
<     "id": "a79846f9-6c95-416c-b3e5-7054d40ff04c",
<     "name": "John Smith",
<     "handle": "john123",
<     "accent_id": 1
<   }
< ]

GET /bot/users/:uid/clients

Fetch a user's list of clients. The user ID must be put in the request path at the position indicated by :uid.

The response body contains a JSON array of objects, each with the following attributes:

  • id (String): The client ID.
  • class (String): The client class. One of phone, tablet or desktop.

Example:

> GET /bot/users/a79846f9-6c95-416c-b3e5-7054d40ff04c/clients

< 200 OK
< Content-Type: application/json
<
< [
<   {
<     "id": "a1b2c3d4e5",
<     "class": "phone"
<   },
<   {
<     "id": "abc1234",
<     "class": "desktop"
<   }
< ]

Conversation

GET /bot/conversation

Fetch metadata about the conversation the bot is in.

The following fields are returned in the JSON response:

  • id: The ID of the conversation.
  • name: Optional. The name of the conversation.
  • members: The list of members, not including the bot itself.

Each member in the list of members has the following attributes:

  • id: The user ID.
  • status: The member status. Currently this is always 0, indicating a regular member. Other status values might be used in the future.
  • service: Optional. The reference to the owning service, if the member is a bot.

Example:

> GET /bot/conversation

< 200 OK
< Content-Type: application/json
<
< {
<   "id": "5b3ec920-f783-46b1-ba32-9ed346498698",
<   "name": "Talk",
<   "members": [
<        {
<           "id": "68225a5c-cbc1-445a-8ce3-8afd5da94a84",
<           "status": 0
<        },
<        {
<           "id": "a79846f9-6c95-416c-b3e5-7054d40ff04c",
<           "status": 0,
<           "service": {
<               "id": "23172003-6be2-48a5-ba22-398c20e9529c",
<               "provider": "fb010189-5107-4d00-9df5-f0ccc7d6eaf5"
<           }
<        }
<   ]
< }

Message

POST /bot/messages

Post an end-to-end encrypted generic message to the conversation the bot is in. The JSON request body must contain an object with the following attributes:

  • sender (String): The sender's (i.e. bot's) client ID.
  • recipients (Object): The intended recipients. This must be a nested JSON object structure, with the first-level keys being user IDs of the intended recipients and the second-level keys being the intended recipient client IDs, mapped to opaque string values containing the ciphertext for each client. See the example below.
  • native_push (Boolean, Optional): Whether the message should be delivered over a channel that is native to the recipient's device (e.g. GCM, APNS), if the device is not currently online. Defaults to true.
  • native_priority (String, Optional): The desired priority of the native push. Must be one of low or high. Defaults to high.
  • transient (Boolean, Optional): Whether the message is not supposed to be temporarily stored in each recipient's notification queue. Defaults to false, i.e. the message is stored. If set to true and the recipient's device is not reachable (via WebSocket or a native push channel like GCM or APNS), then the message will be lost. Transient messages are mostly appropriate for messages that should be delivered "now or never", i.e. where the message loses its purpose if it cannot be immediately delivered.
  • data (String, Optional): An opaque value that is fanned-out (i.e. duplicated) by the server to each recipient.

Additionally, the following query parameters are supported in the request:

  • ignore_missing (Boolean): Whether to ignore missing clients in the recipients.

The service must accept any 2xx success status in the response, indicating a success. If the recipients in the request do not contain entries for all clients in the conversation as known to the server, it will respond with a 412 Precondition Failed error status and the message will not have been sent. In both cases, the JSON response body contains the following attributes:

  • time (String): The UTC timestamp of the server. If the response is a success (2xx), this is the timestamp assigned to the message for each recipient.
  • missing (Object): Clients which are missing from the recipients. The keys are user IDs and the values lists of client IDs.
  • redundant (Object): Clients from the recipients which are no longer in the conversation. The keys are user IDs and the values lists of client IDs.
  • deleted (Object): Clients from the recipients which are no longer in the conversation and are known to have been deleted. The keys are user IDs and the values lists of client IDs.

Those clients reported as redundant or deleted should no longer be included in the recipients in future requests. The service may further wish to discard state associated with clients that are reported as deleted.

If the response is a 412 Precondition Failed due to missing clients, the service may do one of the following, depending on the policy of the service:

  • Automatically fetch prekeys for the missing clients, initialise the cryptographic sessions and re-send the request with updated recipients as well as the ignore_missing query parameter set to true.
  • Purposefully ignore missing clients for the message, e.g. because it is a message targeted at a specific known subset of clients or because the service does generally have the policy of not automatically accepting new clients during the lifetime of a bot. It then sets ignore_missing to true when retrying the request or even on the initial request.

More details on the concepts of end-to-end encryption with Wire can be found here.

Example:

> POST /bot/messages
> Content-Type: application/json
>
> {
>    "sender": "abc123",
>    "recipients": {
>       "a17e1e8b-7d12-412e-845b-b3ef8934e84b": {
>           "a1b2c34": "<base64-ciphertext>",
>           "1a2b3c": "<base64-ciphertext>"
>       },
>       "ba70fb55-1046-4740-a320-1c5d61758a08": {
>           "1a2a3a4a": "<base64-ciphertext>",
>           "b1b2b3b4": "<base64-ciphertext>"
>       }
>    }
> }

< 201 Created
< Content-Type: application/json
<
< {
<    "time": "2016-03-16T13:29:14.123Z",
<    "missing": {},
<    "redundant": {},
<    "deleted": {}
< }

Asset

POST /bot/assets

Upload an asset, as defined in Uploading Assets - Simple Upload, using the request path /bot/assets. Note that resumable uploads are not currently supported for bots.

GET /bot/assets/:key

Download an asset, as defined in Downloading Assets, using the request path /bot/assets/:key.

DELETE /bot/assets/:key

Delete an asset, as defined in Deleting Assets, using the request path /bot/assets/:key.