-
Notifications
You must be signed in to change notification settings - Fork 15
API Service
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.
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.
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.
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.
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.
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 ID65535
. -
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.
> 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>"}
< ]
< }
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): Thetype
-specific message data, as defined below.
The following message type
s 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 intext
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 asdata
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.
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.
> 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
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.
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.
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"
< }
Update the bot's own user profile information.
Not yet available
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
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 alwayspermanent
. -
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"
< }
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 ]
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
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>"
< }
< }
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
< }
< ]
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 ofphone
,tablet
ordesktop
.
Example:
> GET /bot/users/a79846f9-6c95-416c-b3e5-7054d40ff04c/clients
< 200 OK
< Content-Type: application/json
<
< [
< {
< "id": "a1b2c3d4e5",
< "class": "phone"
< },
< {
< "id": "abc1234",
< "class": "desktop"
< }
< ]
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 always0
, 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"
< }
< }
< ]
< }
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 totrue
. -
native_priority
(String, Optional): The desired priority of the native push. Must be one oflow
orhigh
. Defaults tohigh
. -
transient
(Boolean, Optional): Whether the message is not supposed to be temporarily stored in each recipient's notification queue. Defaults tofalse
, i.e. the message is stored. If set totrue
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 therecipients
.
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 therecipients
. The keys are user IDs and the values lists of client IDs. -
redundant
(Object): Clients from therecipients
which are no longer in the conversation. The keys are user IDs and the values lists of client IDs. -
deleted
(Object): Clients from therecipients
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 theignore_missing
query parameter set totrue
. - 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
totrue
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": {}
< }
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.
Download an asset, as defined in Downloading Assets, using
the request path /bot/assets/:key
.
Delete an asset, as defined in Deleting Assets, using
the request path /bot/assets/:key
.