You're looking at an unstable version of this specification. Unstable specifications may change at any time without notice.

Switch to the current stable release.

Client-Server API

The client-server API allows clients to send messages, control rooms and synchronise conversation history. It is designed to support both lightweight clients which store no state and lazy-load data from the server as required - as well as heavyweight clients which maintain a full local persistent copy of server state.

API Standards

The mandatory baseline for client-server communication in Matrix is exchanging JSON objects over HTTP APIs. More efficient transports may be specified in future as optional extensions.

HTTPS is recommended for communication. The use of plain HTTP is not recommended outside test environments.

Clients are authenticated using opaque access_token strings (see Client Authentication for details).

All POST and PUT endpoints, with the exception of those listed below, require the client to supply a request body containing a (potentially empty) JSON object. Clients should supply a Content-Type header of application/json for all requests with JSON bodies, but this is not required.

The exceptions are:

Similarly, all endpoints require the server to return a JSON object, with the exception of 200 responses to the media download endpoints in the Content Repository module. Servers must include a Content-Type header of application/json for all JSON responses.

All JSON data, in requests or responses, must be encoded using UTF-8.

See also Conventions for Matrix APIs in the Appendices for conventions which all Matrix APIs are expected to follow, and Web Browser Clients below for additional requirements for server responses.

Standard error response

Any errors which occur at the Matrix API level MUST return a “standard error response”. This is a JSON object which looks like:

{
  "errcode": "<error code>",
  "error": "<error message>"
}

The error string will be a human-readable error message, usually a sentence explaining what went wrong.

The errcode string will be a unique string which can be used to handle an error message e.g. M_FORBIDDEN. Error codes should have their namespace first in ALL CAPS, followed by a single _. For example, if there was a custom namespace com.mydomain.here, and a FORBIDDEN code, the error code should look like COM.MYDOMAIN.HERE_FORBIDDEN. Error codes defined by this specification should start with M_.

Some errcodes define additional keys which should be present in the error response object, but the keys error and errcode MUST always be present.

Errors are generally best expressed by their error code rather than the HTTP status code returned. When encountering the error code M_UNKNOWN, clients should prefer the HTTP status code as a more reliable reference for what the issue was. For example, if the client receives an error code of M_NOT_FOUND but the request gave a 400 Bad Request status code, the client should treat the error as if the resource was not found. However, if the client were to receive an error code of M_UNKNOWN with a 400 Bad Request, the client should assume that the request being made was invalid.

Common error codes

These error codes can be returned by any API endpoint:

M_FORBIDDEN Forbidden access, e.g. joining a room without permission, failed login.

M_UNKNOWN_TOKEN The access or refresh token specified was not recognised.

An additional response parameter, soft_logout, might be present on the response for 401 HTTP status codes. See the soft logout section for more information.

M_MISSING_TOKEN No access token was specified for the request.

M_USER_LOCKED The account has been locked and cannot be used at this time.

M_USER_SUSPENDED The account has been suspended and can only be used for limited actions at this time.

M_BAD_JSON Request contained valid JSON, but it was malformed in some way, e.g. missing required keys, invalid values for keys.

M_NOT_JSON Request did not contain valid JSON.

M_NOT_FOUND No resource was found for this request.

M_LIMIT_EXCEEDED Too many requests have been sent in a short period of time. Wait a while then try again. See Rate limiting.

M_UNRECOGNIZED The server did not understand the request. This is expected to be returned with a 404 HTTP status code if the endpoint is not implemented or a 405 HTTP status code if the endpoint is implemented, but the incorrect HTTP method is used.

M_UNKNOWN An unknown error has occurred.

Other error codes

The following error codes are specific to certain endpoints.

M_UNAUTHORIZED The request was not correctly authorized. Usually due to login failures.

M_USER_DEACTIVATED The user ID associated with the request has been deactivated. Typically for endpoints that prove authentication, such as /login.

M_USER_IN_USE Encountered when trying to register a user ID which has been taken.

M_INVALID_USERNAME Encountered when trying to register a user ID which is not valid.

M_ROOM_IN_USE Sent when the room alias given to the createRoom API is already in use.

M_INVALID_ROOM_STATE Sent when the initial state given to the createRoom API is invalid.

M_THREEPID_IN_USE Sent when a threepid given to an API cannot be used because the same threepid is already in use.

M_THREEPID_NOT_FOUND Sent when a threepid given to an API cannot be used because no record matching the threepid was found.

M_THREEPID_AUTH_FAILED Authentication could not be performed on the third-party identifier.

M_THREEPID_DENIED The server does not permit this third-party identifier. This may happen if the server only permits, for example, email addresses from a particular domain.

M_SERVER_NOT_TRUSTED The client’s request used a third-party server, e.g. identity server, that this server does not trust.

M_UNSUPPORTED_ROOM_VERSION The client’s request to create a room used a room version that the server does not support.

M_INCOMPATIBLE_ROOM_VERSION The client attempted to join a room that has a version the server does not support. Inspect the room_version property of the error response for the room’s version.

M_BAD_STATE The state change requested cannot be performed, such as attempting to unban a user who is not banned.

M_GUEST_ACCESS_FORBIDDEN The room or resource does not permit guests to access it.

M_CAPTCHA_NEEDED A Captcha is required to complete the request.

M_CAPTCHA_INVALID The Captcha provided did not match what was expected.

M_MISSING_PARAM A required parameter was missing from the request.

M_INVALID_PARAM A parameter that was specified has the wrong value. For example, the server expected an integer and instead received a string.

M_TOO_LARGE The request or entity was too large.

M_EXCLUSIVE The resource being requested is reserved by an application service, or the application service making the request has not created the resource.

M_RESOURCE_LIMIT_EXCEEDED The request cannot be completed because the homeserver has reached a resource limit imposed on it. For example, a homeserver held in a shared hosting environment may reach a resource limit if it starts using too much memory or disk space. The error MUST have an admin_contact field to provide the user receiving the error a place to reach out to. Typically, this error will appear on routes which attempt to modify state (e.g.: sending messages, account data, etc) and not routes which only read state (e.g.: /sync, /user/{userId}/account_data/{type}, etc).

M_CANNOT_LEAVE_SERVER_NOTICE_ROOM The user is unable to reject an invite to join the server notices room. See the Server Notices module for more information.

M_THREEPID_MEDIUM_NOT_SUPPORTED The homeserver does not support adding a third party identifier of the given medium.

M_THREEPID_IN_USE The third party identifier specified by the client is not acceptable because it is already in use in some way.

Rate limiting

Homeservers SHOULD implement rate limiting to reduce the risk of being overloaded. If a request is refused due to rate limiting, it should return a standard error response of the form:

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "string",
  "retry_after_ms": integer (optional, deprecated)
}

Homeservers SHOULD include a Retry-After header for any response with a 429 status code.

The retry_after_ms property MAY be included to tell the client how long they have to wait in milliseconds before they can try again. This property is deprecated, in favour of the Retry-After header.

[Changed in v1.10]: retry_after_ms property deprecated in favour of Retry-After header.

Transaction identifiers

The client-server API typically uses HTTP PUT to submit requests with a client-generated transaction identifier in the HTTP path.

The purpose of the transaction ID is to allow the homeserver to distinguish a new request from a retransmission of a previous request so that it can make the request idempotent.

The transaction ID should only be used for this purpose.

After the request has finished, clients should change the {txnId} value for the next request. How this is achieved, is left as an implementation detail. It is recommended that clients use either version 4 UUIDs or a concatenation of the current timestamp and a monotonically increasing integer.

The homeserver should identify a request as a retransmission if the transaction ID is the same as a previous request, and the path of the HTTP request is the same.

Where a retransmission has been identified, the homeserver should return the same HTTP response code and content as the original request. For example, PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId} would return a 200 OK with the event_id of the original request in the response body.

The scope of a transaction ID is for a single device, and a single HTTP endpoint. In other words: a single device could use the same transaction ID for a request to PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId} and PUT /_matrix/client/v3/sendToDevice/{eventType}/{txnId}, and the two requests would be considered distinct because the two are considered separate endpoints. Similarly, if a client logs out and back in between two requests using the same transaction ID, the requests are distinct because the act of logging in and out creates a new device (unless an existing device_id is passed to POST /_matrix/client/v3/login). On the other hand, if a client re-uses a transaction ID for the same endpoint after refreshing an access token, it will be assumed to be a duplicate request and ignored. See also Relationship between access tokens and devices.

Some API endpoints may allow or require the use of POST requests without a transaction ID. Where this is optional, the use of a PUT request is strongly recommended.

Prior to v1.7, transaction IDs were scoped to “client sessions” rather than devices.

Web Browser Clients

It is realistic to expect that some clients will be written to be run within a web browser or similar environment. In these cases, the homeserver should respond to pre-flight requests and supply Cross-Origin Resource Sharing (CORS) headers on all requests.

Servers MUST expect that clients will approach them with OPTIONS requests, allowing clients to discover the CORS headers. All endpoints in this specification support the OPTIONS method, however the server MUST NOT perform any logic defined for the endpoints when approached with an OPTIONS request.

When a client approaches the server with a request, the server should respond with the CORS headers for that route. The recommended CORS headers to be returned by servers on all requests are:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: X-Requested-With, Content-Type, Authorization

[Added in v1.13] The recommended value of the Access-Control-Allow-Methods header only covers the existing endpoints in the specification. Servers which support additional endpoints or methods should add those methods as well.

This section will be updated whenever a new method is supported by an endpoint. Examples of possible future-use methods include PATCH and HEAD.

Server Discovery

In order to allow users to connect to a Matrix server without needing to explicitly specify the homeserver’s URL or other parameters, clients SHOULD use an auto-discovery mechanism to determine the server’s URL based on a user’s Matrix ID. Auto-discovery should only be done at login time.

In this section, the following terms are used with specific meanings:

PROMPT Retrieve the specific piece of information from the user in a way which fits within the existing client user experience, if the client is inclined to do so. Failure can take place instead if no good user experience for this is possible at this point.

IGNORE Stop the current auto-discovery mechanism. If no more auto-discovery mechanisms are available, then the client may use other methods of determining the required parameters, such as prompting the user, or using default values.

FAIL_PROMPT Inform the user that auto-discovery failed due to invalid/empty data and PROMPT for the parameter.

FAIL_ERROR Inform the user that auto-discovery did not return any usable URLs. Do not continue further with the current login process. At this point, valid data was obtained, but no server is available to serve the client. No further guess should be attempted and the user should make a conscientious decision what to do next.

Well-known URI

Servers hosting the .well-known JSON file SHOULD offer CORS headers, as per the CORS section in this specification.

The .well-known method uses a JSON file at a predetermined location to specify parameter values. The flow for this method is as follows:

  1. Extract the server name from the user’s Matrix ID by splitting the Matrix ID at the first colon.
  2. Extract the hostname from the server name as described by the grammar.
  3. Make a GET request to https://hostname/.well-known/matrix/client.
    1. If the returned status code is 404, then IGNORE.
    2. If the returned status code is not 200, or the response body is empty, then FAIL_PROMPT.
    3. Parse the response body as a JSON object
      1. If the content cannot be parsed, then FAIL_PROMPT.
    4. Extract the base_url value from the m.homeserver property. This value is to be used as the base URL of the homeserver.
      1. If this value is not provided, then FAIL_PROMPT.
    5. Validate the homeserver base URL:
      1. Parse it as a URL. If it is not a URL, then FAIL_ERROR.
      2. Clients SHOULD validate that the URL points to a valid homeserver before accepting it by connecting to the /_matrix/client/versions endpoint, ensuring that it does not return an error, and parsing and validating that the data conforms with the expected response format. If any step in the validation fails, then FAIL_ERROR. Validation is done as a simple check against configuration errors, in order to ensure that the discovered address points to a valid homeserver.
      3. It is important to note that the base_url value might include a trailing /. Consumers should be prepared to handle both cases.
    6. If the m.identity_server property is present, extract the base_url value for use as the base URL of the identity server. Validation for this URL is done as in the step above, but using /_matrix/identity/v2 as the endpoint to connect to. If the m.identity_server property is present, but does not have a base_url value, then FAIL_PROMPT.

GET /.well-known/matrix/client


Gets discovery information about the domain. The file may include additional keys, which MUST follow the Java package naming convention, e.g. com.example.myapp.property. This ensures property names are suitably namespaced for each application and reduces the risk of clashes.

Note that this endpoint is not necessarily handled by the homeserver, but by another webserver, to be used for discovering the homeserver URL.

Rate-limited: No
Requires authentication: No

Request

No request parameters or request body.


Responses

Status Description
200 Server discovery information.
404 No server discovery information available.

200 response

Discovery Information
Name Type Description
m.homeserver Homeserver Information

Required: Used by clients to discover homeserver information.

m.identity_server Identity Server Information

Used by clients to discover identity server information.

<Other properties>

Application-dependent keys using Java package naming convention.

Homeserver Information
Name Type Description
base_url URI

Required: The base URL for the homeserver for client-server connections.

Identity Server Information
Name Type Description
base_url URI

Required: The base URL for the identity server for client-server connections.

{
  "m.homeserver": {
    "base_url": "https://matrix.example.com"
  },
  "m.identity_server": {
    "base_url": "https://identity.example.com"
  },
  "org.example.custom.property": {
    "app_url": "https://custom.app.example.org"
  }
}

GET /_matrix/client/versions


Changed in v1.10: This endpoint can behave differently when authentication is provided.

Gets the versions of the specification supported by the server.

Values will take the form vX.Y or rX.Y.Z in historical cases. See the Specification Versioning for more information.

The server may additionally advertise experimental features it supports through unstable_features. These features should be namespaced and may optionally include version information within their name if desired. Features listed here are not for optionally toggling parts of the Matrix specification and should only be used to advertise support for a feature which has not yet landed in the spec. For example, a feature currently undergoing the proposal process may appear here and eventually be taken off this list once the feature lands in the spec and the server deems it reasonable to do so. Servers can choose to enable some features only for some users, so clients should include authentication in the request to get all the features available for the logged-in user. If no authentication is provided, the server should only return the features available to all users. Servers may wish to keep advertising features here after they’ve been released into the spec to give clients a chance to upgrade appropriately. Additionally, clients should avoid using unstable features in their stable releases.

Rate-limited: No
Requires authentication: Optional

Request

No request parameters or request body.


Responses

Status Description
200 The versions supported by the server.

200 response

Name Type Description
unstable_features {string: boolean}

Experimental features the server supports. Features not listed here, or the lack of this property all together, indicate that a feature is not supported.

versions [string]

Required: The supported versions.

{
  "unstable_features": {
    "org.example.my_feature": true
  },
  "versions": [
    "r0.0.1",
    "v1.1"
  ]
}

GET /.well-known/matrix/support


Added in v1.10

Gets server admin contact and support page of the domain.

Like the well-known discovery URI, this should be accessed with the hostname of the homeserver by making a GET request to https://hostname/.well-known/matrix/support.

Note that this endpoint is not necessarily handled by the homeserver. It may be served by another webserver, used for discovering support information for the homeserver.

Rate-limited: No
Requires authentication: No

Request

No request parameters or request body.


Responses

Status Description
200 Server support information.
404 No server support information available.

200 response

Name Type Description
contacts [Contact]

Ways to contact the server administrator.

At least one of contacts or support_page is required. If only contacts is set, it must contain at least one item.

support_page URI

The URL of a page to give users help specific to the homeserver, like extra login/registration steps.

At least one of contacts or support_page is required.

Contact
Name Type Description
email_address Email Address

An email address to reach the administrator.

At least one of matrix_id or email_address is required.

matrix_id User ID

A Matrix User ID representing the administrator.

It could be an account registered on a different homeserver so the administrator can be contacted when the homeserver is down.

At least one of matrix_id or email_address is required.

role string

Required: An informal description of what the contact methods are used for.

m.role.admin is a catch-all role for any queries and m.role.security is intended for sensitive requests.

Unspecified roles are permitted through the use of Namespaced Identifiers.

One of: [m.role.admin, m.role.security].

{
  "contacts": [
    {
      "email_address": "[email protected]",
      "matrix_id": "@admin:example.org",
      "role": "m.role.admin"
    },
    {
      "email_address": "[email protected]",
      "role": "m.role.security"
    }
  ],
  "support_page": "https://example.org/support.html"
}

Client Authentication

Most API endpoints require the user to identify themselves by presenting previously obtained credentials in the form of an access token. An access token is typically obtained via the Login or Registration processes. Access tokens can expire; a new access token can be generated by using a refresh token.

This specification does not mandate a particular format for the access token. Clients should treat it as an opaque byte sequence. Servers are free to choose an appropriate format. Server implementors may like to investigate macaroons.

Using access tokens

Access tokens may be provided via a request header, using the Authentication Bearer scheme: Authorization: Bearer TheTokenHere.

Clients may alternatively provide the access token via a query string parameter: access_token=TheTokenHere. This method is deprecated to prevent the access token being leaked in access/HTTP logs and SHOULD NOT be used by clients.

Homeservers MUST support both methods.

[Changed in v1.11] Sending the access token as a query string parameter is now deprecated.

When credentials are required but missing or invalid, the HTTP call will return with a status of 401 and the error code, M_MISSING_TOKEN or M_UNKNOWN_TOKEN respectively. Note that an error code of M_UNKNOWN_TOKEN could mean one of four things:

  1. the access token was never valid.
  2. the access token has been logged out.
  3. the access token has been soft logged out.
  4. [Added in v1.3] the access token needs to be refreshed.

When a client receives an error code of M_UNKNOWN_TOKEN, it should:

  • attempt to refresh the token, if it has a refresh token;
  • if soft_logout is set to true, it can offer to re-log in the user, retaining any of the client’s persisted information;
  • otherwise, consider the user as having been logged out.

Relationship between access tokens and devices

Client devices are closely related to access tokens and refresh tokens. Matrix servers should record which device each access token and refresh token are assigned to, so that subsequent requests can be handled correctly. When a refresh token is used to generate a new access token and refresh token, the new access and refresh tokens are now bound to the device associated with the initial refresh token.

By default, the Login and Registration processes auto-generate a new device_id. A client is also free to generate its own device_id or, provided the user remains the same, reuse a device: in either case the client should pass the device_id in the request body. If the client sets the device_id, the server will invalidate any access and refresh tokens previously assigned to that device.

Refreshing access tokens

[Added in v1.3]

Access tokens can expire after a certain amount of time. Any HTTP calls that use an expired access token will return with an error code M_UNKNOWN_TOKEN, preferably with soft_logout: true. When a client receives this error and it has a refresh token, it should attempt to refresh the access token by calling /refresh. Clients can also refresh their access token at any time, even if it has not yet expired. If the token refresh succeeds, the client should use the new token for future requests, and can re-try previously-failed requests with the new token. When an access token is refreshed, a new refresh token may be returned; if a new refresh token is given, the old refresh token will be invalidated, and the new refresh token should be used when the access token needs to be refreshed.

The old refresh token remains valid until the new access token or refresh token is used, at which point the old refresh token is revoked. This ensures that if a client fails to receive or persist the new tokens, it will be able to repeat the refresh operation.

If the token refresh fails and the error response included a soft_logout: true property, then the client can treat it as a soft logout and attempt to obtain a new access token by re-logging in. If the error response does not include a soft_logout: true property, the client should consider the user as being logged out.

Handling of clients that do not support refresh tokens is up to the homeserver; clients indicate their support for refresh tokens by including a refresh_token: true property in the request body of the /login and /register endpoints. For example, homeservers may allow the use of non-expiring access tokens, or may expire access tokens anyways and rely on soft logout behaviour on clients that don’t support refreshing.

Soft logout

A client can be in a “soft logout” state if the server requires re-authentication before continuing, but does not want to invalidate the client’s session. The server indicates that the client is in a soft logout state by including a soft_logout: true parameter in an M_UNKNOWN_TOKEN error response; the soft_logout parameter defaults to false. If the soft_logout parameter is omitted or is false, this means the server has destroyed the session and the client should not reuse it. That is, any persisted state held by the client, such as encryption keys and device information, must not be reused and must be discarded. If soft_logout is true the client can reuse any persisted state.

[Changed in v1.3] A client that receives such a response can try to refresh its access token, if it has a refresh token available. If it does not have a refresh token available, or refreshing fails with soft_logout: true, the client can acquire a new access token by specifying the device ID it is already using to the login API.

[Changed in v1.12] A client that receives such a response together with an M_USER_LOCKED error code, cannot obtain a new access token until the account has been unlocked.

User-Interactive Authentication API

Overview

Some API endpoints require authentication that interacts with the user. The homeserver may provide many different ways of authenticating, such as user/password auth, login via a single-sign-on server (SSO), etc. This specification does not define how homeservers should authorise their users but instead defines the standard interface which implementations should follow so that ANY client can log in to ANY homeserver.

The process takes the form of one or more ‘stages’. At each stage the client submits a set of data for a given authentication type and awaits a response from the server, which will either be a final success or a request to perform an additional stage. This exchange continues until the final success.

For each endpoint, a server offers one or more ‘flows’ that the client can use to authenticate itself. Each flow comprises a series of stages, as described above. The client is free to choose which flow it follows, however the flow’s stages must be completed in order. Failing to follow the flows in order must result in an HTTP 401 response, as defined below. When all stages in a flow are complete, authentication is complete and the API call succeeds.

User-interactive API in the REST API

In the REST API described in this specification, authentication works by the client and server exchanging JSON dictionaries. The server indicates what authentication data it requires via the body of an HTTP 401 response, and the client submits that authentication data via the auth request parameter.

A client should first make a request with no auth parameter. The homeserver returns an HTTP 401 response, with a JSON body, as follows:

HTTP/1.1 401 Unauthorized
Content-Type: application/json
{
  "flows": [
    {
      "stages": [ "example.type.foo", "example.type.bar" ]
    },
    {
      "stages": [ "example.type.foo", "example.type.baz" ]
    }
  ],
  "params": {
      "example.type.baz": {
          "example_key": "foobar"
      }
  },
  "session": "xxxxxx"
}

In addition to the flows, this object contains some extra information:

  • params: This section contains any information that the client will need to know in order to use a given type of authentication. For each authentication type presented, that type may be present as a key in this dictionary. For example, the public part of an OAuth client ID could be given here.

  • session: This is a session identifier that the client must pass back to the homeserver, if one is provided, in subsequent attempts to authenticate in the same API call.

The client then chooses a flow and attempts to complete the first stage. It does this by resubmitting the same request with the addition of an auth key in the object that it submits. This dictionary contains a type key whose value is the name of the authentication type that the client is attempting to complete. It must also contain a session key with the value of the session key given by the homeserver, if one was given. It also contains other keys dependent on the auth type being attempted. For example, if the client is attempting to complete auth type example.type.foo, it might submit something like this:

POST /_matrix/client/v3/endpoint HTTP/1.1
Content-Type: application/json
{
  "a_request_parameter": "something",
  "another_request_parameter": "something else",
  "auth": {
      "type": "example.type.foo",
      "session": "xxxxxx",
      "example_credential": "verypoorsharedsecret"
  }
}

If the homeserver deems the authentication attempt to be successful but still requires more stages to be completed, it returns HTTP status 401 along with the same object as when no authentication was attempted, with the addition of the completed key which is an array of auth types the client has completed successfully:

HTTP/1.1 401 Unauthorized
Content-Type: application/json
{
  "completed": [ "example.type.foo" ],
  "flows": [
    {
      "stages": [ "example.type.foo", "example.type.bar" ]
    },
    {
      "stages": [ "example.type.foo", "example.type.baz" ]
    }
  ],
  "params": {
      "example.type.baz": {
          "example_key": "foobar"
      }
  },
  "session": "xxxxxx"
}

Individual stages may require more than one request to complete, in which case the response will be as if the request was unauthenticated with the addition of any other keys as defined by the auth type.

If the homeserver decides that an attempt on a stage was unsuccessful, but the client may make a second attempt, it returns the same HTTP status 401 response as above, with the addition of the standard errcode and error fields describing the error. For example:

HTTP/1.1 401 Unauthorized
Content-Type: application/json
{
  "errcode": "M_FORBIDDEN",
  "error": "Invalid password",
  "completed": [ "example.type.foo" ],
  "flows": [
    {
      "stages": [ "example.type.foo", "example.type.bar" ]
    },
    {
      "stages": [ "example.type.foo", "example.type.baz" ]
    }
  ],
  "params": {
      "example.type.baz": {
          "example_key": "foobar"
      }
  },
  "session": "xxxxxx"
}

If the request fails for a reason other than authentication, the server returns an error message in the standard format. For example:

HTTP/1.1 400 Bad request
Content-Type: application/json
{
  "errcode": "M_EXAMPLE_ERROR",
  "error": "Something was wrong"
}

If the client has completed all stages of a flow, the homeserver performs the API call and returns the result as normal. Completed stages cannot be retried by clients, therefore servers must return either a 401 response with the completed stages, or the result of the API call if all stages were completed when a client retries a stage.

Some authentication types may be completed by means other than through the Matrix client, for example, an email confirmation may be completed when the user clicks on the link in the email. In this case, the client retries the request with an auth dict containing only the session key. The response to this will be the same as if the client were attempting to complete an auth state normally, i.e. the request will either complete or request auth, with the presence or absence of that auth type in the ‘completed’ array indicating whether that stage is complete.

A request to an endpoint that uses User-Interactive Authentication never succeeds without auth. Homeservers may allow requests that don’t require auth by offering a stage with only the m.login.dummy auth type, but they must still give a 401 response to requests with no auth data.

Example

At a high level, the requests made for an API call completing an auth flow with three stages will resemble the following diagram:

    _______________________
    |       Stage 0         |
    | No auth               |
    |  ___________________  |
    | |_Request_1_________| | <-- Returns "session" key which is used throughout.
    |_______________________|
             |
             |
    _________V_____________
    |       Stage 1         |
    | type: "<auth type1>"  |
    |  ___________________  |
    | |_Request_1_________| |
    |_______________________|
             |
             |
    _________V_____________
    |       Stage 2         |
    | type: "<auth type2>"  |
    |  ___________________  |
    | |_Request_1_________| |
    |  ___________________  |
    | |_Request_2_________| |
    |  ___________________  |
    | |_Request_3_________| |
    |_______________________|
             |
             |
    _________V_____________
    |       Stage 3         |
    | type: "<auth type3>"  |
    |  ___________________  |
    | |_Request_1_________| | <-- Returns API response
    |_______________________|

Authentication types

This specification defines the following auth types:

  • m.login.password
  • m.login.recaptcha
  • m.login.sso
  • m.login.email.identity
  • m.login.msisdn
  • m.login.dummy
  • m.login.registration_token
Password-based
Type Description
m.login.password The client submits an identifier and secret password, both sent in plain-text.

To use this authentication type, clients should submit an auth dict as follows:

{
  "type": "m.login.password",
  "identifier": {
    ...
  },
  "password": "<password>",
  "session": "<session ID>"
}

where the identifier property is a user identifier object, as described in Identifier types.

For example, to authenticate using the user’s Matrix ID, clients would submit:

{
  "type": "m.login.password",
  "identifier": {
    "type": "m.id.user",
    "user": "<user_id or user localpart>"
  },
  "password": "<password>",
  "session": "<session ID>"
}

Alternatively reply using a 3PID bound to the user’s account on the homeserver using the /account/3pid API rather than giving the user explicitly as follows:

{
  "type": "m.login.password",
  "identifier": {
    "type": "m.id.thirdparty",
    "medium": "<The medium of the third-party identifier.>",
    "address": "<The third-party address of the user>"
  },
  "password": "<password>",
  "session": "<session ID>"
}

In the case that the homeserver does not know about the supplied 3PID, the homeserver must respond with 403 Forbidden.

Google ReCaptcha
Type Description
m.login.recaptcha The user completes a Google ReCaptcha 2.0 challenge.

To use this authentication type, clients should submit an auth dict as follows:

{
  "type": "m.login.recaptcha",
  "response": "<captcha response>",
  "session": "<session ID>"
}
Single Sign-On
Type Description
m.login.sso Authentication is supported by authorising with an external single sign-on provider.

A client wanting to complete authentication using SSO should use the Fallback mechanism. See SSO during User-Interactive Authentication for more information.

Email-based (identity / homeserver)
Type Description
m.login.email.identity Authentication is supported by authorising an email address with an identity server, or homeserver if supported.

Prior to submitting this, the client should authenticate with an identity server (or homeserver). After authenticating, the session information should be submitted to the homeserver.

To use this authentication type, clients should submit an auth dict as follows:

{
  "type": "m.login.email.identity",
  "threepid_creds": {
    "sid": "<identity server session id>",
    "client_secret": "<identity server client secret>",
    "id_server": "<url of identity server authed with, e.g. 'matrix.org:8090'>",
    "id_access_token": "<access token previously registered with the identity server>"
  },
  "session": "<session ID>"
}

Note that id_server (and therefore id_access_token) is optional if the /requestToken request did not include them.

Phone number/MSISDN-based (identity / homeserver)
Type Description
m.login.msisdn Authentication is supported by authorising a phone number with an identity server, or homeserver if supported.

Prior to submitting this, the client should authenticate with an identity server (or homeserver). After authenticating, the session information should be submitted to the homeserver.

To use this authentication type, clients should submit an auth dict as follows:

{
  "type": "m.login.msisdn",
  "threepid_creds": {
    "sid": "<identity server session id>",
    "client_secret": "<identity server client secret>",
    "id_server": "<url of identity server authed with, e.g. 'matrix.org:8090'>",
    "id_access_token": "<access token previously registered with the identity server>"
  },
  "session": "<session ID>"
}

Note that id_server (and therefore id_access_token) is optional if the /requestToken request did not include them.

Dummy Auth
Type Description
m.login.dummy Dummy authentication always succeeds and requires no extra parameters.

The purpose of dummy authentication is to allow servers to not require any form of User-Interactive Authentication to perform a request. It can also be used to differentiate flows where otherwise one flow would be a subset of another flow. e.g. if a server offers flows m.login.recaptcha and m.login.recaptcha, m.login.email.identity and the client completes the recaptcha stage first, the auth would succeed with the former flow, even if the client was intending to then complete the email auth stage. A server can instead send flows m.login.recaptcha, m.login.dummy and m.login.recaptcha, m.login.email.identity to fix the ambiguity.

To use this authentication type, clients should submit an auth dict with just the type and session, if provided:

{
  "type": "m.login.dummy",
  "session": "<session ID>"
}
Token-authenticated registration

[Added in v1.2]

Type Description
m.login.registration_token Registers an account with a pre-shared token for authentication

The m.login.registration_token authentication type is only valid on the /register endpoint.

This authentication type provides homeservers the ability to allow registrations to a limited set of people instead of either offering completely open registrations or completely closed registration (where the homeserver administrators create and distribute accounts).

The token required for this authentication type is shared out of band from Matrix and is an opaque string using the Opaque Identifier Grammar, with maximum length of 64 characters. The server can keep any number of tokens for any length of time/validity. Such cases might be a token limited to 100 uses or for the next 2 hours - after the tokens expire, they can no longer be used to create accounts.

To use this authentication type, clients should submit an auth dict with just the type, token, and session:

{
  "type": "m.login.registration_token",
  "token": "fBVFdqVE",
  "session": "<session ID>"
}

To determine if a token is valid before attempting to use it, the client can use the /validity API defined below. The API doesn’t guarantee that a token will be valid when used, but does avoid cases where the user finds out late in the registration process that their token has expired.

GET /_matrix/client/v1/register/m.login.registration_token/validity


Added in v1.2

Queries the server to determine if a given registration token is still valid at the time of request. This is a point-in-time check where the token might still expire by the time it is used.

Servers should be sure to rate limit this endpoint to avoid brute force attacks.

Rate-limited: Yes
Requires authentication: No

Request

Request parameters

query parameters
Name Type Description
token string

Required: The token to check validity of.


Responses

Status Description
200 The check has a result.
403 The homeserver does not permit registration and thus all tokens are considered invalid.
429 This request was rate-limited.

200 response

Name Type Description
valid boolean

Required: True if the token is still valid, false otherwise. This should additionally be false if the token is not a recognised token by the server.

{
  "valid": true
}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_FORBIDDEN",
  "error": "Registration is not enabled on this homeserver."
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}
Terms of service at registration

[Added in v1.11]

Type Description
m.login.terms Authentication requires the user to accept a set of policy documents.

The m.login.terms authentication type is only valid on the /register endpoint.

This authentication type is used when the homeserver requires new users to accept a given set of policy documents, such as a terms of service and a privacy policy. There may be many different types of documents, all of which are versioned and presented in (potentially) multiple languages.

When the server requires the user to accept some terms, it does so by returning a 401 response to the /register request, where the response body includes m.login.terms in the flows list, and the m.login.terms property in the params object has the structure shown below.

If a client encounters an invalid parameter, registration should stop with an error presented to the user.

The client should present the user with a checkbox to accept each policy, including a link to the provided URL. Once the user has done so, the client submits an auth dict with just the type and session, as follows, to indicate that all of the policies have been accepted:

{
  "type": "m.login.terms",
  "session": "<session ID>"
}

The server is expected to track which document versions it presented to the user during registration, if applicable.

Example

  1. A client might submit a registration request as follows:

    POST /_matrix/client/v3/register
    
    {
      "username": "cheeky_monkey",
      "password": "ilovebananas"
    }
    
  2. The server requires the user to accept some terms of service before registration, so returns the following response:

    HTTP/1.1 401 Unauthorized
    Content-Type: application/json
    
    {
      "flows": [
        { "stages": [ "m.login.terms" ] }
      ],
      "params": {
        "m.login.terms": {
          "policies": {
            "terms_of_service": {
              "version": "1.2",
              "en": {
                  "name": "Terms of Service",
                  "url": "https://example.org/somewhere/terms-1.2-en.html"
              },
              "fr": {
                  "name": "Conditions d'utilisation",
                  "url": "https://example.org/somewhere/terms-1.2-fr.html"
              }
            }
          }
        }
      },
      "session": "kasgjaelkgj"
    }
    
  3. The client presents the list of documents to the user, inviting them to accept the polices.

  4. The client repeats the registration request, confirming that the user has accepted the documents:

    POST /_matrix/client/v3/register
    
    {
      "username": "cheeky_monkey",
      "password": "ilovebananas",
      "auth": {
        "type": "m.login.terms",
        "session": "kasgjaelkgj"
      }
    }
    
  5. All authentication steps have now completed, so the request is successful:

    HTTP/1.1 200 OK
    Content-Type: application/json
    
    {
      "access_token": "abc123",
      "device_id": "GHTYAJCE",
      "user_id": "@cheeky_monkey:matrix.org"
    }
    

m.login.terms params


Schema for m.login.terms entry in the params object in a User-Interactive Authentication response.

m.login.terms params
Name Type Description
policies {string: Policy Definition}

Required: A map from “Policy ID” to the current definition of this policy document. The Policy ID is a unique identifier for a given policy document, using the Opaque Identifier Grammar.

Policy Definition
Name Type Description
version string

Required: The version of this policy document. This is provided as a convenience for the client, and uses the Opaque Identifier Grammar.

<Other properties> Policy Translation

Map from language codes to details of the document in that language. Language codes SHOULD be formatted as per Section 2.2 of RFC 5646, though some implementations may use an underscore instead of dash (for example, en_US instead of en-US).

Policy Translation
Name Type Description
name string

Required: The name of this document, in the appropriate language. An arbitrary string with no specified maximum length.

url URI

Required: A link to the text of this document, in the appropriate language. MUST be a valid URI with scheme https:// or http://. Insecure HTTP is discouraged.

Examples

{
  "policies": {
    "privacy_policy": {
      "en": {
        "name": "Privacy Policy",
        "url": "https://example.org/somewhere/privacy-1.2-en.html"
      },
      "fr": {
        "name": "Politique de confidentialité",
        "url": "https://example.org/somewhere/privacy-1.2-fr.html"
      },
      "version": "1.2"
    },
    "terms_of_service": {
      "en": {
        "name": "Terms of Service",
        "url": "https://example.org/somewhere/terms-1.2-en.html"
      },
      "fr": {
        "name": "Conditions d'utilisation",
        "url": "https://example.org/somewhere/terms-1.2-fr.html"
      },
      "version": "1.2"
    }
  }
}

Fallback

Clients cannot be expected to be able to know how to process every single login type. If a client does not know how to handle a given login type, it can direct the user to a web browser with the URL of a fallback page which will allow the user to complete that login step out-of-band in their web browser. The URL it should open is:

/_matrix/client/v3/auth/<auth type>/fallback/web?session=<session ID>

Where auth type is the type name of the stage it is attempting and session ID is the ID of the session given by the homeserver.

This MUST return an HTML page which can perform this authentication stage. This page must use the following JavaScript when the authentication has been completed:

if (window.onAuthDone) {
    window.onAuthDone();
} else if (window.opener && window.opener.postMessage) {
    window.opener.postMessage("authDone", "*");
}

This allows the client to either arrange for the global function onAuthDone to be defined in an embedded browser, or to use the HTML5 cross-document messaging API, to receive a notification that the authentication stage has been completed.

Once a client receives the notification that the authentication stage has been completed, it should resubmit the request with an auth dict with just the session ID:

{
  "session": "<session ID>"
}
Example

A client webapp might use the following JavaScript to open a popup window which will handle unknown login types:

/**
 * Arguments:
 *     homeserverUrl: the base url of the homeserver (e.g. "https://matrix.org")
 *
 *     apiEndpoint: the API endpoint being used (e.g.
 *        "/_matrix/client/v3/account/password")
 *
 *     loginType: the loginType being attempted (e.g. "m.login.recaptcha")
 *
 *     sessionID: the session ID given by the homeserver in earlier requests
 *
 *     onComplete: a callback which will be called with the results of the request
 */
function unknownLoginType(homeserverUrl, apiEndpoint, loginType, sessionID, onComplete) {
    var popupWindow;

    var eventListener = function(ev) {
        // check it's the right message from the right place.
        if (ev.data !== "authDone" || ev.origin !== homeserverUrl) {
            return;
        }

        // close the popup
        popupWindow.close();
        window.removeEventListener("message", eventListener);

        // repeat the request
        var requestBody = {
            auth: {
                session: sessionID,
            },
        };

        request({
            method:'POST', url:apiEndpoint, json:requestBody,
        }, onComplete);
    };

    window.addEventListener("message", eventListener);

    var url = homeserverUrl +
        "/_matrix/client/v3/auth/" +
        encodeURIComponent(loginType) +
        "/fallback/web?session=" +
        encodeURIComponent(sessionID);

   popupWindow = window.open(url);
}

Identifier types

Some authentication mechanisms use a user identifier object to identify a user. The user identifier object has a type field to indicate the type of identifier being used, and depending on the type, has other fields giving the information required to identify the user as described below.

This specification defines the following identifier types:

  • m.id.user
  • m.id.thirdparty
  • m.id.phone
Matrix User ID
Type Description
m.id.user The user is identified by their Matrix ID.

A client can identify a user using their Matrix ID. This can either be the fully qualified Matrix user ID, or just the localpart of the user ID.

"identifier": {
  "type": "m.id.user",
  "user": "<user_id or user localpart>"
}
Third-party ID
Type Description
m.id.thirdparty The user is identified by a third-party identifier in canonicalised form.

A client can identify a user using a 3PID associated with the user’s account on the homeserver, where the 3PID was previously associated using the /account/3pid API. See the 3PID Types Appendix for a list of Third-party ID media.

"identifier": {
  "type": "m.id.thirdparty",
  "medium": "<The medium of the third-party identifier>",
  "address": "<The canonicalised third-party address of the user>"
}
Phone number
Type Description
m.id.phone The user is identified by a phone number.

A client can identify a user using a phone number associated with the user’s account, where the phone number was previously associated using the /account/3pid API. The phone number can be passed in as entered by the user; the homeserver will be responsible for canonicalising it. If the client wishes to canonicalise the phone number, then it can use the m.id.thirdparty identifier type with a medium of msisdn instead.

"identifier": {
  "type": "m.id.phone",
  "country": "<The country that the phone number is from>",
  "phone": "<The phone number>"
}

The country is the two-letter uppercase ISO-3166-1 alpha-2 country code that the number in phone should be parsed as if it were dialled from.

Login

A client can obtain access tokens using the /login API.

Note that this endpoint does not currently use the User-Interactive Authentication API.

For a simple username/password login, clients should submit a /login request as follows:

{
  "type": "m.login.password",
  "identifier": {
    "type": "m.id.user",
    "user": "<user_id or user localpart>"
  },
  "password": "<password>"
}

Alternatively, a client can use a 3PID bound to the user’s account on the homeserver using the /account/3pid API rather than giving the user explicitly, as follows:

{
  "type": "m.login.password",
  "identifier": {
    "medium": "<The medium of the third-party identifier>",
    "address": "<The canonicalised third-party address of the user>"
  },
  "password": "<password>"
}

In the case that the homeserver does not know about the supplied 3PID, the homeserver must respond with 403 Forbidden.

To log in using a login token, clients should submit a /login request as follows:

{
  "type": "m.login.token",
  "token": "<login token>"
}

The token must encode the user ID, since there is no other identifying data in the request. In the case that the token is not valid, the homeserver must respond with 403 Forbidden and an error code of M_FORBIDDEN.

If the homeserver advertises m.login.sso as a viable flow, and the client supports it, the client should redirect the user to the /redirect endpoint for client login via SSO. After authentication is complete, the client will need to submit a /login request matching m.login.token.

[Added in v1.7] Already-authenticated clients can additionally generate a token for their user ID if supported by the homeserver using POST /login/get_token.

GET /_matrix/client/v3/login


Gets the homeserver’s supported login types to authenticate users. Clients should pick one of these and supply it as the type when logging in.

Rate-limited: Yes
Requires authentication: No

Request

No request parameters or request body.


Responses

Status Description
200 The login types the homeserver supports
429 This request was rate-limited.

200 response

Name Type Description
flows [LoginFlow]

The homeserver’s supported login types

LoginFlow
Name Type Description
get_login_token boolean

If type is m.login.token, an optional field to indicate to the unauthenticated client that the homeserver supports the POST /login/get_token endpoint. Note that supporting the endpoint does not necessarily indicate that the user attempting to log in will be able to generate such a token.

Added in v1.7

type string

Required: The login type. This is supplied as the type when logging in.

{
  "flows": [
    {
      "type": "m.login.password"
    },
    {
      "get_login_token": true,
      "type": "m.login.token"
    }
  ]
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

POST /_matrix/client/v3/login


Authenticates the user, and issues an access token they can use to authorize themself in subsequent requests.

If the client does not supply a device_id, the server must auto-generate one.

The returned access token must be associated with the device_id supplied by the client or generated by the server. The server may invalidate any access token previously associated with that device. See Relationship between access tokens and devices.

Rate-limited: Yes
Requires authentication: No

Request

Request body

Name Type Description
address string

Third-party identifier for the user. Deprecated in favour of identifier.

device_id string

ID of the client device. If this does not correspond to a known client device, a new device will be created. The given device ID must not be the same as a cross-signing key ID. The server will auto-generate a device_id if this is not specified.

identifier User identifier

Identification information for a user

initial_device_display_name string

A display name to assign to the newly-created device. Ignored if device_id corresponds to a known device.

medium string

When logging in using a third-party identifier, the medium of the identifier. Must be ’email’. Deprecated in favour of identifier.

password string

Required when type is m.login.password. The user’s password.

refresh_token boolean

If true, the client supports refresh tokens.

Added in v1.3

token string

Required when type is m.login.token. Part of Token-based login.

type string

Required: The login type being used.

This must be a type returned in one of the flows of the response of the GET /login endpoint, like m.login.password or m.login.token.

user string

The fully qualified user ID or just local part of the user ID, to log in. Deprecated in favour of identifier.

User identifier
Name Type Description
type string

Required: The type of identification. See Identifier types for supported values and additional property descriptions.

<Other properties>

Keys dependent on the identification type.

Request body example

{
  "identifier": {
    "type": "m.id.user",
    "user": "cheeky_monkey"
  },
  "initial_device_display_name": "Jungle Phone",
  "password": "ilovebananas",
  "type": "m.login.password"
}

Responses

Status Description
200 The user has been authenticated.
400 Part of the request was invalid. For example, the login type may not be recognised.
403

The login attempt failed. This can include one of the following error codes:

  • M_FORBIDDEN: The provided authentication data was incorrect or the requested device ID is the same as a cross-signing key ID.
  • M_USER_DEACTIVATED: The user has been deactivated.
429 This request was rate-limited.

200 response

Name Type Description
access_token string

Required: An access token for the account. This access token can then be used to authorize other requests.

device_id string

Required: ID of the logged-in device. Will be the same as the corresponding parameter in the request, if one was specified.

expires_in_ms integer

The lifetime of the access token, in milliseconds. Once the access token has expired a new access token can be obtained by using the provided refresh token. If no refresh token is provided, the client will need to re-log in to obtain a new access token. If not given, the client can assume that the access token will not expire.

Added in v1.3

home_server Server Name

The server_name of the homeserver on which the account has been registered.

Deprecated. Clients should extract the server_name from user_id (by splitting at the first colon) if they require it. Note also that homeserver is not spelt this way.

refresh_token string

A refresh token for the account. This token can be used to obtain a new access token when it expires by calling the /refresh endpoint.

Added in v1.3

user_id User ID

Required: The fully-qualified Matrix ID for the account.

well_known Discovery Information

Optional client configuration provided by the server. If present, clients SHOULD use the provided object to reconfigure themselves, optionally validating the URLs within. This object takes the same form as the one returned from .well-known autodiscovery.

Discovery Information
Name Type Description
m.homeserver Homeserver Information

Required: Used by clients to discover homeserver information.

m.identity_server Identity Server Information

Used by clients to discover identity server information.

<Other properties>

Application-dependent keys using Java package naming convention.

Homeserver Information
Name Type Description
base_url URI

Required: The base URL for the homeserver for client-server connections.

Identity Server Information
Name Type Description
base_url URI

Required: The base URL for the identity server for client-server connections.

{
  "access_token": "abc123",
  "device_id": "GHTYAJCE",
  "expires_in_ms": 60000,
  "refresh_token": "def456",
  "user_id": "@cheeky_monkey:matrix.org",
  "well_known": {
    "m.homeserver": {
      "base_url": "https://example.org"
    },
    "m.identity_server": {
      "base_url": "https://id.example.org"
    }
  }
}

400 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_UNKNOWN",
  "error": "Bad login type."
}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_FORBIDDEN"
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

POST /_matrix/client/v1/login/get_token


Added in v1.7

Optional endpoint - the server is not required to implement this endpoint if it does not intend to use or support this functionality.

This API endpoint uses the User-Interactive Authentication API.

An already-authenticated client can call this endpoint to generate a single-use, time-limited, token for an unauthenticated client to log in with, becoming logged in as the same user which called this endpoint. The unauthenticated client uses the generated token in a m.login.token login flow with the homeserver.

Clients, both authenticated and unauthenticated, might wish to hide user interface which exposes this feature if the server is not offering it. Authenticated clients can check for support on a per-user basis with the m.get_login_token capability, while unauthenticated clients can detect server support by looking for an m.login.token login flow with get_login_token: true on GET /login.

In v1.7 of the specification, transmission of the generated token to an unauthenticated client is left as an implementation detail. Future MSCs such as MSC3906 might standardise a way to transmit the token between clients.

The generated token MUST only be valid for a single login, enforced by the server. Clients which intend to log in multiple devices must generate a token for each.

With other User-Interactive Authentication (UIA)-supporting endpoints, servers sometimes do not re-prompt for verification if the session recently passed UIA. For this endpoint, servers MUST always re-prompt the user for verification to ensure explicit consent is gained for each additional client.

Servers are encouraged to apply stricter than normal rate limiting to this endpoint, such as maximum of 1 request per minute.

Rate-limited: Yes
Requires authentication: Yes

Request

Request body

Name Type Description
auth Authentication Data

Additional authentication information for the user-interactive authentication API.

Authentication Data
Name Type Description
session string

The value of the session key given by the homeserver.

type string

The authentication type that the client is attempting to complete. May be omitted if session is given, and the client is reissuing a request which it believes has been completed out-of-band (for example, via the fallback mechanism).

<Other properties>

Keys dependent on the login type

Request body example

{
  "auth": {
    "example_credential": "verypoorsharedsecret",
    "session": "xxxxx",
    "type": "example.type.foo"
  }
}

Responses

Status Description
200 The login token an unauthenticated client can use to log in as the requesting user.
400

The request was malformed, or the user does not have an ability to generate tokens for their devices, as implied by the User-Interactive Authentication API.

Clients should verify whether the user has an ability to call this endpoint with the m.get_login_token capability.

401 The homeserver requires additional authentication information.
429 This request was rate-limited.

200 response

Name Type Description
expires_in_ms integer

Required: The time remaining in milliseconds until the homeserver will no longer accept the token. 120000 (2 minutes) is recommended as a default.

login_token string

Required: The login token for the m.login.token login flow.

{
  "expires_in_ms": 120000,
  "login_token": "<opaque string>"
}

400 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_UNKNOWN",
  "error": "An unknown error occurred"
}

401 response

Authentication response
Name Type Description
completed [string]

A list of the stages the client has completed successfully

flows [Flow information]

Required: A list of the login flows supported by the server for this API.

params {string: object}

Contains any information that the client will need to know in order to use a given type of authentication. For each login type presented, that type may be present as a key in this dictionary. For example, the public part of an OAuth client ID could be given here.

session string

This is a session identifier that the client must pass back to the home server, if one is provided, in subsequent attempts to authenticate in the same API call.

Flow information
Name Type Description
stages [string]

Required: The login type of each of the stages required to complete this authentication flow

{
  "completed": [
    "example.type.foo"
  ],
  "flows": [
    {
      "stages": [
        "example.type.foo"
      ]
    }
  ],
  "params": {
    "example.type.baz": {
      "example_key": "foobar"
    }
  },
  "session": "xxxxxxyz"
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

POST /_matrix/client/v3/refresh


Added in v1.3

Refresh an access token. Clients should use the returned access token when making subsequent API calls, and store the returned refresh token (if given) in order to refresh the new access token when necessary.

After an access token has been refreshed, a server can choose to invalidate the old access token immediately, or can choose not to, for example if the access token would expire soon anyways. Clients should not make any assumptions about the old access token still being valid, and should use the newly provided access token instead.

The old refresh token remains valid until the new access token or refresh token is used, at which point the old refresh token is revoked.

Note that this endpoint does not require authentication via an access token. Authentication is provided via the refresh token.

Application Service identity assertion is disabled for this endpoint.

Rate-limited: Yes
Requires authentication: No

Request

Request body

Name Type Description
refresh_token string

Required: The refresh token

Request body example

{
  "refresh_token": "some_token"
}

Responses

Status Description
200 A new access token and refresh token were generated.
401 The provided token was unknown, or has already been used.
429 This request was rate-limited.

200 response

Name Type Description
access_token string

Required: The new access token to use.

expires_in_ms integer

The lifetime of the access token, in milliseconds. If not given, the client can assume that the access token will not expire.

refresh_token string

The new refresh token to use when the access token needs to be refreshed again. If not given, the old refresh token can be re-used.

{
  "access_token": "a_new_token",
  "expires_in_ms": 60000,
  "refresh_token": "another_new_token"
}

401 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_UNKNOWN_TOKEN",
  "error": "Soft logged out",
  "soft_logout": true
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

POST /_matrix/client/v3/logout


Invalidates an existing access token, so that it can no longer be used for authorization. The device associated with the access token is also deleted. Device keys for the device are deleted alongside the device.

Rate-limited: No
Requires authentication: Yes

Request

No request parameters or request body.


Responses

Status Description
200 The access token used in the request was successfully invalidated.

200 response

{}

POST /_matrix/client/v3/logout/all


Invalidates all access tokens for a user, so that they can no longer be used for authorization. This includes the access token that made this request. All devices for the user are also deleted. Device keys for the device are deleted alongside the device.

This endpoint does not use the User-Interactive Authentication API because User-Interactive Authentication is designed to protect against attacks where the someone gets hold of a single access token then takes over the account. This endpoint invalidates all access tokens for the user, including the token used in the request, and therefore the attacker is unable to take over the account in this way.

Rate-limited: No
Requires authentication: Yes

Request

No request parameters or request body.


Responses

Status Description
200 The user’s access tokens were successfully invalidated.

200 response

{}

Appservice Login

[Added in v1.2]

An appservice can log in by providing a valid appservice token and a user within the appservice’s namespace.

Appservices do not need to log in as individual users in all cases, as they can perform Identity Assertion using the appservice token. However, if the appservice needs a scoped token for a single user then they can use this API instead.

This request must be authenticated by the appservice as_token (see Client Authentication on how to provide the token).

To use this login type, clients should submit a /login request as follows:

{
  "type": "m.login.application_service",
  "identifier": {
    "type": "m.id.user",
    "user": "<user_id or user localpart>"
  }
}

If the access token is not valid, does not correspond to an appservice or the user has not previously been registered then the homeserver will respond with an errcode of M_FORBIDDEN.

If the access token does correspond to an appservice, but the user id does not lie within its namespace then the homeserver will respond with an errcode of M_EXCLUSIVE.

Login Fallback

If a client does not recognize any or all login flows it can use the fallback login API:

GET /_matrix/static/client/login/

This returns an HTML and JavaScript page which can perform the entire login process. The page will attempt to call the JavaScript function window.matrixLogin.onLogin(response) when login has been successfully completed. The argument, response, is the JSON response body of POST /_matrix/client/v3/login parsed into a JavaScript object.

[Added in v1.1] Non-credential parameters valid for the /login endpoint can be provided as query string parameters here. These are to be forwarded to the login endpoint during the login process. For example:

GET /_matrix/static/client/login/?device_id=GHTYAJCE

Account registration and management

POST /_matrix/client/v3/account/deactivate


Deactivate the user’s account, removing all ability for the user to login again.

This API endpoint uses the User-Interactive Authentication API.

An access token should be submitted to this endpoint if the client has an active session.

The homeserver may change the flows available depending on whether a valid access token is provided.

Unlike other endpoints, this endpoint does not take an id_access_token parameter because the homeserver is expected to sign the request to the identity server instead.

Rate-limited: Yes
Requires authentication: Optional

Request

Request body

Name Type Description
auth Authentication Data

Additional authentication information for the user-interactive authentication API.

erase boolean

Whether the user would like their content to be erased as much as possible from the server.

Erasure means that any users (or servers) which join the room after the erasure request are served redacted copies of the events sent by this account. Users which had visibility on those events prior to the erasure are still able to see unredacted copies. No redactions are sent and the erasure request is not shared over federation, so other servers might still serve unredacted copies.

The server should additionally erase any non-event data associated with the user, such as account data and contact 3PIDs.

Defaults to false if not present.

Added in v1.10

id_server string

The identity server to unbind all of the user’s 3PIDs from. If not provided, the homeserver MUST use the id_server that was originally use to bind each identifier. If the homeserver does not know which id_server that was, it must return an id_server_unbind_result of no-support.

Authentication Data
Name Type Description
session string

The value of the session key given by the homeserver.

type string

The authentication type that the client is attempting to complete. May be omitted if session is given, and the client is reissuing a request which it believes has been completed out-of-band (for example, via the fallback mechanism).

<Other properties>

Keys dependent on the login type

Request body example

{
  "auth": {
    "example_credential": "verypoorsharedsecret",
    "session": "xxxxx",
    "type": "example.type.foo"
  },
  "id_server": "example.org"
}

Responses

Status Description
200 The account has been deactivated.
401 The homeserver requires additional authentication information.
429 This request was rate-limited.

200 response

Name Type Description
id_server_unbind_result string

Required: An indicator as to whether or not the homeserver was able to unbind the user’s 3PIDs from the identity server(s). success indicates that all identifiers have been unbound from the identity server while no-support indicates that one or more identifiers failed to unbind due to the identity server refusing the request or the homeserver being unable to determine an identity server to unbind from. This must be success if the homeserver has no identifiers to unbind for the user.

One of: [success, no-support].

{
  "id_server_unbind_result": "success"
}

401 response

Authentication response
Name Type Description
completed [string]

A list of the stages the client has completed successfully

flows [Flow information]

Required: A list of the login flows supported by the server for this API.

params {string: object}

Contains any information that the client will need to know in order to use a given type of authentication. For each login type presented, that type may be present as a key in this dictionary. For example, the public part of an OAuth client ID could be given here.

session string

This is a session identifier that the client must pass back to the home server, if one is provided, in subsequent attempts to authenticate in the same API call.

Flow information
Name Type Description
stages [string]

Required: The login type of each of the stages required to complete this authentication flow

{
  "completed": [
    "example.type.foo"
  ],
  "flows": [
    {
      "stages": [
        "example.type.foo"
      ]
    }
  ],
  "params": {
    "example.type.baz": {
      "example_key": "foobar"
    }
  },
  "session": "xxxxxxyz"
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

POST /_matrix/client/v3/account/password


Changes the password for an account on this homeserver.

This API endpoint uses the User-Interactive Authentication API to ensure the user changing the password is actually the owner of the account.

An access token should be submitted to this endpoint if the client has an active session.

The homeserver may change the flows available depending on whether a valid access token is provided. The homeserver SHOULD NOT revoke the access token provided in the request. Whether other access tokens for the user are revoked depends on the request parameters.

Rate-limited: Yes
Requires authentication: Optional

Request

Request body

Name Type Description
auth Authentication Data

Additional authentication information for the user-interactive authentication API.

logout_devices boolean

Whether the user’s other access tokens, and their associated devices, should be revoked if the request succeeds. Defaults to true.

When false, the server can still take advantage of the soft logout method for the user’s remaining devices.

new_password string

Required: The new password for the account.

Authentication Data
Name Type Description
session string

The value of the session key given by the homeserver.

type string

The authentication type that the client is attempting to complete. May be omitted if session is given, and the client is reissuing a request which it believes has been completed out-of-band (for example, via the fallback mechanism).

<Other properties>

Keys dependent on the login type

Request body example

{
  "auth": {
    "example_credential": "verypoorsharedsecret",
    "session": "xxxxx",
    "type": "example.type.foo"
  },
  "logout_devices": true,
  "new_password": "ihatebananas"
}

Responses

Status Description
200 The password has been changed.
401 The homeserver requires additional authentication information.
429 This request was rate-limited.

200 response

{}

401 response

Authentication response
Name Type Description
completed [string]

A list of the stages the client has completed successfully

flows [Flow information]

Required: A list of the login flows supported by the server for this API.

params {string: object}

Contains any information that the client will need to know in order to use a given type of authentication. For each login type presented, that type may be present as a key in this dictionary. For example, the public part of an OAuth client ID could be given here.

session string

This is a session identifier that the client must pass back to the home server, if one is provided, in subsequent attempts to authenticate in the same API call.

Flow information
Name Type Description
stages [string]

Required: The login type of each of the stages required to complete this authentication flow

{
  "completed": [
    "example.type.foo"
  ],
  "flows": [
    {
      "stages": [
        "example.type.foo"
      ]
    }
  ],
  "params": {
    "example.type.baz": {
      "example_key": "foobar"
    }
  },
  "session": "xxxxxxyz"
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

POST /_matrix/client/v3/account/password/email/requestToken


The homeserver must check that the given email address is associated with an account on this homeserver. This API should be used to request validation tokens when authenticating for the /account/password endpoint.

This API’s parameters and response are identical to that of the /register/email/requestToken endpoint, except that M_THREEPID_NOT_FOUND may be returned if no account matching the given email address could be found. The server may instead send an email to the given address prompting the user to create an account. M_THREEPID_IN_USE may not be returned.

The homeserver should validate the email itself, either by sending a validation email itself or by using a service it has control over.

Rate-limited: No
Requires authentication: No

Request

Request body

Name Type Description
client_secret string

Required: A unique string generated by the client, and used to identify the validation attempt. It must be a string consisting of the characters [0-9a-zA-Z.=_-]. Its length must not exceed 255 characters and it must not be empty.

email string

Required: The email address to validate.

id_access_token string

An access token previously registered with the identity server. Servers can treat this as optional to distinguish between r0.5-compatible clients and this specification version.

Required if an id_server is supplied.

id_server string

The hostname of the identity server to communicate with. May optionally include a port. This parameter is ignored when the homeserver handles 3PID verification.

This parameter is deprecated with a plan to be removed in a future specification version for /account/password and /register requests.

next_link string

Optional. When the validation is completed, the identity server will redirect the user to this URL. This option is ignored when submitting 3PID validation information through a POST request.

send_attempt integer

Required: The server will only send an email if the send_attempt is a number greater than the most recent one which it has seen, scoped to that email + client_secret pair. This is to avoid repeatedly sending the same email in the case of request retries between the POSTing user and the identity server. The client should increment this value if they desire a new email (e.g. a reminder) to be sent. If they do not, the server should respond with success but not resend the email.

Request body example

{
  "client_secret": "monkeys_are_GREAT",
  "email": "[email protected]",
  "id_server": "id.example.com",
  "next_link": "https://example.org/congratulations.html",
  "send_attempt": 1
}

Responses

Status Description
200 An email was sent to the given address.
400 The referenced third-party identifier is not recognised by the homeserver, or the request was invalid. The error code M_SERVER_NOT_TRUSTED can be returned if the server does not trust/support the identity server provided in the request.
403 The homeserver does not allow the third-party identifier as a contact option.

200 response

RequestTokenResponse
Name Type Description
sid string

Required: The session ID. Session IDs are opaque strings that must consist entirely of the characters [0-9a-zA-Z.=_-]. Their length must not exceed 255 characters and they must not be empty.

submit_url URI

An optional field containing a URL where the client must submit the validation token to, with identical parameters to the Identity Service API’s POST /validate/email/submitToken endpoint (without the requirement for an access token). The homeserver must send this token to the user (if applicable), who should then be prompted to provide it to the client.

If this field is not present, the client can assume that verification will happen without the client’s involvement provided the homeserver advertises this specification version in the /versions response (ie: r0.5.0).

{
  "sid": "123abc",
  "submit_url": "https://example.org/path/to/submitToken"
}

400 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_THREEPID_NOT_FOUND",
  "error": "Email not found"
}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_THREEPID_DENIED",
  "error": "Third-party identifier is not allowed"
}

POST /_matrix/client/v3/account/password/msisdn/requestToken


The homeserver must check that the given phone number is associated with an account on this homeserver. This API should be used to request validation tokens when authenticating for the /account/password endpoint.

This API’s parameters and response are identical to that of the /register/msisdn/requestToken endpoint, except that M_THREEPID_NOT_FOUND may be returned if no account matching the given phone number could be found. The server may instead send the SMS to the given phone number prompting the user to create an account. M_THREEPID_IN_USE may not be returned.

The homeserver should validate the phone number itself, either by sending a validation message itself or by using a service it has control over.

Rate-limited: No
Requires authentication: No

Request

Request body

Name Type Description
client_secret string

Required: A unique string generated by the client, and used to identify the validation attempt. It must be a string consisting of the characters [0-9a-zA-Z.=_-]. Its length must not exceed 255 characters and it must not be empty.

country string

Required: The two-letter uppercase ISO-3166-1 alpha-2 country code that the number in phone_number should be parsed as if it were dialled from.

id_access_token string

An access token previously registered with the identity server. Servers can treat this as optional to distinguish between r0.5-compatible clients and this specification version.

Required if an id_server is supplied.

id_server string

The hostname of the identity server to communicate with. May optionally include a port. This parameter is ignored when the homeserver handles 3PID verification.

This parameter is deprecated with a plan to be removed in a future specification version for /account/password and /register requests.

next_link string

Optional. When the validation is completed, the identity server will redirect the user to this URL. This option is ignored when submitting 3PID validation information through a POST request.

phone_number string

Required: The phone number to validate.

send_attempt integer

Required: The server will only send an SMS if the send_attempt is a number greater than the most recent one which it has seen, scoped to that country + phone_number + client_secret triple. This is to avoid repeatedly sending the same SMS in the case of request retries between the POSTing user and the identity server. The client should increment this value if they desire a new SMS (e.g. a reminder) to be sent.

Request body example

{
  "client_secret": "monkeys_are_GREAT",
  "country": "GB",
  "id_server": "id.example.com",
  "next_link": "https://example.org/congratulations.html",
  "phone_number": "07700900001",
  "send_attempt": 1
}

Responses

Status Description
200 An SMS message was sent to the given phone number.
400 The referenced third-party identifier is not recognised by the homeserver, or the request was invalid. The error code M_SERVER_NOT_TRUSTED can be returned if the server does not trust/support the identity server provided in the request.
403 The homeserver does not allow the third-party identifier as a contact option.

200 response

RequestTokenResponse
Name Type Description
sid string

Required: The session ID. Session IDs are opaque strings that must consist entirely of the characters [0-9a-zA-Z.=_-]. Their length must not exceed 255 characters and they must not be empty.

submit_url URI

An optional field containing a URL where the client must submit the validation token to, with identical parameters to the Identity Service API’s POST /validate/email/submitToken endpoint (without the requirement for an access token). The homeserver must send this token to the user (if applicable), who should then be prompted to provide it to the client.

If this field is not present, the client can assume that verification will happen without the client’s involvement provided the homeserver advertises this specification version in the /versions response (ie: r0.5.0).

{
  "sid": "123abc",
  "submit_url": "https://example.org/path/to/submitToken"
}

400 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_THREEPID_NOT_FOUND",
  "error": "Phone number not found"
}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_THREEPID_DENIED",
  "error": "Third-party identifier is not allowed"
}

POST /_matrix/client/v3/register


This API endpoint uses the User-Interactive Authentication API, except in the cases where a guest account is being registered.

Register for an account on this homeserver.

There are two kinds of user account:

  • user accounts. These accounts may use the full API described in this specification.

  • guest accounts. These accounts may have limited permissions and may not be supported by all servers.

If registration is successful, this endpoint will issue an access token the client can use to authorize itself in subsequent requests.

If the client does not supply a device_id, the server must auto-generate one.

The server SHOULD register an account with a User ID based on the username provided, if any. Note that the grammar of Matrix User ID localparts is restricted, so the server MUST either map the provided username onto a user_id in a logical manner, or reject any username which does not comply to the grammar with M_INVALID_USERNAME.

Matrix clients MUST NOT assume that localpart of the registered user_id matches the provided username.

The returned access token must be associated with the device_id supplied by the client or generated by the server. The server may invalidate any access token previously associated with that device. See Relationship between access tokens and devices.

When registering a guest account, all parameters in the request body with the exception of initial_device_display_name MUST BE ignored by the server. The server MUST pick a device_id for the account regardless of input.

Any user ID returned by this API must conform to the grammar given in the Matrix specification.

Rate-limited: Yes
Requires authentication: No

Request

Request parameters

query parameters
Name Type Description
kind string

The kind of account to register. Defaults to user.

One of: [guest, user].

Request body

Name Type Description
auth Authentication Data

Additional authentication information for the user-interactive authentication API. Note that this information is not used to define how the registered user should be authenticated, but is instead used to authenticate the register call itself.

device_id string

ID of the client device. If this does not correspond to a known client device, a new device will be created. The server will auto-generate a device_id if this is not specified.

inhibit_login boolean

If true, an access_token and device_id should not be returned from this call, therefore preventing an automatic login. Defaults to false.

initial_device_display_name string

A display name to assign to the newly-created device. Ignored if device_id corresponds to a known device.

password string

The desired password for the account.

refresh_token boolean

If true, the client supports refresh tokens.

Added in v1.3

username string

The basis for the localpart of the desired Matrix ID. If omitted, the homeserver MUST generate a Matrix ID local part.

Authentication Data
Name Type Description
session string

The value of the session key given by the homeserver.

type string

The authentication type that the client is attempting to complete. May be omitted if session is given, and the client is reissuing a request which it believes has been completed out-of-band (for example, via the fallback mechanism).

<Other properties>

Keys dependent on the login type

Request body example

{
  "auth": {
    "example_credential": "verypoorsharedsecret",
    "session": "xxxxx",
    "type": "example.type.foo"
  },
  "device_id": "GHTYAJCE",
  "initial_device_display_name": "Jungle Phone",
  "password": "ilovebananas",
  "username": "cheeky_monkey"
}

Responses

Status Description
200 The account has been registered.
400

Part of the request was invalid. This may include one of the following error codes:

  • M_USER_IN_USE : The desired user ID is already taken.
  • M_INVALID_USERNAME : The desired user ID is not a valid user name.
  • M_EXCLUSIVE : The desired user ID is in the exclusive namespace claimed by an application service.

These errors may be returned at any stage of the registration process, including after authentication if the requested user ID was registered whilst the client was performing authentication.

Homeservers MUST perform the relevant checks and return these codes before performing User-Interactive Authentication, although they may also return them after authentication is completed if, for example, the requested user ID was registered whilst the client was performing authentication.

401 The homeserver requires additional authentication information.
403 The homeserver does not permit registering the account. This response can be used to identify that a particular kind of account is not allowed, or that registration is generally not supported by the homeserver.
429 This request was rate-limited.

200 response

Name Type Description
access_token string

An access token for the account. This access token can then be used to authorize other requests. Required if the inhibit_login option is false.

device_id string

ID of the registered device. Will be the same as the corresponding parameter in the request, if one was specified. Required if the inhibit_login option is false.

expires_in_ms integer

The lifetime of the access token, in milliseconds. Once the access token has expired a new access token can be obtained by using the provided refresh token. If no refresh token is provided, the client will need to re-log in to obtain a new access token. If not given, the client can assume that the access token will not expire.

Omitted if the inhibit_login option is true.

Added in v1.3

home_server string

The server_name of the homeserver on which the account has been registered.

Deprecated. Clients should extract the server_name from user_id (by splitting at the first colon) if they require it. Note also that homeserver is not spelt this way.

refresh_token string

A refresh token for the account. This token can be used to obtain a new access token when it expires by calling the /refresh endpoint.

Omitted if the inhibit_login option is true.

Added in v1.3

user_id string

Required: The fully-qualified Matrix user ID (MXID) that has been registered.

Any user ID returned by this API must conform to the grammar given in the Matrix specification.

{
  "access_token": "abc123",
  "device_id": "GHTYAJCE",
  "user_id": "@cheeky_monkey:matrix.org"
}

400 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_USER_IN_USE",
  "error": "Desired user ID is already taken."
}

401 response

Authentication response
Name Type Description
completed [string]

A list of the stages the client has completed successfully

flows [Flow information]

Required: A list of the login flows supported by the server for this API.

params {string: object}

Contains any information that the client will need to know in order to use a given type of authentication. For each login type presented, that type may be present as a key in this dictionary. For example, the public part of an OAuth client ID could be given here.

session string

This is a session identifier that the client must pass back to the home server, if one is provided, in subsequent attempts to authenticate in the same API call.

Flow information
Name Type Description
stages [string]

Required: The login type of each of the stages required to complete this authentication flow

{
  "completed": [
    "example.type.foo"
  ],
  "flows": [
    {
      "stages": [
        "example.type.foo"
      ]
    }
  ],
  "params": {
    "example.type.baz": {
      "example_key": "foobar"
    }
  },
  "session": "xxxxxxyz"
}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_FORBIDDEN",
  "error": "Registration is disabled"
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

GET /_matrix/client/v3/register/available


Checks to see if a username is available, and valid, for the server.

The server should check to ensure that, at the time of the request, the username requested is available for use. This includes verifying that an application service has not claimed the username and that the username fits the server’s desired requirements (for example, a server could dictate that it does not permit usernames with underscores).

Matrix clients may wish to use this API prior to attempting registration, however the clients must also be aware that using this API does not normally reserve the username. This can mean that the username becomes unavailable between checking its availability and attempting to register it.

Rate-limited: Yes
Requires authentication: No

Request

Request parameters

query parameters
Name Type Description
username string

Required: The username to check the availability of.


Responses

Status Description
200 The username is available
400

Part of the request was invalid or the username is not available. This may include one of the following error codes:

  • M_USER_IN_USE : The desired username is already taken.
  • M_INVALID_USERNAME : The desired username is not a valid user name.
  • M_EXCLUSIVE : The desired username is in the exclusive namespace claimed by an application service.
429 This request was rate-limited.

200 response

Name Type Description
available boolean

A flag to indicate that the username is available. This should always be true when the server replies with 200 OK.

{
  "available": true
}

400 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_USER_IN_USE",
  "error": "Desired user ID is already taken."
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

POST /_matrix/client/v3/register/email/requestToken


The homeserver must check that the given email address is not already associated with an account on this homeserver. The homeserver should validate the email itself, either by sending a validation email itself or by using a service it has control over.

Rate-limited: No
Requires authentication: No

Request

Request body

Name Type Description
client_secret string

Required: A unique string generated by the client, and used to identify the validation attempt. It must be a string consisting of the characters [0-9a-zA-Z.=_-]. Its length must not exceed 255 characters and it must not be empty.

email string

Required: The email address to validate.

id_access_token string

An access token previously registered with the identity server. Servers can treat this as optional to distinguish between r0.5-compatible clients and this specification version.

Required if an id_server is supplied.

id_server string

The hostname of the identity server to communicate with. May optionally include a port. This parameter is ignored when the homeserver handles 3PID verification.

This parameter is deprecated with a plan to be removed in a future specification version for /account/password and /register requests.

next_link string

Optional. When the validation is completed, the identity server will redirect the user to this URL. This option is ignored when submitting 3PID validation information through a POST request.

send_attempt integer

Required: The server will only send an email if the send_attempt is a number greater than the most recent one which it has seen, scoped to that email + client_secret pair. This is to avoid repeatedly sending the same email in the case of request retries between the POSTing user and the identity server. The client should increment this value if they desire a new email (e.g. a reminder) to be sent. If they do not, the server should respond with success but not resend the email.

Request body example

{
  "client_secret": "monkeys_are_GREAT",
  "email": "[email protected]",
  "id_server": "id.example.com",
  "next_link": "https://example.org/congratulations.html",
  "send_attempt": 1
}

Responses

Status Description
200 An email has been sent to the specified address. Note that this may be an email containing the validation token or it may be informing the user of an error.
400

Part of the request was invalid. This may include one of the following error codes:

  • M_THREEPID_IN_USE : The email address is already registered to an account on this server. However, if the homeserver has the ability to send email, it is recommended that the server instead send an email to the user with instructions on how to reset their password. This prevents malicious parties from being able to determine if a given email address has an account on the homeserver in question.
  • M_SERVER_NOT_TRUSTED : The id_server parameter refers to an identity server that is not trusted by this homeserver.
403 The homeserver does not permit the address to be bound.

200 response

RequestTokenResponse
Name Type Description
sid string

Required: The session ID. Session IDs are opaque strings that must consist entirely of the characters [0-9a-zA-Z.=_-]. Their length must not exceed 255 characters and they must not be empty.

submit_url URI

An optional field containing a URL where the client must submit the validation token to, with identical parameters to the Identity Service API’s POST /validate/email/submitToken endpoint (without the requirement for an access token). The homeserver must send this token to the user (if applicable), who should then be prompted to provide it to the client.

If this field is not present, the client can assume that verification will happen without the client’s involvement provided the homeserver advertises this specification version in the /versions response (ie: r0.5.0).

{
  "sid": "123abc",
  "submit_url": "https://example.org/path/to/submitToken"
}

400 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_THREEPID_IN_USE",
  "error": "The specified address is already in use"
}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_THREEPID_DENIED",
  "error": "Third-party identifier is not allowed"
}

POST /_matrix/client/v3/register/msisdn/requestToken


The homeserver must check that the given phone number is not already associated with an account on this homeserver. The homeserver should validate the phone number itself, either by sending a validation message itself or by using a service it has control over.

Rate-limited: No
Requires authentication: No

Request

Request body

Name Type Description
client_secret string

Required: A unique string generated by the client, and used to identify the validation attempt. It must be a string consisting of the characters [0-9a-zA-Z.=_-]. Its length must not exceed 255 characters and it must not be empty.

country string

Required: The two-letter uppercase ISO-3166-1 alpha-2 country code that the number in phone_number should be parsed as if it were dialled from.

id_access_token string

An access token previously registered with the identity server. Servers can treat this as optional to distinguish between r0.5-compatible clients and this specification version.

Required if an id_server is supplied.

id_server string

The hostname of the identity server to communicate with. May optionally include a port. This parameter is ignored when the homeserver handles 3PID verification.

This parameter is deprecated with a plan to be removed in a future specification version for /account/password and /register requests.

next_link string

Optional. When the validation is completed, the identity server will redirect the user to this URL. This option is ignored when submitting 3PID validation information through a POST request.

phone_number string

Required: The phone number to validate.

send_attempt integer

Required: The server will only send an SMS if the send_attempt is a number greater than the most recent one which it has seen, scoped to that country + phone_number + client_secret triple. This is to avoid repeatedly sending the same SMS in the case of request retries between the POSTing user and the identity server. The client should increment this value if they desire a new SMS (e.g. a reminder) to be sent.

Request body example

{
  "client_secret": "monkeys_are_GREAT",
  "country": "GB",
  "id_server": "id.example.com",
  "next_link": "https://example.org/congratulations.html",
  "phone_number": "07700900001",
  "send_attempt": 1
}

Responses

Status Description
200 An SMS message has been sent to the specified phone number. Note that this may be an SMS message containing the validation token or it may be informing the user of an error.
400

Part of the request was invalid. This may include one of the following error codes:

  • M_THREEPID_IN_USE : The phone number is already registered to an account on this server. However, if the homeserver has the ability to send SMS message, it is recommended that the server instead send an SMS message to the user with instructions on how to reset their password. This prevents malicious parties from being able to determine if a given phone number has an account on the homeserver in question.
  • M_SERVER_NOT_TRUSTED : The id_server parameter refers to an identity server that is not trusted by this homeserver.
403 The homeserver does not permit the address to be bound.

200 response

RequestTokenResponse
Name Type Description
sid string

Required: The session ID. Session IDs are opaque strings that must consist entirely of the characters [0-9a-zA-Z.=_-]. Their length must not exceed 255 characters and they must not be empty.

submit_url URI

An optional field containing a URL where the client must submit the validation token to, with identical parameters to the Identity Service API’s POST /validate/email/submitToken endpoint (without the requirement for an access token). The homeserver must send this token to the user (if applicable), who should then be prompted to provide it to the client.

If this field is not present, the client can assume that verification will happen without the client’s involvement provided the homeserver advertises this specification version in the /versions response (ie: r0.5.0).

{
  "sid": "123abc",
  "submit_url": "https://example.org/path/to/submitToken"
}

400 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_THREEPID_IN_USE",
  "error": "The specified address is already in use"
}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_THREEPID_DENIED",
  "error": "Third-party identifier is not allowed"
}

Notes on password management

Clients SHOULD enforce that the password provided is suitably complex. The password SHOULD include a lower-case letter, an upper-case letter, a number and a symbol and be at a minimum 8 characters in length. Servers MAY reject weak passwords with an error code M_WEAK_PASSWORD.

Account locking

[Added in v1.12]

Server administrators may apply locks to prevent users from usefully using their accounts, for instance, due to safety or security concerns. In contrast to account deactivation, locking is a non-destructive action that can be reversed. This specification describes the behaviour of clients and servers when an account is locked. It deliberately leaves the methods for locking and unlocking accounts as a server implementation detail.

When an account is locked, servers MUST return a 401 Unauthorized error response with an M_USER_LOCKED error code and soft_logout set to true on all but the following Client-Server APIs:

Servers MAY additionally include details of why the lock was applied in the error field.

HTTP/1.1 401 Unauthorized
Content-Type: application/json
{
  "errcode": "M_USER_LOCKED",
  "error": "This account has been locked",
  "soft_logout": true
}

Servers SHOULD NOT invalidate access tokens on locked accounts unless the client requests a logout (using the above endpoints). This ensures that users can retain their sessions without having to log back in if the account becomes unlocked.

Upon receiving an M_USER_LOCKED error, clients SHOULD retain session information including encryption state and inform the user that their account has been locked. While the lock is applied, clients SHOULD hide the normal UI from the user, preventing general use of their account. Clients SHOULD, however, continue to make rate-limited requests to /sync and other APIs to detect when the lock has been lifted.

To enable users to appeal to a lock clients MAY use server contact discovery.

Account suspension

[Added in v1.13]

Server administrators MAY suspend a user’s account to prevent further activity from that account. The effect is similar to locking, though without risk of the client losing state from a logout. Suspensions are reversible, like locks and unlike deactivations.

The actions a user can perform while suspended is deliberately left as an implementation detail. Servers SHOULD permit the user to perform at least the following, however:

General purpose endpoints like /send/{eventType} MAY return the error described below depending on the path parameters. For example, a user may be allowed to send m.room.redaction events but not m.room.message events through /send.

Where a room is used to maintain communication between server administration teams and the suspended user, servers are recommended to allow the user to send events to that room specifically. Server administrators which do not want the user to continue receiving messages may be interested in account locking instead.

Otherwise, the recommended set of explicitly forbidden actions is:

When a client attempts to perform an action while suspended, the server MUST respond with a 403 Forbidden error response with M_USER_SUSPENDED as the error code, as shown below:

HTTP/1.1 403 Forbidden
Content-Type: application/json
{
  "errcode": "M_USER_SUSPENDED",
  "error": "You cannot perform this action while suspended."
}

APIs for initiating suspension or unsuspension are not included in this version of the specification, and left as an implementation detail.

Adding Account Administrative Contact Information

A homeserver may keep some contact information for administrative use. This is independent of any information kept by any identity servers, though can be proxied (bound) to the identity server in many cases.

This section deals with two terms: “add” and “bind”. Where “add” (or “remove”) is used, it is speaking about an identifier that was not bound to an identity server. As a result, “bind” (or “unbind”) references an identifier that is found in an identity server. Note that an identifier can be added and bound at the same time, depending on context.

GET /_matrix/client/v3/account/3pid


Gets a list of the third-party identifiers that the homeserver has associated with the user’s account.

This is not the same as the list of third-party identifiers bound to the user’s Matrix ID in identity servers.

Identifiers in this list may be used by the homeserver as, for example, identifiers that it will accept to reset the user’s account password.

Rate-limited: No
Requires authentication: Yes

Request

No request parameters or request body.


Responses

Status Description
200 The lookup was successful.

200 response

Name Type Description
threepids [Third-party identifier]
Third-party identifier
Name Type Description
added_at integer

Required: The timestamp, in milliseconds, when the homeserver associated the third-party identifier with the user.

address string

Required: The third-party identifier address.

medium string

Required: The medium of the third-party identifier.

One of: [email, msisdn].

validated_at integer

Required: The timestamp, in milliseconds, when the identifier was validated by the identity server.

{
  "threepids": [
    {
      "added_at": 1535336848756,
      "address": "[email protected]",
      "medium": "email",
      "validated_at": 1535176800000
    }
  ]
}

POST /_matrix/client/v3/account/3pid


This API is deprecated and will be removed from a future release.

Adds contact information to the user’s account.

This endpoint is deprecated in favour of the more specific /3pid/add and /3pid/bind endpoints.

Note: Previously this endpoint supported a bind parameter. This parameter has been removed, making this endpoint behave as though it was false. This results in this endpoint being an equivalent to /3pid/bind rather than dual-purpose.

Rate-limited: No
Requires authentication: Yes

Request

Request body

Name Type Description
three_pid_creds ThreePidCredentials

Required: The third-party credentials to associate with the account.

ThreePidCredentials
Name Type Description
client_secret string

Required: The client secret used in the session with the identity server.

id_access_token string

Required: An access token previously registered with the identity server. Servers can treat this as optional to distinguish between r0.5-compatible clients and this specification version.

id_server string

Required: The identity server to use.

sid string

Required: The session identifier given by the identity server.

Request body example

{
  "three_pid_creds": {
    "client_secret": "d0nt-T3ll",
    "id_access_token": "abc123_OpaqueString",
    "id_server": "matrix.org",
    "sid": "abc123987"
  }
}

Responses

Status Description
200 The addition was successful.
403 The credentials could not be verified with the identity server.

200 response

Name Type Description
submit_url URI

An optional field containing a URL where the client must submit the validation token to, with identical parameters to the Identity Service API’s POST /validate/email/submitToken endpoint (without the requirement for an access token). The homeserver must send this token to the user (if applicable), who should then be prompted to provide it to the client.

If this field is not present, the client can assume that verification will happen without the client’s involvement provided the homeserver advertises this specification version in the /versions response (ie: r0.5.0).

{
  "submit_url": "https://example.org/path/to/submitToken"
}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_THREEPID_AUTH_FAILED",
  "error": "The third-party credentials could not be verified by the identity server."
}

POST /_matrix/client/v3/account/3pid/add


This API endpoint uses the User-Interactive Authentication API.

Adds contact information to the user’s account. Homeservers should use 3PIDs added through this endpoint for password resets instead of relying on the identity server.

Homeservers should prevent the caller from adding a 3PID to their account if it has already been added to another user’s account on the homeserver.

Rate-limited: Yes
Requires authentication: Yes

Request

Request body

Name Type Description
auth Authentication Data

Additional authentication information for the user-interactive authentication API.

client_secret string

Required: The client secret used in the session with the homeserver.

sid string

Required: The session identifier given by the homeserver.

Authentication Data
Name Type Description
session string

The value of the session key given by the homeserver.

type string

The authentication type that the client is attempting to complete. May be omitted if session is given, and the client is reissuing a request which it believes has been completed out-of-band (for example, via the fallback mechanism).

<Other properties>

Keys dependent on the login type

Request body example

{
  "auth": {
    "example_credential": "verypoorsharedsecret",
    "session": "xxxxx",
    "type": "example.type.foo"
  },
  "client_secret": "d0nt-T3ll",
  "sid": "abc123987"
}

Responses

Status Description
200 The addition was successful.
401 The homeserver requires additional authentication information.
429 This request was rate-limited.

200 response

{}

401 response

Authentication response
Name Type Description
completed [string]

A list of the stages the client has completed successfully

flows [Flow information]

Required: A list of the login flows supported by the server for this API.

params {string: object}

Contains any information that the client will need to know in order to use a given type of authentication. For each login type presented, that type may be present as a key in this dictionary. For example, the public part of an OAuth client ID could be given here.

session string

This is a session identifier that the client must pass back to the home server, if one is provided, in subsequent attempts to authenticate in the same API call.

Flow information
Name Type Description
stages [string]

Required: The login type of each of the stages required to complete this authentication flow

{
  "completed": [
    "example.type.foo"
  ],
  "flows": [
    {
      "stages": [
        "example.type.foo"
      ]
    }
  ],
  "params": {
    "example.type.baz": {
      "example_key": "foobar"
    }
  },
  "session": "xxxxxxyz"
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

POST /_matrix/client/v3/account/3pid/bind


Binds a 3PID to the user’s account through the specified identity server.

Homeservers should not prevent this request from succeeding if another user has bound the 3PID. Homeservers should simply proxy any errors received by the identity server to the caller.

Homeservers should track successful binds so they can be unbound later.

Rate-limited: Yes
Requires authentication: Yes

Request

Request body

Name Type Description
client_secret string

Required: The client secret used in the session with the identity server.

id_access_token string

Required: An access token previously registered with the identity server.

id_server string

Required: The identity server to use.

sid string

Required: The session identifier given by the identity server.

Request body example

{
  "client_secret": "d0nt-T3ll",
  "id_access_token": "abc123_OpaqueString",
  "id_server": "example.org",
  "sid": "abc123987"
}

Responses

Status Description
200 The addition was successful.
429 This request was rate-limited.

200 response

{}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

POST /_matrix/client/v3/account/3pid/delete


Removes a third-party identifier from the user’s account. This might not cause an unbind of the identifier from the identity server.

Unlike other endpoints, this endpoint does not take an id_access_token parameter because the homeserver is expected to sign the request to the identity server instead.

Rate-limited: No
Requires authentication: Yes

Request

Request body

Name Type Description
address string

Required: The third-party address being removed.

id_server string

The identity server to unbind from. If not provided, the homeserver MUST use the id_server the identifier was added through. If the homeserver does not know the original id_server, it MUST return a id_server_unbind_result of no-support.

medium string

Required: The medium of the third-party identifier being removed.

One of: [email, msisdn].

Request body example

{
  "address": "[email protected]",
  "id_server": "example.org",
  "medium": "email"
}

Responses

Status Description
200 The homeserver has disassociated the third-party identifier from the user.

200 response

Name Type Description
id_server_unbind_result string

Required: An indicator as to whether or not the homeserver was able to unbind the 3PID from the identity server. success indicates that the identity server has unbound the identifier whereas no-support indicates that the identity server refuses to support the request or the homeserver was not able to determine an identity server to unbind from.

One of: [no-support, success].

{
  "id_server_unbind_result": "success"
}

POST /_matrix/client/v3/account/3pid/email/requestToken


The homeserver must check that the given email address is not already associated with an account on this homeserver. This API should be used to request validation tokens when adding an email address to an account. This API’s parameters and response are identical to that of the /register/email/requestToken endpoint. The homeserver should validate the email itself, either by sending a validation email itself or by using a service it has control over.

Rate-limited: No
Requires authentication: No

Request

Request body

Name Type Description
client_secret string

Required: A unique string generated by the client, and used to identify the validation attempt. It must be a string consisting of the characters [0-9a-zA-Z.=_-]. Its length must not exceed 255 characters and it must not be empty.

email string

Required: The email address to validate.

id_access_token string

An access token previously registered with the identity server. Servers can treat this as optional to distinguish between r0.5-compatible clients and this specification version.

Required if an id_server is supplied.

id_server string

The hostname of the identity server to communicate with. May optionally include a port. This parameter is ignored when the homeserver handles 3PID verification.

This parameter is deprecated with a plan to be removed in a future specification version for /account/password and /register requests.

next_link string

Optional. When the validation is completed, the identity server will redirect the user to this URL. This option is ignored when submitting 3PID validation information through a POST request.

send_attempt integer

Required: The server will only send an email if the send_attempt is a number greater than the most recent one which it has seen, scoped to that email + client_secret pair. This is to avoid repeatedly sending the same email in the case of request retries between the POSTing user and the identity server. The client should increment this value if they desire a new email (e.g. a reminder) to be sent. If they do not, the server should respond with success but not resend the email.

Request body example

{
  "client_secret": "monkeys_are_GREAT",
  "email": "[email protected]",
  "id_server": "id.example.com",
  "next_link": "https://example.org/congratulations.html",
  "send_attempt": 1
}

Responses

Status Description
200 An email was sent to the given address. Note that this may be an email containing the validation token or it may be informing the user of an error.
400

The third-party identifier is already in use on the homeserver, or the request was invalid. Error codes that can be returned are:

  • M_THREEPID_IN_USE: The email supplied cannot be bound because is is already associated with a different Matrix ID.
  • M_SERVER_NOT_TRUSTED: The server does not trust/support the identity server provided in the request.
  • M_THREEPID_MEDIUM_NOT_SUPPORTED: The homeserver does not support adding email addresses.
  • M_INVALID_PARAM: The email address given was not valid.
403 The homeserver does not allow the third-party identifier as a contact option.

200 response

RequestTokenResponse
Name Type Description
sid string

Required: The session ID. Session IDs are opaque strings that must consist entirely of the characters [0-9a-zA-Z.=_-]. Their length must not exceed 255 characters and they must not be empty.

submit_url URI

An optional field containing a URL where the client must submit the validation token to, with identical parameters to the Identity Service API’s POST /validate/email/submitToken endpoint (without the requirement for an access token). The homeserver must send this token to the user (if applicable), who should then be prompted to provide it to the client.

If this field is not present, the client can assume that verification will happen without the client’s involvement provided the homeserver advertises this specification version in the /versions response (ie: r0.5.0).

{
  "sid": "123abc",
  "submit_url": "https://example.org/path/to/submitToken"
}

400 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_THREEPID_IN_USE",
  "error": "Third-party identifier already in use"
}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_THREEPID_DENIED",
  "error": "Third-party identifier is not allowed"
}

POST /_matrix/client/v3/account/3pid/msisdn/requestToken


The homeserver must check that the given phone number is not already associated with an account on this homeserver. This API should be used to request validation tokens when adding a phone number to an account. This API’s parameters and response are identical to that of the /register/msisdn/requestToken endpoint. The homeserver should validate the phone number itself, either by sending a validation message itself or by using a service it has control over.

Rate-limited: No
Requires authentication: No

Request

Request body

Name Type Description
client_secret string

Required: A unique string generated by the client, and used to identify the validation attempt. It must be a string consisting of the characters [0-9a-zA-Z.=_-]. Its length must not exceed 255 characters and it must not be empty.

country string

Required: The two-letter uppercase ISO-3166-1 alpha-2 country code that the number in phone_number should be parsed as if it were dialled from.

id_access_token string

An access token previously registered with the identity server. Servers can treat this as optional to distinguish between r0.5-compatible clients and this specification version.

Required if an id_server is supplied.

id_server string

The hostname of the identity server to communicate with. May optionally include a port. This parameter is ignored when the homeserver handles 3PID verification.

This parameter is deprecated with a plan to be removed in a future specification version for /account/password and /register requests.

next_link string

Optional. When the validation is completed, the identity server will redirect the user to this URL. This option is ignored when submitting 3PID validation information through a POST request.

phone_number string

Required: The phone number to validate.

send_attempt integer

Required: The server will only send an SMS if the send_attempt is a number greater than the most recent one which it has seen, scoped to that country + phone_number + client_secret triple. This is to avoid repeatedly sending the same SMS in the case of request retries between the POSTing user and the identity server. The client should increment this value if they desire a new SMS (e.g. a reminder) to be sent.

Request body example

{
  "client_secret": "monkeys_are_GREAT",
  "country": "GB",
  "id_server": "id.example.com",
  "next_link": "https://example.org/congratulations.html",
  "phone_number": "07700900001",
  "send_attempt": 1
}

Responses

Status Description
200 An SMS message was sent to the given phone number.
400

The third-party identifier is already in use on the homeserver, or the request was invalid. Error codes that can be returned are:

  • M_THREEPID_IN_USE: The phone number supplied cannot be bound because is is already associated with a different Matrix ID.
  • M_SERVER_NOT_TRUSTED: The server does not trust/support the identity server
  • M_THREEPID_MEDIUM_NOT_SUPPORTED: The homeserver does not support adding phone numbers.
  • M_INVALID_PARAM: The phone number given was not valid.
403 The homeserver does not allow the third-party identifier as a contact option.

200 response

RequestTokenResponse
Name Type Description
sid string

Required: The session ID. Session IDs are opaque strings that must consist entirely of the characters [0-9a-zA-Z.=_-]. Their length must not exceed 255 characters and they must not be empty.

submit_url URI

An optional field containing a URL where the client must submit the validation token to, with identical parameters to the Identity Service API’s POST /validate/email/submitToken endpoint (without the requirement for an access token). The homeserver must send this token to the user (if applicable), who should then be prompted to provide it to the client.

If this field is not present, the client can assume that verification will happen without the client’s involvement provided the homeserver advertises this specification version in the /versions response (ie: r0.5.0).

{
  "sid": "123abc",
  "submit_url": "https://example.org/path/to/submitToken"
}

400 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_THREEPID_IN_USE",
  "error": "Third-party identifier already in use"
}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_THREEPID_DENIED",
  "error": "Third-party identifier is not allowed"
}

POST /_matrix/client/v3/account/3pid/unbind


Removes a user’s third-party identifier from the provided identity server without removing it from the homeserver.

Unlike other endpoints, this endpoint does not take an id_access_token parameter because the homeserver is expected to sign the request to the identity server instead.

Rate-limited: No
Requires authentication: Yes

Request

Request body

Name Type Description
address string

Required: The third-party address being removed.

id_server string

The identity server to unbind from. If not provided, the homeserver MUST use the id_server the identifier was added through. If the homeserver does not know the original id_server, it MUST return a id_server_unbind_result of no-support.

medium string

Required: The medium of the third-party identifier being removed.

One of: [email, msisdn].

Request body example

{
  "address": "[email protected]",
  "id_server": "example.org",
  "medium": "email"
}

Responses

Status Description
200 The identity server has disassociated the third-party identifier from the user.

200 response

Name Type Description
id_server_unbind_result string

Required: An indicator as to whether or not the identity server was able to unbind the 3PID. success indicates that the identity server has unbound the identifier whereas no-support indicates that the identity server refuses to support the request or the homeserver was not able to determine an identity server to unbind from.

One of: [no-support, success].

{
  "id_server_unbind_result": "success"
}

Current account information

GET /_matrix/client/v3/account/whoami


Gets information about the owner of a given access token.

Note that, as with the rest of the Client-Server API, Application Services may masquerade as users within their namespace by giving a user_id query parameter. In this situation, the server should verify that the given user_id is registered by the appservice, and return it in the response body.

Rate-limited: Yes
Requires authentication: Yes

Request

No request parameters or request body.


Responses

Status Description
200 The token belongs to a known user.
401 The token is not recognised
403 The appservice cannot masquerade as the user or has not registered them.
429 This request was rate-limited.

200 response

Name Type Description
device_id string

Device ID associated with the access token. If no device is associated with the access token (such as in the case of application services) then this field can be omitted. Otherwise this is required.

Added in v1.1

is_guest boolean

When true, the user is a Guest User. When not present or false, the user is presumed to be a non-guest user.

Added in v1.2

user_id string

Required: The user ID that owns the access token.

{
  "device_id": "ABC1234",
  "user_id": "@joe:example.org"
}

401 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_UNKNOWN_TOKEN",
  "error": "Unrecognised access token."
}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_FORBIDDEN",
  "error": "Application service has not registered this user."
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

Notes on identity servers

Identity servers in Matrix store bindings (relationships) between a user’s third-party identifier, typically email or phone number, and their user ID. Once a user has chosen an identity server, that identity server should be used by all clients.

Clients can see which identity server the user has chosen through the m.identity_server account data event, as described below. Clients SHOULD refrain from making requests to any identity server until the presence of m.identity_server is confirmed as (not) present. If present, the client SHOULD check for the presence of the base_url property in the event’s content. If the base_url is present, the client SHOULD use the identity server in that property as the identity server for the user. If the base_url is missing, or the account data event is not present, the client SHOULD use whichever default value it normally would for an identity server, if applicable. Clients SHOULD NOT update the account data with the default identity server when the user is missing an identity server in their account data.

Clients SHOULD listen for changes to the m.identity_server account data event and update the identity server they are contacting as a result.

If the client offers a way to set the identity server to use, it MUST update the value of m.identity_server accordingly. A base_url of null MUST be treated as though the user does not want to use an identity server, disabling all related functionality as a result.

Clients SHOULD refrain from populating the account data as a migration step for users who are lacking the account data, unless the user sets the identity server within the client to a value. For example, a user which has no m.identity_server account data event should not end up with the client’s default identity server in their account data, unless the user first visits their account settings to set the identity server.

m.identity_server


Persists the user’s preferred identity server, or preference to not use an identity server at all, in the user’s account data.

Event type: Message event

Content

Name Type Description
base_url string

The URL of the identity server the user prefers to use, or null if the user does not want to use an identity server. This value is similar in structure to the base_url for identity servers in the .well-known/matrix/client schema.

Examples

{
  "content": {
    "base_url": "https://example.org"
  },
  "type": "m.identity_server"
}

Capabilities negotiation

A homeserver may not support certain operations and clients must be able to query for what the homeserver can and can’t offer. For example, a homeserver may not support users changing their password as it is configured to perform authentication against an external system.

The capabilities advertised through this system are intended to advertise functionality which is optional in the API, or which depend in some way on the state of the user or server. This system should not be used to advertise unstable or experimental features - this is better done by the /versions endpoint.

Some examples of what a reasonable capability could be are:

  • Whether the server supports user presence.
  • Whether the server supports optional features, such as the user or room directories.
  • The rate limits or file type restrictions imposed on clients by the server.

Some examples of what should not be a capability are:

  • Whether the server supports a feature in the unstable specification.
  • Media size limits - these are handled by the /config API.
  • Optional encodings or alternative transports for communicating with the server.

Capabilities prefixed with m. are reserved for definition in the Matrix specification while other values may be used by servers using the Java package naming convention. The capabilities supported by the Matrix specification are defined later in this section.

GET /_matrix/client/v3/capabilities


Gets information about the server’s supported feature set and other relevant capabilities.

Rate-limited: Yes
Requires authentication: Yes

Request

No request parameters or request body.


Responses

Status Description
200 The capabilities of the server.
429 This request was rate-limited.

200 response

Name Type Description
capabilities Capabilities

Required: The custom capabilities the server supports, using the Java package naming convention.

Capabilities
Name Type Description
m.3pid_changes BooleanCapability

Capability to indicate if the user can change 3PID associations on their account.

m.change_password BooleanCapability

Capability to indicate if the user can change their password.

m.get_login_token BooleanCapability

Capability to indicate if the user can generate tokens to log further clients into their account.

m.room_versions RoomVersionsCapability

The room versions the server supports.

m.set_avatar_url BooleanCapability

Capability to indicate if the user can change their avatar.

m.set_displayname BooleanCapability

Capability to indicate if the user can change their display name.

<Other properties>

Application-dependent keys using the Common Namespaced Identifier Grammar.

BooleanCapability
Name Type Description
enabled boolean

Required: True if the user can perform the action, false otherwise.

RoomVersionsCapability
Name Type Description
available {string: string}

Required: A detailed description of the room versions the server supports.

default string

Required: The default room version the server is using for new rooms.

{
  "capabilities": {
    "com.example.custom.ratelimit": {
      "max_requests_per_hour": 600
    },
    "m.change_password": {
      "enabled": false
    },
    "m.room_versions": {
      "available": {
        "1": "stable",
        "2": "stable",
        "3": "unstable",
        "test-version": "unstable"
      },
      "default": "1"
    }
  }
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

m.change_password capability

This capability has a single flag, enabled, which indicates whether or not the user can use the /account/password API to change their password. If not present, the client should assume that password changes are possible via the API. When present, clients SHOULD respect the capability’s enabled flag and indicate to the user if they are unable to change their password.

An example of the capability API’s response for this capability is:

{
  "capabilities": {
    "m.change_password": {
      "enabled": false
    }
  }
}

m.room_versions capability

This capability describes the default and available room versions a server supports, and at what level of stability. Clients should make use of this capability to determine if users need to be encouraged to upgrade their rooms.

An example of the capability API’s response for this capability is:

{
  "capabilities": {
    "m.room_versions": {
      "default": "1",
      "available": {
        "1": "stable",
        "2": "stable",
        "3": "unstable",
        "custom-version": "unstable"
      }
    }
  }
}

This capability mirrors the same restrictions of room versions to describe which versions are stable and unstable. Clients should assume that the default version is stable. Any version not explicitly labelled as stable in the available versions is to be treated as unstable. For example, a version listed as future-stable should be treated as unstable.

The default version is the version the server is using to create new rooms. Clients should encourage users with sufficient permissions in a room to upgrade their room to the default version when the room is using an unstable version.

When this capability is not listed, clients should use "1" as the default and only stable available room version.

m.set_displayname capability

This capability has a single flag, enabled, to denote whether the user is able to change their own display name via profile endpoints. Cases for disabling might include users mapped from external identity/directory services, such as LDAP.

Note that this is well paired with the m.set_avatar_url capability.

When not listed, clients should assume the user is able to change their display name.

An example of the capability API’s response for this capability is:

{
  "capabilities": {
    "m.set_displayname": {
      "enabled": false
    }
  }
}

m.set_avatar_url capability

This capability has a single flag, enabled, to denote whether the user is able to change their own avatar via profile endpoints. Cases for disabling might include users mapped from external identity/directory services, such as LDAP.

Note that this is well paired with the m.set_displayname capability.

When not listed, clients should assume the user is able to change their avatar.

An example of the capability API’s response for this capability is:

{
  "capabilities": {
    "m.set_avatar_url": {
      "enabled": false
    }
  }
}

m.3pid_changes capability

This capability has a single flag, enabled, to denote whether the user is able to add, remove, or change 3PID associations on their account. Note that this only affects a user’s ability to use the Admin Contact Information API, not endpoints exposed by an Identity Service. Cases for disabling might include users mapped from external identity/directory services, such as LDAP.

When not listed, clients should assume the user is able to modify their 3PID associations.

An example of the capability API’s response for this capability is:

{
  "capabilities": {
    "m.3pid_changes": {
      "enabled": false
    }
  }
}

m.get_login_token capability

This capability has a single flag, enabled, to denote whether the user is able to use POST /login/get_token to generate single-use, time-limited tokens to log unauthenticated clients into their account.

When not listed, clients SHOULD assume the user is unable to generate tokens.

An example of the capability API’s response for this capability is:

{
  "capabilities": {
    "m.get_login_token": {
      "enabled": false
    }
  }
}

Filtering

Filters can be created on the server and can be passed as a parameter to APIs which return events. These filters alter the data returned from those APIs. Not all APIs accept filters.

Lazy-loading room members

Membership events often take significant resources for clients to track. In an effort to reduce the number of resources used, clients can enable “lazy-loading” for room members. By doing this, servers will attempt to only send membership events which are relevant to the client.

It is important to understand that lazy-loading is not intended to be a perfect optimisation, and that it may not be practical for the server to calculate precisely which membership events are relevant to the client. As a result, it is valid for the server to send redundant membership events to the client to ease implementation, although such redundancy should be minimised where possible to conserve bandwidth.

In terms of filters, lazy-loading is enabled by enabling lazy_load_members on a RoomEventFilter. When enabled, lazy-loading aware endpoints (see below) will only include membership events for the sender of events being included in the response. For example, if a client makes a /sync request with lazy-loading enabled, the server will only return membership events for the sender of events in the timeline, not all members of a room.

When processing a sequence of events (e.g. by looping on /sync or paginating /messages), it is common for blocks of events in the sequence to share a similar set of senders. Rather than responses in the sequence sending duplicate membership events for these senders to the client, the server MAY assume that clients will remember membership events they have already been sent, and choose to skip sending membership events for members whose membership has not changed. These are called ‘redundant membership events’. Clients may request that redundant membership events are always included in responses by setting include_redundant_members to true in the filter.

The expected pattern for using lazy-loading is currently:

  • Client performs an initial /sync with lazy-loading enabled, and receives only the membership events which relate to the senders of the events it receives.
  • Clients which support display-name tab-completion or other operations which require rapid access to all members in a room should call /members for the currently selected room, with an ?at parameter set to the /sync response’s from token. The member list for the room is then maintained by the state in subsequent incremental /sync responses.
  • Clients which do not support tab-completion may instead pull in profiles for arbitrary users (e.g. read receipts, typing notifications) on demand by querying the room state or /profile.

The current endpoints which support lazy-loading room members are:

API endpoints

POST /_matrix/client/v3/user/{userId}/filter


Uploads a new filter definition to the homeserver. Returns a filter ID that may be used in future requests to restrict which events are returned to the client.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
userId string

Required: The id of the user uploading the filter. The access token must be authorized to make requests for this user id.

Request body

Filter
Name Type Description
account_data EventFilter

The user account data that isn’t associated with rooms to include.

event_fields [string]

List of event fields to include. If this list is absent then all fields are included. The entries are dot-separated paths for each property to include. So [‘content.body’] will include the ‘body’ field of the ‘content’ object. A server may include more fields than were requested.

event_format string

The format to use for events. ‘client’ will return the events in a format suitable for clients. ‘federation’ will return the raw event as received over federation. The default is ‘client’.

One of: [client, federation].

presence EventFilter

The presence updates to include.

room RoomFilter

Filters to be applied to room data.

EventFilter
Name Type Description
limit integer

The maximum number of events to return, must be an integer greater than 0.

Servers should apply a default value, and impose a maximum value to avoid resource exhaustion.

not_senders [string]

A list of sender IDs to exclude. If this list is absent then no senders are excluded. A matching sender will be excluded even if it is listed in the 'senders' filter.

not_types [string]

A list of event types to exclude. If this list is absent then no event types are excluded. A matching type will be excluded even if it is listed in the 'types' filter. A ‘*’ can be used as a wildcard to match any sequence of characters.

senders [string]

A list of senders IDs to include. If this list is absent then all senders are included.

types [string]

A list of event types to include. If this list is absent then all event types are included. A '*' can be used as a wildcard to match any sequence of characters.

RoomFilter
Name Type Description
account_data RoomEventFilter

The per user account data to include for rooms.

ephemeral RoomEventFilter

The ephemeral events to include for rooms. These are the events that appear in the ephemeral property in the /sync response.

include_leave boolean

Include rooms that the user has left in the sync, default false

not_rooms [string]

A list of room IDs to exclude. If this list is absent then no rooms are excluded. A matching room will be excluded even if it is listed in the 'rooms' filter. This filter is applied before the filters in ephemeral, state, timeline or account_data

rooms [string]

A list of room IDs to include. If this list is absent then all rooms are included. This filter is applied before the filters in ephemeral, state, timeline or account_data

state RoomEventFilter

The state events to include for rooms.

timeline RoomEventFilter

The message and state update events to include for rooms.

RoomEventFilter
Name Type Description
contains_url boolean

If true, includes only events with a url key in their content. If false, excludes those events. If omitted, url key is not considered for filtering.

include_redundant_members boolean

If true, sends all membership events for all events, even if they have already been sent to the client. Does not apply unless lazy_load_members is true. See Lazy-loading room members for more information. Defaults to false.

lazy_load_members boolean

If true, enables lazy-loading of membership events. See Lazy-loading room members for more information. Defaults to false.

limit integer

The maximum number of events to return, must be an integer greater than 0.

Servers should apply a default value, and impose a maximum value to avoid resource exhaustion.

not_rooms [string]

A list of room IDs to exclude. If this list is absent then no rooms are excluded. A matching room will be excluded even if it is listed in the 'rooms' filter.

not_senders [string]

A list of sender IDs to exclude. If this list is absent then no senders are excluded. A matching sender will be excluded even if it is listed in the 'senders' filter.

not_types [string]

A list of event types to exclude. If this list is absent then no event types are excluded. A matching type will be excluded even if it is listed in the 'types' filter. A ‘*’ can be used as a wildcard to match any sequence of characters.

rooms [string]

A list of room IDs to include. If this list is absent then all rooms are included.

senders [string]

A list of senders IDs to include. If this list is absent then all senders are included.

types [string]

A list of event types to include. If this list is absent then all event types are included. A '*' can be used as a wildcard to match any sequence of characters.

unread_thread_notifications boolean

If true, enables per-thread notification counts. Only applies to the /sync endpoint. Defaults to false.

Added in v1.4

Request body example

{
  "event_fields": [
    "type",
    "content",
    "sender"
  ],
  "event_format": "client",
  "presence": {
    "not_senders": [
      "@alice:example.com"
    ],
    "types": [
      "m.presence"
    ]
  },
  "room": {
    "ephemeral": {
      "not_rooms": [
        "!726s6s6q:example.com"
      ],
      "not_senders": [
        "@spam:example.com"
      ],
      "types": [
        "m.receipt",
        "m.typing"
      ]
    },
    "state": {
      "not_rooms": [
        "!726s6s6q:example.com"
      ],
      "types": [
        "m.room.*"
      ]
    },
    "timeline": {
      "limit": 10,
      "not_rooms": [
        "!726s6s6q:example.com"
      ],
      "not_senders": [
        "@spam:example.com"
      ],
      "types": [
        "m.room.message"
      ]
    }
  }
}

Responses

Status Description
200 The filter was created.

200 response

Name Type Description
filter_id string

Required: The ID of the filter that was created. Cannot start with a { as this character is used to determine if the filter provided is inline JSON or a previously declared filter by homeservers on some APIs.

{
  "filter_id": "66696p746572"
}

GET /_matrix/client/v3/user/{userId}/filter/{filterId}


Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
filterId string

Required: The filter ID to download.

userId string

Required: The user ID to download a filter for.


Responses

Status Description
200 The filter definition.
404 Unknown filter.

200 response

Filter
Name Type Description
account_data EventFilter

The user account data that isn’t associated with rooms to include.

event_fields [string]

List of event fields to include. If this list is absent then all fields are included. The entries are dot-separated paths for each property to include. So [‘content.body’] will include the ‘body’ field of the ‘content’ object. A server may include more fields than were requested.

event_format string

The format to use for events. ‘client’ will return the events in a format suitable for clients. ‘federation’ will return the raw event as received over federation. The default is ‘client’.

One of: [client, federation].

presence EventFilter

The presence updates to include.

room RoomFilter

Filters to be applied to room data.

EventFilter
Name Type Description
limit integer

The maximum number of events to return, must be an integer greater than 0.

Servers should apply a default value, and impose a maximum value to avoid resource exhaustion.

not_senders [string]

A list of sender IDs to exclude. If this list is absent then no senders are excluded. A matching sender will be excluded even if it is listed in the 'senders' filter.

not_types [string]

A list of event types to exclude. If this list is absent then no event types are excluded. A matching type will be excluded even if it is listed in the 'types' filter. A ‘*’ can be used as a wildcard to match any sequence of characters.

senders [string]

A list of senders IDs to include. If this list is absent then all senders are included.

types [string]

A list of event types to include. If this list is absent then all event types are included. A '*' can be used as a wildcard to match any sequence of characters.

RoomFilter
Name Type Description
account_data RoomEventFilter

The per user account data to include for rooms.

ephemeral RoomEventFilter

The ephemeral events to include for rooms. These are the events that appear in the ephemeral property in the /sync response.

include_leave boolean

Include rooms that the user has left in the sync, default false

not_rooms [string]

A list of room IDs to exclude. If this list is absent then no rooms are excluded. A matching room will be excluded even if it is listed in the 'rooms' filter. This filter is applied before the filters in ephemeral, state, timeline or account_data

rooms [string]

A list of room IDs to include. If this list is absent then all rooms are included. This filter is applied before the filters in ephemeral, state, timeline or account_data

state RoomEventFilter

The state events to include for rooms.

timeline RoomEventFilter

The message and state update events to include for rooms.

RoomEventFilter
Name Type Description
contains_url boolean

If true, includes only events with a url key in their content. If false, excludes those events. If omitted, url key is not considered for filtering.

include_redundant_members boolean

If true, sends all membership events for all events, even if they have already been sent to the client. Does not apply unless lazy_load_members is true. See Lazy-loading room members for more information. Defaults to false.

lazy_load_members boolean

If true, enables lazy-loading of membership events. See Lazy-loading room members for more information. Defaults to false.

limit integer

The maximum number of events to return, must be an integer greater than 0.

Servers should apply a default value, and impose a maximum value to avoid resource exhaustion.

not_rooms [string]

A list of room IDs to exclude. If this list is absent then no rooms are excluded. A matching room will be excluded even if it is listed in the 'rooms' filter.

not_senders [string]

A list of sender IDs to exclude. If this list is absent then no senders are excluded. A matching sender will be excluded even if it is listed in the 'senders' filter.

not_types [string]

A list of event types to exclude. If this list is absent then no event types are excluded. A matching type will be excluded even if it is listed in the 'types' filter. A ‘*’ can be used as a wildcard to match any sequence of characters.

rooms [string]

A list of room IDs to include. If this list is absent then all rooms are included.

senders [string]

A list of senders IDs to include. If this list is absent then all senders are included.

types [string]

A list of event types to include. If this list is absent then all event types are included. A '*' can be used as a wildcard to match any sequence of characters.

unread_thread_notifications boolean

If true, enables per-thread notification counts. Only applies to the /sync endpoint. Defaults to false.

Added in v1.4

{
  "event_fields": [
    "type",
    "content",
    "sender"
  ],
  "event_format": "client",
  "presence": {
    "not_senders": [
      "@alice:example.com"
    ],
    "types": [
      "m.presence"
    ]
  },
  "room": {
    "ephemeral": {
      "not_rooms": [
        "!726s6s6q:example.com"
      ],
      "not_senders": [
        "@spam:example.com"
      ],
      "types": [
        "m.receipt",
        "m.typing"
      ]
    },
    "state": {
      "not_rooms": [
        "!726s6s6q:example.com"
      ],
      "types": [
        "m.room.*"
      ]
    },
    "timeline": {
      "limit": 10,
      "not_rooms": [
        "!726s6s6q:example.com"
      ],
      "not_senders": [
        "@spam:example.com"
      ],
      "types": [
        "m.room.message"
      ]
    }
  }
}

Events

The model of conversation history exposed by the client-server API can be considered as a list of events. The server ’linearises’ the eventually-consistent event graph of events into an ’event stream’ at any given point in time:

[E0]->[E1]->[E2]->[E3]->[E4]->[E5]

Types of room events

Room events are split into two categories:

  • State events: These are events which update the metadata state of the room (e.g. room topic, room membership etc). State is keyed by a tuple of event type and a state_key. State in the room with the same key-tuple will be overwritten.

  • Message events: These are events which describe transient “once-off” activity in a room: typically communication such as sending an instant message or setting up a VoIP call.

This specification outlines several events, all with the event type prefix m.. (See Room Events for the m. event specification.) However, applications may wish to add their own type of event, and this can be achieved using the REST API detailed in the following sections. If new events are added, the event type key SHOULD follow the Java package naming convention, e.g. com.example.myapp.event. This ensures event types are suitably namespaced for each application and reduces the risk of clashes.

Events are not limited to the types defined in this specification. New or custom event types can be created on a whim using the Java package naming convention. For example, a com.example.game.score event can be sent by clients and other clients would receive it through Matrix, assuming the client has access to the com.example namespace.

Room event format

The “federation” format of a room event, which is used internally by homeservers and between homeservers via the Server-Server API, depends on the “room version” in use by the room. See, for example, the definitions in room version 1 and room version 3.

However, it is unusual that a Matrix client would encounter this event format. Instead, homeservers are responsible for converting events into the format shown below so that they can be easily parsed by clients.

Event bodies are considered untrusted data. This means that any application using Matrix must validate that the event body is of the expected shape/schema before using the contents verbatim.

It is not safe to assume that an event body will have all the expected fields of the expected types.

See MSC2801 for more detail on why this assumption is unsafe.

ClientEvent


The format used for events when they are returned from a homeserver to a client via the Client-Server API, or sent to an Application Service via the Application Services API.

ClientEvent
Name Type Description
content object

Required: The body of this event, as created by the client which sent it.

event_id string

Required: The globally unique identifier for this event.

origin_server_ts integer

Required: Timestamp (in milliseconds since the unix epoch) on originating homeserver when this event was sent.

room_id string

Required: The ID of the room associated with this event.

sender string

Required: Contains the fully-qualified ID of the user who sent this event.

state_key string

Present if, and only if, this event is a state event. The key making this piece of state unique in the room. Note that it is often an empty string.

State keys starting with an @ are reserved for referencing user IDs, such as room members. With the exception of a few events, state events set with a given user’s ID as the state key MUST only be set by that user.

type string

Required: The type of the event.

unsigned UnsignedData

Contains optional extra information about the event.

UnsignedData
Name Type Description
age integer

The time in milliseconds that has elapsed since the event was sent. This field is generated by the local homeserver, and may be incorrect if the local time on at least one of the two servers is out of sync, which can cause the age to either be negative or greater than it actually is.

membership string

The room membership of the user making the request, at the time of the event.

This property is the value of the membership property of the requesting user’s m.room.member state at the point of the event, including any changes caused by the event. If the user had yet to join the room at the time of the event (i.e, they have no m.room.member state), this property is set to leave.

Homeservers SHOULD populate this property wherever practical, but they MAY omit it if necessary (for example, if calculating the value is expensive, servers might choose to only implement it in encrypted rooms). The property is not normally populated in events pushed to application services via the application service transaction API (where there is no clear definition of “requesting user”).

Added in v1.11

prev_content EventContent

The previous content for this event. This field is generated by the local homeserver, and is only returned if the event is a state event, and the client has permission to see the previous content.

Changed in v1.2: Previously, this field was specified at the top level of returned events rather than in unsigned (with the exception of the GET .../notifications endpoint), though in practice no known server implementations honoured this.

redacted_because ClientEvent

The event that redacted this event, if any.

transaction_id string

The client-supplied transaction ID, for example, provided via PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}, if the client being given the event is the same one which sent it.

Examples

{
  "content": {
    "membership": "join"
  },
  "event_id": "$26RqwJMLw-yds1GAH_QxjHRC1Da9oasK0e5VLnck_45",
  "origin_server_ts": 1632489532305,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "state_key": "@user:example.org",
  "type": "m.room.member",
  "unsigned": {
    "age": 1567437,
    "membership": "join",
    "redacted_because": {
      "content": {
        "reason": "spam"
      },
      "event_id": "$Nhl3rsgHMjk-DjMJANawr9HHAhLg4GcoTYrSiYYGqEE",
      "origin_server_ts": 1632491098485,
      "redacts": "$26RqwJMLw-yds1GAH_QxjHRC1Da9oasK0e5VLnck_45",
      "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
      "sender": "@moderator:example.org",
      "type": "m.room.redaction",
      "unsigned": {
        "age": 1257,
        "membership": "leave"
      }
    }
  }
}

Stripped state

Stripped state is a simplified view of the state of a room intended to help a potential joiner identify the room. It consists of a limited set of state events that are themselves simplified to reduce the amount of data required.

Stripped state events can only have the sender, type, state_key and content properties present.

Stripped state typically appears in invites, knocks, and in other places where a user could join the room under the conditions available (such as a restricted room).

Clients should only use stripped state events when they don’t have access to the proper state of the room. Once the state of the room is available, all stripped state should be discarded. In cases where the client has an archived state of the room (such as after being kicked) and the client is receiving stripped state for the room, such as from an invite or knock, then the stripped state should take precedence until fresh state can be acquired from a join.

Stripped state should contain some or all of the following state events, which should be represented as stripped state events when possible:

Clients should inspect the list of stripped state events and not assume any particular event is present. The server might include events not described here as well.

The name, avatar, topic, and aliases are presented as aesthetic information about the room, allowing users to make decisions about whether or not they want to join the room.

The join rules are given to help the client determine why it is able to potentially join. For example, annotating the room decoration with iconography consistent with the respective join rule for the room.

The create event can help identify what kind of room is being joined, as it may be a Space or other kind of room. The client might choose to render the invite in a different area of the application as a result.

Similar to join rules, the encryption information is given to help clients decorate the room with appropriate iconography or messaging.

Although stripped state is usually generated and provided by the server, it is still possible to be incorrect on the receiving end. The stripped state events are not signed and could theoretically be modified, or outdated due to updates not being sent.

Stripped state event


A stripped down state event, with only the type, state_key, sender, and content keys.

Name Type Description
content EventContent

Required: The content for the event.

sender string

Required: The sender for the event.

state_key string

Required: The state_key for the event.

type string

Required: The type for the event.

Size limits

The complete event MUST NOT be larger than 65536 bytes, when formatted with the federation event format, including any signatures, and encoded as Canonical JSON.

There are additional restrictions on sizes per key:

  • sender MUST NOT exceed the size limit for user IDs.
  • room_id MUST NOT exceed the size limit for room IDs.
  • state_key MUST NOT exceed 255 bytes.
  • type MUST NOT exceed 255 bytes.
  • event_id MUST NOT exceed the size limit for event IDs.

Some event types have additional size restrictions which are specified in the description of the event. Additional keys have no limit other than that implied by the total 64 KiB limit on events.

Room Events

This section is a work in progress.

This specification outlines several standard event types, all of which are prefixed with m.

m.room.canonical_alias


This event is used to inform the room about which alias should be considered the canonical one, and which other aliases point to the room. This could be for display purposes or as suggestion to users which alias to use to advertise and access the room.

Event type: State event
State key A zero-length string.

Content

Name Type Description
alias string

The canonical alias for the room. If not present, null, or empty the room should be considered to have no canonical alias.

alt_aliases [string]

Alternative aliases the room advertises. This list can have aliases despite the alias field being null, empty, or otherwise not present.

Examples

{
  "content": {
    "alias": "#somewhere:localhost",
    "alt_aliases": [
      "#somewhere:example.org",
      "#myroom:example.com"
    ]
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "state_key": "",
  "type": "m.room.canonical_alias",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

m.room.create


This is the first event in a room and cannot be changed. It acts as the root of all other events.

Event type: State event
State key A zero-length string.

Content

Name Type Description
creator string

The user_id of the room creator. Required for, and only present in, room versions 1 - 10. Starting with room version 11 the event sender should be used instead.

m.federate boolean

Whether users on other servers can join this room. Defaults to true if key does not exist.

predecessor Previous Room

A reference to the room this room replaces, if the previous room was upgraded.

room_version string

The version of the room. Defaults to "1" if the key does not exist.

type string

Optional room type to denote a room’s intended function outside of traditional conversation.

Unspecified room types are possible using Namespaced Identifiers.

Previous Room
Name Type Description
event_id string

Required: The event ID of the last known event in the old room.

room_id string

Required: The ID of the old room.

Examples

{
  "content": {
    "m.federate": true,
    "predecessor": {
      "event_id": "$something:example.org",
      "room_id": "!oldroom:example.org"
    },
    "room_version": "11"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "state_key": "",
  "type": "m.room.create",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

m.room.join_rules


A room may have one of the following designations:

  • public - anyone can join the room without any prior action.
  • invite - a user must first receive an invite from someone already in the room in order to join.
  • knock - a user can request an invite to the room. They can be allowed (invited) or denied (kicked/banned) access. Otherwise, users need to be invited in. Only available in rooms which support knocking.
  • restricted - anyone able to satisfy at least one of the allow conditions is able to join the room without prior action. Otherwise, an invite is required. Only available in rooms which support the join rule.
  • knock_restricted - a user can request an invite using the same functions offered by the knock join rule, or can attempt to join having satisfied an allow condition per the restricted join rule. Only available in rooms which support the join rule.
  • private - reserved without implementation. No significant meaning.
Event type: State event
State key A zero-length string.

Content

Name Type Description
allow [AllowCondition]

For restricted rooms, the conditions the user will be tested against. The user needs only to satisfy one of the conditions to join the restricted room. If the user fails to meet any condition, or the condition is unable to be confirmed as satisfied, then the user requires an invite to join the room. Improper or no allow conditions on a restricted join rule imply the room is effectively invite-only (no conditions can be satisfied).

Added in v1.2

join_rule string

Required: The type of rules used for users wishing to join this room.

One of: [public, knock, invite, private, restricted, knock_restricted].

AllowCondition
Name Type Description
room_id string

Required if type is m.room_membership. The room ID to check the user’s membership against. If the user is joined to this room, they satisfy the condition and thus are permitted to join the restricted room.

type string

Required: The type of condition:

  • m.room_membership - the user satisfies the condition if they are joined to the referenced room.

One of: [m.room_membership].

Examples

{
  "content": {
    "join_rule": "public"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "state_key": "",
  "type": "m.room.join_rules",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}
{
  "content": {
    "allow": [
      {
        "room_id": "!other:example.org",
        "type": "m.room_membership"
      },
      {
        "room_id": "!elsewhere:example.org",
        "type": "m.room_membership"
      }
    ],
    "join_rule": "restricted"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "state_key": "",
  "type": "m.room.join_rules",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

m.room.member


Adjusts the membership state for a user in a room. It is preferable to use the membership APIs (/rooms/<room id>/invite etc) when performing membership actions rather than adjusting the state directly as there are a restricted set of valid transformations. For example, user A cannot force user B to join a room, and trying to force this state change directly will fail.

The following membership states are specified:

  • invite - The user has been invited to join a room, but has not yet joined it. They may not participate in the room until they join.
  • join - The user has joined the room (possibly after accepting an invite), and may participate in it.
  • leave - The user was once joined to the room, but has since left (possibly by choice, or possibly by being kicked).
  • ban - The user has been banned from the room, and is no longer allowed to join it until they are un-banned from the room (by having their membership state set to a value other than ban).
  • knock - The user has knocked on the room, requesting permission to participate. They may not participate in the room until they join.

The third_party_invite property will be set if this invite is an invite event and is the successor of an m.room.third_party_invite event, and absent otherwise.

This event may also include an invite_room_state key inside the event’s unsigned data. If present, this contains an array of stripped state events to assist the receiver in identifying the room.

The user for which a membership applies is represented by the state_key. Under some conditions, the sender and state_key may not match - this may be interpreted as the sender affecting the membership state of the state_key user.

The membership for a given user can change over time. The table below represents the various changes over time and how clients and servers must interpret those changes. Previous membership can be retrieved from the prev_content object on an event. If not present, the user’s previous membership must be assumed as leave.

to invite to join to leave to ban to knock
from invite No change. User joined the room. If the state_key is the same as the sender, the user rejected the invite. Otherwise, the state_key user had their invite revoked. User was banned. User is re-knocking.
from join Must never happen. displayname or avatar_url changed. If the state_key is the same as the sender, the user left. Otherwise, the state_key user was kicked. User was kicked and banned. Must never happen.
from leave New invitation sent. User joined. No change. User was banned. User is knocking.
from ban Must never happen. Must never happen. User was unbanned. No change. Must never happen.
from knock Knock accepted. Must never happen. If the state_key is the same as the sender, the user retracted the knock. Otherwise, the state_key user had their knock denied. User was banned. No change.
Event type: State event
State key The user_id this membership event relates to. In all cases except for when membership is join, the user ID sending the event does not need to match the user ID in the state_key, unlike other events. Regular authorisation rules still apply.

Content

EventContent
Name Type Description
avatar_url URI

The avatar URL for this user, if any.

displayname string|null

The display name for this user, if any.

is_direct boolean

Flag indicating if the room containing this event was created with the intention of being a direct chat. See Direct Messaging.

join_authorised_via_users_server string

Usually found on join events, this field is used to denote which homeserver (through representation of a user with sufficient power level) authorised the user’s join. More information about this field can be found in the Restricted Rooms Specification.

Client and server implementations should be aware of the signing implications of including this field in further events: in particular, the event must be signed by the server which owns the user ID in the field. When copying the membership event’s content (for profile updates and similar) it is therefore encouraged to exclude this field in the copy, as otherwise the event might fail event authorization.

Added in v1.2

membership string

Required: The membership state of the user.

One of: [invite, join, knock, leave, ban].

reason string

Optional user-supplied text for why their membership has changed. For kicks and bans, this is typically the reason for the kick or ban. For other membership changes, this is a way for the user to communicate their intent without having to send a message to the room, such as in a case where Bob rejects an invite from Alice about an upcoming concert, but can’t make it that day.

Clients are not recommended to show this reason to users when receiving an invite due to the potential for spam and abuse. Hiding the reason behind a button or other component is recommended.

Added in v1.1

third_party_invite Invite
Invite
Name Type Description
display_name string

Required: A name which can be displayed to represent the user instead of their third-party identifier

signed signed

Required: A block of content which has been signed, which servers can use to verify the event. Clients should ignore this.

signed
Name Type Description
mxid string

Required: The invited matrix user ID. Must be equal to the user_id property of the event.

signatures {string: {string: string}}

Required: A single signature from the verifying server, in the format specified by the Signing Events section of the server-server API.

token string

Required: The token property of the containing third_party_invite object.

Examples

{
  "content": {
    "avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
    "displayname": "Alice Margatroid",
    "membership": "join",
    "reason": "Looking for support"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "state_key": "@alice:example.org",
  "type": "m.room.member",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}
{
  "content": {
    "avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
    "displayname": "Alice Margatroid",
    "membership": "invite",
    "reason": "Looking for support"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "state_key": "@alice:example.org",
  "type": "m.room.member",
  "unsigned": {
    "age": 1234,
    "invite_room_state": [
      {
        "content": {
          "name": "Example Room"
        },
        "sender": "@bob:example.org",
        "state_key": "",
        "type": "m.room.name"
      },
      {
        "content": {
          "join_rule": "invite"
        },
        "sender": "@bob:example.org",
        "state_key": "",
        "type": "m.room.join_rules"
      }
    ]
  }
}
{
  "content": {
    "avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
    "displayname": "Alice Margatroid",
    "join_authorised_via_users_server": "@bob:other.example.org",
    "membership": "join"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "state_key": "@alice:example.org",
  "type": "m.room.member",
  "unsigned": {
    "age": 1234
  }
}
{
  "content": {
    "avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
    "displayname": "Alice Margatroid",
    "membership": "knock",
    "reason": "Looking for support"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "state_key": "@alice:example.org",
  "type": "m.room.member",
  "unsigned": {
    "age": 1234,
    "knock_room_state": [
      {
        "content": {
          "name": "Example Room"
        },
        "sender": "@bob:example.org",
        "state_key": "",
        "type": "m.room.name"
      },
      {
        "content": {
          "join_rule": "knock"
        },
        "sender": "@bob:example.org",
        "state_key": "",
        "type": "m.room.join_rules"
      }
    ]
  }
}
{
  "content": {
    "avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
    "displayname": "Alice Margatroid",
    "membership": "invite",
    "third_party_invite": {
      "display_name": "alice",
      "signed": {
        "mxid": "@alice:example.org",
        "signatures": {
          "magic.forest": {
            "ed25519:3": "fQpGIW1Snz+pwLZu6sTy2aHy/DYWWTspTJRPyNp0PKkymfIsNffysMl6ObMMFdIJhk6g6pwlIqZ54rxo8SLmAg"
          }
        },
        "token": "abc123"
      }
    }
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "state_key": "@alice:example.org",
  "type": "m.room.member",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

m.room.power_levels


This event specifies the minimum level a user must have in order to perform a certain action. It also specifies the levels of each user in the room.

If a user_id is in the users list, then that user_id has the associated power level. Otherwise they have the default level users_default. If users_default is not supplied, it is assumed to be 0. If the room contains no m.room.power_levels event, the room’s creator has a power level of 100, and all other users have a power level of 0.

The level required to send a certain event is governed by events, state_default and events_default. If an event type is specified in events, then the user must have at least the level specified in order to send that event. If the event type is not supplied, it defaults to events_default for Message Events and state_default for State Events.

If there is no state_default in the m.room.power_levels event, or there is no m.room.power_levels event, the state_default is 50. If there is no events_default in the m.room.power_levels event, or there is no m.room.power_levels event, the events_default is 0.

The power level required to invite a user to the room, kick a user from the room, ban a user from the room, or redact an event sent by another user, is defined by invite, kick, ban, and redact, respectively. The levels for kick, ban and redact default to 50 if they are not specified in the m.room.power_levels event, or if the room contains no m.room.power_levels event. invite defaults to 0 in either case.

Note:

The allowed range for power level values is [-(2**53)+1, (2**53)-1], as required by the Canonical JSON specification.

Event type: State event
State key A zero-length string.

Content

Name Type Description
ban integer

The level required to ban a user. Defaults to 50 if unspecified.

events {string: integer}

The level required to send specific event types. This is a mapping from event type to power level required.

events_default integer

The default level required to send message events. Can be overridden by the events key. Defaults to 0 if unspecified.

invite integer

The level required to invite a user. Defaults to 0 if unspecified.

kick integer

The level required to kick a user. Defaults to 50 if unspecified.

notifications Notifications

The power level requirements for specific notification types. This is a mapping from key to power level for that notifications key.

redact integer

The level required to redact an event sent by another user. Defaults to 50 if unspecified.

state_default integer

The default level required to send state events. Can be overridden by the events key. Defaults to 50 if unspecified.

users {User ID: integer}

The power levels for specific users. This is a mapping from user_id to power level for that user.

users_default integer

The power level for users in the room whose user_id is not mentioned in the users key. Defaults to 0 if unspecified.

Note: When there is no m.room.power_levels event in the room, the room creator has a power level of 100, and all other users have a power level of 0.

Notifications
Name Type Description
room integer

The level required to trigger an @room notification. Defaults to 50 if unspecified.

<Other properties> integer

Examples

{
  "content": {
    "ban": 50,
    "events": {
      "m.room.name": 100,
      "m.room.power_levels": 100
    },
    "events_default": 0,
    "invite": 50,
    "kick": 50,
    "notifications": {
      "room": 20
    },
    "redact": 50,
    "state_default": 50,
    "users": {
      "@example:localhost": 100
    },
    "users_default": 0
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "state_key": "",
  "type": "m.room.power_levels",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

Historical events

Some events within the m. namespace might appear in rooms, however they serve no significant meaning in this version of the specification. They are:

  • m.room.aliases

Previous versions of the specification have more information on these events.

Syncing

To read events, the intended flow of operation is for clients to first call the /sync API without a since parameter. This returns the most recent message events for each room, as well as the state of the room at the start of the returned timeline. The response also includes a next_batch field, which should be used as the value of the since parameter in the next call to /sync. Finally, the response includes, for each room, a prev_batch field, which can be passed as a start parameter to the /rooms/<room_id>/messages API to retrieve earlier messages.

For example, a /sync request might return a range of four events E2, E3, E4 and E5 within a given room, omitting two prior events E0 and E1. This can be visualised as follows:

    [E0]->[E1]->[E2]->[E3]->[E4]->[E5]
               ^                      ^
               |                      |
         prev_batch: '1-2-3'        next_batch: 'a-b-c'

Clients then receive new events by “long-polling” the homeserver via the /sync API, passing the value of the next_batch field from the response to the previous call as the since parameter. The client should also pass a timeout parameter. The server will then hold open the HTTP connection for a short period of time waiting for new events, returning early if an event occurs. Only the /sync API (and the deprecated /events API) support long-polling in this way.

Continuing the example above, an incremental sync might report a single new event E6. The response can be visualised as:

    [E0]->[E1]->[E2]->[E3]->[E4]->[E5]->[E6]
                                      ^     ^
                                      |     |
                                      |  next_batch: 'x-y-z'
                                    prev_batch: 'a-b-c'

Normally, all new events which are visible to the client will appear in the response to the /sync API. However, if a large number of events arrive between calls to /sync, a “limited” timeline is returned, containing only the most recent message events. A state “delta” is also returned, summarising any state changes in the omitted part of the timeline. The client may therefore end up with “gaps” in its knowledge of the message timeline. The client can fill these gaps using the /rooms/<room_id>/messages API.

Continuing our example, suppose we make a third /sync request asking for events since the last sync, by passing the next_batch token x-y-z as the since parameter. The server knows about four new events, E7, E8, E9 and E10, but decides this is too many to report at once. Instead, the server sends a limited response containing E8, E9 and E10but omitting E7. This forms a gap, which we can see in the visualisation:

                                            | gap |
                                            | <-> |
    [E0]->[E1]->[E2]->[E3]->[E4]->[E5]->[E6]->[E7]->[E8]->[E9]->[E10]
                                            ^     ^                  ^
                                            |     |                  |
                                 since: 'x-y-z'   |                  |
                                       prev_batch: 'd-e-f'       next_batch: 'u-v-w'

The limited response includes a state delta which describes how the state of the room changes over the gap. This delta explains how to build the state prior to returned timeline (i.e. at E7) from the state the client knows (i.e. at E6). To close the gap, the client should make a request to /rooms/<room_id>/messages with the query parameters from=x-y-z and to=d-e-f.

Events are ordered in this API according to the arrival time of the event on the homeserver. This can conflict with other APIs which order events based on their partial ordering in the event graph. This can result in duplicate events being received (once per distinct API called). Clients SHOULD de-duplicate events based on the event ID when this happens.

The /sync API returns a state list which is separate from the timeline. This state list allows clients to keep their model of the room state in sync with that on the server. In the case of an initial (since-less) sync, the state list represents the complete state of the room at the start of the returned timeline (so in the case of a recently-created room whose state fits entirely in the timeline, the state list will be empty).

In the case of an incremental sync, the state list gives a delta between the state of the room at the since parameter and that at the start of the returned timeline. (It will therefore be empty unless the timeline was limited.)

In both cases, it should be noted that the events returned in the state list did not necessarily take place just before the returned timeline, so clients should not display them to the user in the timeline.

An early design of this specification made the state list represent the room state at the end of the returned timeline, instead of the start. This was unsatisfactory because it led to duplication of events between the state list and the timeline, but more importantly, it made it difficult for clients to show the timeline correctly.

In particular, consider a returned timeline [M0, S1, M2], where M0 and M2 are both messages sent by the same user, and S1 is a state event where that user changes their displayname. If the state list represents the room state at the end of the timeline, the client must take a copy of the state dictionary, and rewind S1, in order to correctly calculate the display name for M0.

GET /_matrix/client/v3/sync


Synchronise the client’s state with the latest state on the server. Clients use this API when they first log in to get an initial snapshot of the state on the server, and then continue to call this API to get incremental deltas to the state, and to receive new messages.

Note: This endpoint supports lazy-loading. See Filtering for more information. Lazy-loading members is only supported on the state part of a RoomFilter for this endpoint. When lazy-loading is enabled, servers MUST include the syncing user’s own membership event when they join a room, or when the full state of rooms is requested, to aid discovering the user’s avatar & displayname.

Further, like other members, the user’s own membership event is eligible for being considered redundant by the server. When a sync is limited, the server MUST return membership events for events in the gap (between since and the start of the returned timeline), regardless as to whether or not they are redundant. This ensures that joins/leaves and profile changes which occur during the gap are not lost.

Note that the default behaviour of state is to include all membership events, alongside other state, when lazy-loading is not enabled.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

query parameters
Name Type Description
filter string

The ID of a filter created using the filter API or a filter JSON object encoded as a string. The server will detect whether it is an ID or a JSON object by whether the first character is a "{" open brace. Passing the JSON inline is best suited to one off requests. Creating a filter using the filter API is recommended for clients that reuse the same filter multiple times, for example in long poll requests.

See Filtering for more information.

full_state boolean

Controls whether to include the full state for all rooms the user is a member of.

If this is set to true, then all state events will be returned, even if since is non-empty. The timeline will still be limited by the since parameter. In this case, the timeout parameter will be ignored and the query will return immediately, possibly with an empty timeline.

If false, and since is non-empty, only state which has changed since the point indicated by since will be returned.

By default, this is false.

set_presence string

Controls whether the client is automatically marked as online by polling this API. If this parameter is omitted then the client is automatically marked as online when it uses this API. Otherwise if the parameter is set to “offline” then the client is not marked as being online when it uses this API. When set to “unavailable”, the client is marked as being idle.

One of: [offline, online, unavailable].

since string

A point in time to continue a sync from. This should be the next_batch token returned by an earlier call to this endpoint.

timeout integer

The maximum time to wait, in milliseconds, before returning this request. If no events (or other data) become available before this time elapses, the server will return a response with empty fields.

By default, this is 0, so the server will return immediately even if the response is empty.


Responses

Status Description
200 The initial snapshot or delta for the client to use to update their state.

200 response

Name Type Description
account_data Account Data

The global private data created by this user.

device_lists DeviceLists

Information on end-to-end device updates, as specified in End-to-end encryption.

device_one_time_keys_count {string: integer}

Information on end-to-end encryption keys, as specified in End-to-end encryption.

next_batch string

Required: The batch token to supply in the since param of the next /sync request.

presence Presence

The updates to the presence status of other users.

rooms Rooms

Updates to rooms.

to_device ToDevice

Information on the send-to-device messages for the client device, as defined in Send-to-Device messaging.

Account Data
Name Type Description
events [Event]

List of events.

Event
Name Type Description
content object

Required: The fields in this object will vary depending on the type of event. When interacting with the REST API, this is the HTTP body.

type string

Required: The type of event. This SHOULD be namespaced similar to Java package naming conventions e.g. ‘com.example.subdomain.event.type’

Presence
Name Type Description
events [Event]

List of events.

Rooms
Name Type Description
invite {Room ID: Invited Room}

The rooms that the user has been invited to, mapped as room ID to room information.

join {Room ID: Joined Room}

The rooms that the user has joined, mapped as room ID to room information.

knock {Room ID: Knocked Room}

The rooms that the user has knocked upon, mapped as room ID to room information.

leave {Room ID: Left Room}

The rooms that the user has left or been banned from, mapped as room ID to room information.

Invited Room
Name Type Description
invite_state InviteState

The stripped state of a room that the user has been invited to.

InviteState
Name Type Description
events [StrippedStateEvent]

The stripped state events that form the invite state.

StrippedStateEvent
Name Type Description
content EventContent

Required: The content for the event.

sender string

Required: The sender for the event.

state_key string

Required: The state_key for the event.

type string

Required: The type for the event.

Joined Room
Name Type Description
account_data Account Data

The private data that this user has attached to this room.

ephemeral Ephemeral

The new ephemeral events in the room (events that aren’t recorded in the timeline or state of the room). In this version of the spec, these are typing notification and read receipt events.

state State

Updates to the state, between the time indicated by the since parameter, and the start of the timeline (or all state up to the start of the timeline, if since is not given, or full_state is true).

N.B. state updates for m.room.member events will be incomplete if lazy_load_members is enabled in the /sync filter, and only return the member events required to display the senders of the timeline events in this response.

summary RoomSummary

Information about the room which clients may need to correctly render it to users.

timeline Timeline

The timeline of messages and state changes in the room.

unread_notifications Unread Notification Counts

Counts of unread notifications for this room. See the Receiving notifications section for more information on how these are calculated.

If unread_thread_notifications was specified as true on the RoomEventFilter, these counts will only be for the main timeline rather than all events in the room. See the threading module for more information.

Changed in v1.4: Updated to reflect behaviour of having unread_thread_notifications as true in the RoomEventFilter for /sync.

unread_thread_notifications {Event ID: ThreadNotificationCounts}

If unread_thread_notifications was specified as true on the RoomEventFilter, the notification counts for each thread in this room. The object is keyed by thread root ID, with values matching unread_notifications.

If a thread does not have any notifications it can be omitted from this object. If no threads have notification counts, this whole object can be omitted.

Added in v1.4

Ephemeral
Name Type Description
events [Event]

List of events.

State
Name Type Description
events [ClientEventWithoutRoomID]

List of events.

ClientEventWithoutRoomID
Name Type Description
content object

Required: The body of this event, as created by the client which sent it.

event_id string

Required: The globally unique identifier for this event.

origin_server_ts integer

Required: Timestamp (in milliseconds since the unix epoch) on originating homeserver when this event was sent.

sender string

Required: Contains the fully-qualified ID of the user who sent this event.

state_key string

Present if, and only if, this event is a state event. The key making this piece of state unique in the room. Note that it is often an empty string.

State keys starting with an @ are reserved for referencing user IDs, such as room members. With the exception of a few events, state events set with a given user’s ID as the state key MUST only be set by that user.

type string

Required: The type of the event.

unsigned UnsignedData

Contains optional extra information about the event.

UnsignedData
Name Type Description
age integer

The time in milliseconds that has elapsed since the event was sent. This field is generated by the local homeserver, and may be incorrect if the local time on at least one of the two servers is out of sync, which can cause the age to either be negative or greater than it actually is.

membership string

The room membership of the user making the request, at the time of the event.

This property is the value of the membership property of the requesting user’s m.room.member state at the point of the event, including any changes caused by the event. If the user had yet to join the room at the time of the event (i.e, they have no m.room.member state), this property is set to leave.

Homeservers SHOULD populate this property wherever practical, but they MAY omit it if necessary (for example, if calculating the value is expensive, servers might choose to only implement it in encrypted rooms). The property is not normally populated in events pushed to application services via the application service transaction API (where there is no clear definition of “requesting user”).

Added in v1.11

prev_content EventContent

The previous content for this event. This field is generated by the local homeserver, and is only returned if the event is a state event, and the client has permission to see the previous content.

Changed in v1.2: Previously, this field was specified at the top level of returned events rather than in unsigned (with the exception of the GET .../notifications endpoint), though in practice no known server implementations honoured this.

redacted_because ClientEventWithoutRoomID

The event that redacted this event, if any.

transaction_id string

The client-supplied transaction ID, for example, provided via PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}, if the client being given the event is the same one which sent it.

RoomSummary
Name Type Description
m.heroes [string]

The users which can be used to generate a room name if the room does not have one. Required if the room’s m.room.name or m.room.canonical_alias state events are unset or empty.

This should be the first 5 members of the room, ordered by stream ordering, which are joined or invited. The list must never include the client’s own user ID. When no joined or invited members are available, this should consist of the banned and left users. More than 5 members may be provided, however less than 5 should only be provided when there are less than 5 members to represent.

When lazy-loading room members is enabled, the membership events for the heroes MUST be included in the state, unless they are redundant. When the list of users changes, the server notifies the client by sending a fresh list of heroes. If there are no changes since the last sync, this field may be omitted.

m.invited_member_count integer

The number of users with membership of invite. If this field has not changed since the last sync, it may be omitted. Required otherwise.

m.joined_member_count integer

The number of users with membership of join, including the client’s own user ID. If this field has not changed since the last sync, it may be omitted. Required otherwise.

Timeline
Name Type Description
events [ClientEventWithoutRoomID]

Required: List of events.

limited boolean

True if the number of events returned was limited by the limit on the filter.

prev_batch string

A token that can be supplied to the from parameter of the /rooms/<room_id>/messages endpoint in order to retrieve earlier events. If no earlier events are available, this property may be omitted from the response.

Unread Notification Counts
Name Type Description
highlight_count integer

The number of unread notifications for this room with the highlight flag set.

notification_count integer

The total number of unread notifications for this room.

ThreadNotificationCounts
Name Type Description
highlight_count integer

The number of unread notifications for this thread with the highlight flag set.

notification_count integer

The total number of unread notifications for this thread.

Knocked Room
Name Type Description
knock_state KnockState

The stripped state of a room that the user has knocked upon.

KnockState
Name Type Description
events [StrippedStateEvent]

The stripped state events that form the knock state.

Left Room
Name Type Description
account_data Account Data

The private data that this user has attached to this room.

state State

The state updates for the room up to the start of the timeline.

timeline Timeline

The timeline of messages and state changes in the room up to the point when the user left.

{
  "account_data": {
    "events": [
      {
        "content": {
          "custom_config_key": "custom_config_value"
        },
        "type": "org.example.custom.config"
      }
    ]
  },
  "next_batch": "s72595_4483_1934",
  "presence": {
    "events": [
      {
        "content": {
          "avatar_url": "mxc://localhost/wefuiwegh8742w",
          "currently_active": false,
          "last_active_ago": 2478593,
          "presence": "online",
          "status_msg": "Making cupcakes"
        },
        "sender": "@example:localhost",
        "type": "m.presence"
      }
    ]
  },
  "rooms": {
    "invite": {
      "!696r7674:example.com": {
        "invite_state": {
          "events": [
            {
              "content": {
                "name": "My Room Name"
              },
              "sender": "@alice:example.com",
              "state_key": "",
              "type": "m.room.name"
            },
            {
              "content": {
                "membership": "invite"
              },
              "sender": "@alice:example.com",
              "state_key": "@bob:example.com",
              "type": "m.room.member"
            }
          ]
        }
      }
    },
    "join": {
      "!726s6s6q:example.com": {
        "account_data": {
          "events": [
            {
              "content": {
                "tags": {
                  "u.work": {
                    "order": 0.9
                  }
                }
              },
              "type": "m.tag"
            },
            {
              "content": {
                "custom_config_key": "custom_config_value"
              },
              "type": "org.example.custom.room.config"
            }
          ]
        },
        "ephemeral": {
          "events": [
            {
              "content": {
                "user_ids": [
                  "@alice:matrix.org",
                  "@bob:example.com"
                ]
              },
              "type": "m.typing"
            },
            {
              "content": {
                "$1435641916114394fHBLK:matrix.org": {
                  "m.read": {
                    "@erikj:jki.re": {
                      "ts": 1436451550453
                    }
                  },
                  "m.read.private": {
                    "@self:example.org": {
                      "ts": 1661384801651
                    }
                  }
                }
              },
              "type": "m.receipt"
            }
          ]
        },
        "state": {
          "events": [
            {
              "content": {
                "avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
                "displayname": "Alice Margatroid",
                "membership": "join",
                "reason": "Looking for support"
              },
              "event_id": "$143273582443PhrSn:example.org",
              "origin_server_ts": 1432735824653,
              "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
              "sender": "@example:example.org",
              "state_key": "@alice:example.org",
              "type": "m.room.member",
              "unsigned": {
                "age": 1234,
                "membership": "join"
              }
            }
          ]
        },
        "summary": {
          "m.heroes": [
            "@alice:example.com",
            "@bob:example.com"
          ],
          "m.invited_member_count": 0,
          "m.joined_member_count": 2
        },
        "timeline": {
          "events": [
            {
              "content": {
                "avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
                "displayname": "Alice Margatroid",
                "membership": "join",
                "reason": "Looking for support"
              },
              "event_id": "$143273582443PhrSn:example.org",
              "origin_server_ts": 1432735824653,
              "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
              "sender": "@example:example.org",
              "state_key": "@alice:example.org",
              "type": "m.room.member",
              "unsigned": {
                "age": 1234,
                "membership": "join"
              }
            },
            {
              "content": {
                "body": "This is an example text message",
                "format": "org.matrix.custom.html",
                "formatted_body": "<b>This is an example text message</b>",
                "msgtype": "m.text"
              },
              "event_id": "$143273582443PhrSn:example.org",
              "origin_server_ts": 1432735824653,
              "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
              "sender": "@example:example.org",
              "type": "m.room.message",
              "unsigned": {
                "age": 1234,
                "membership": "join"
              }
            }
          ],
          "limited": true,
          "prev_batch": "t34-23535_0_0"
        },
        "unread_notifications": {
          "highlight_count": 1,
          "notification_count": 5
        },
        "unread_thread_notifications": {
          "$threadroot": {
            "highlight_count": 3,
            "notification_count": 6
          }
        }
      }
    },
    "knock": {
      "!223asd456:example.com": {
        "knock_state": {
          "events": [
            {
              "content": {
                "name": "My Room Name"
              },
              "sender": "@alice:example.com",
              "state_key": "",
              "type": "m.room.name"
            },
            {
              "content": {
                "membership": "knock"
              },
              "sender": "@bob:example.com",
              "state_key": "@bob:example.com",
              "type": "m.room.member"
            }
          ]
        }
      }
    },
    "leave": {}
  }
}

GET /_matrix/client/v3/events


This API is deprecated and will be removed from a future release.

This will listen for new events and return them to the caller. This will block until an event is received, or until the timeout is reached.

This endpoint was deprecated in r0 of this specification. Clients should instead call the /sync endpoint with a since parameter. See the migration guide.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

query parameters
Name Type Description
from string

The token to stream from. This token is either from a previous request to this API or from the initial sync API.

timeout integer

The maximum time in milliseconds to wait for an event.


Responses

Status Description
200 The events received, which may be none.
400 Bad pagination from parameter.

200 response

Name Type Description
chunk [ClientEvent]

An array of events.

end string

A token which correlates to the end of chunk. This token should be used in the next request to /events.

start string

A token which correlates to the start of chunk. This is usually the same token supplied to from=.

ClientEvent
Name Type Description
content object

Required: The body of this event, as created by the client which sent it.

event_id string

Required: The globally unique identifier for this event.

origin_server_ts integer

Required: Timestamp (in milliseconds since the unix epoch) on originating homeserver when this event was sent.

room_id string

Required: The ID of the room associated with this event.

sender string

Required: Contains the fully-qualified ID of the user who sent this event.

state_key string

Present if, and only if, this event is a state event. The key making this piece of state unique in the room. Note that it is often an empty string.

State keys starting with an @ are reserved for referencing user IDs, such as room members. With the exception of a few events, state events set with a given user’s ID as the state key MUST only be set by that user.

type string

Required: The type of the event.

unsigned UnsignedData

Contains optional extra information about the event.

UnsignedData
Name Type Description
age integer

The time in milliseconds that has elapsed since the event was sent. This field is generated by the local homeserver, and may be incorrect if the local time on at least one of the two servers is out of sync, which can cause the age to either be negative or greater than it actually is.

membership string

The room membership of the user making the request, at the time of the event.

This property is the value of the membership property of the requesting user’s m.room.member state at the point of the event, including any changes caused by the event. If the user had yet to join the room at the time of the event (i.e, they have no m.room.member state), this property is set to leave.

Homeservers SHOULD populate this property wherever practical, but they MAY omit it if necessary (for example, if calculating the value is expensive, servers might choose to only implement it in encrypted rooms). The property is not normally populated in events pushed to application services via the application service transaction API (where there is no clear definition of “requesting user”).

Added in v1.11

prev_content EventContent

The previous content for this event. This field is generated by the local homeserver, and is only returned if the event is a state event, and the client has permission to see the previous content.

Changed in v1.2: Previously, this field was specified at the top level of returned events rather than in unsigned (with the exception of the GET .../notifications endpoint), though in practice no known server implementations honoured this.

redacted_because ClientEvent

The event that redacted this event, if any.

transaction_id string

The client-supplied transaction ID, for example, provided via PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}, if the client being given the event is the same one which sent it.

{
  "chunk": [
    {
      "content": {
        "body": "This is an example text message",
        "format": "org.matrix.custom.html",
        "formatted_body": "<b>This is an example text message</b>",
        "msgtype": "m.text"
      },
      "event_id": "$143273582443PhrSn:example.org",
      "origin_server_ts": 1432735824653,
      "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
      "sender": "@example:example.org",
      "type": "m.room.message",
      "unsigned": {
        "age": 1234,
        "membership": "join"
      }
    }
  ],
  "end": "s3457_9_0",
  "start": "s3456_9_0"
}

GET /_matrix/client/v3/events/{eventId}


This API is deprecated and will be removed from a future release.

Get a single event based on event_id. You must have permission to retrieve this event e.g. by being a member in the room for this event.

This endpoint was deprecated in r0 of this specification. Clients should instead call the /rooms/{roomId}/event/{eventId} API or the /rooms/{roomId}/context/{eventId API.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
eventId string

Required: The event ID to get.


Responses

Status Description
200 The full event.
404 The event was not found or you do not have permission to read this event.

200 response

ClientEvent
Name Type Description
content object

Required: The body of this event, as created by the client which sent it.

event_id string

Required: The globally unique identifier for this event.

origin_server_ts integer

Required: Timestamp (in milliseconds since the unix epoch) on originating homeserver when this event was sent.

room_id string

Required: The ID of the room associated with this event.

sender string

Required: Contains the fully-qualified ID of the user who sent this event.

state_key string

Present if, and only if, this event is a state event. The key making this piece of state unique in the room. Note that it is often an empty string.

State keys starting with an @ are reserved for referencing user IDs, such as room members. With the exception of a few events, state events set with a given user’s ID as the state key MUST only be set by that user.

type string

Required: The type of the event.

unsigned UnsignedData

Contains optional extra information about the event.

UnsignedData
Name Type Description
age integer

The time in milliseconds that has elapsed since the event was sent. This field is generated by the local homeserver, and may be incorrect if the local time on at least one of the two servers is out of sync, which can cause the age to either be negative or greater than it actually is.

membership string

The room membership of the user making the request, at the time of the event.

This property is the value of the membership property of the requesting user’s m.room.member state at the point of the event, including any changes caused by the event. If the user had yet to join the room at the time of the event (i.e, they have no m.room.member state), this property is set to leave.

Homeservers SHOULD populate this property wherever practical, but they MAY omit it if necessary (for example, if calculating the value is expensive, servers might choose to only implement it in encrypted rooms). The property is not normally populated in events pushed to application services via the application service transaction API (where there is no clear definition of “requesting user”).

Added in v1.11

prev_content EventContent

The previous content for this event. This field is generated by the local homeserver, and is only returned if the event is a state event, and the client has permission to see the previous content.

Changed in v1.2: Previously, this field was specified at the top level of returned events rather than in unsigned (with the exception of the GET .../notifications endpoint), though in practice no known server implementations honoured this.

redacted_because ClientEvent

The event that redacted this event, if any.

transaction_id string

The client-supplied transaction ID, for example, provided via PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}, if the client being given the event is the same one which sent it.

{
  "content": {
    "body": "This is an example text message",
    "format": "org.matrix.custom.html",
    "formatted_body": "<b>This is an example text message</b>",
    "msgtype": "m.text"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "type": "m.room.message",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

GET /_matrix/client/v3/initialSync


This API is deprecated and will be removed from a future release.

This returns the full state for this user, with an optional limit on the number of messages per room to return.

This endpoint was deprecated in r0 of this specification. Clients should instead call the /sync endpoint with no since parameter. See the migration guide.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

query parameters
Name Type Description
archived boolean

Whether to include rooms that the user has left. If false then only rooms that the user has been invited to or has joined are included. If set to true then rooms that the user has left are included as well. By default this is false.

limit integer

The maximum number of messages to return for each room.


Responses

Status Description
200 The user’s current state.
404 There is no avatar URL for this user or this user does not exist.

200 response

Name Type Description
account_data [Event]

The global private data created by this user.

end string

Required: A token which correlates to the end of the timelines returned. This token should be used with the /events endpoint to listen for new events.

presence [Event]

Required: A list of presence events.

rooms [RoomInfo]

Required:

Event
Name Type Description
content object

Required: The fields in this object will vary depending on the type of event. When interacting with the REST API, this is the HTTP body.

type string

Required: The type of event. This SHOULD be namespaced similar to Java package naming conventions e.g. ‘com.example.subdomain.event.type’

RoomInfo
Name Type Description
account_data [Event]

The private data that this user has attached to this room.

invite InviteEvent

The invite event if membership is invite

membership string

Required: The user’s membership state in this room.

One of: [invite, join, leave, ban].

messages PaginationChunk

The pagination chunk for this room.

room_id string

Required: The ID of this room.

state [ClientEvent]

If the user is a member of the room this will be the current state of the room as a list of events. If the user has left the room this will be the state of the room when they left it.

visibility string

Whether this room is visible to the /publicRooms API or not."

One of: [private, public].

InviteEvent
Name Type Description
content object

Required: The body of this event, as created by the client which sent it.

event_id string

Required: The globally unique identifier for this event.

origin_server_ts integer

Required: Timestamp (in milliseconds since the unix epoch) on originating homeserver when this event was sent.

room_id string

Required: The ID of the room associated with this event.

sender string

Required: Contains the fully-qualified ID of the user who sent this event.

state_key string

Present if, and only if, this event is a state event. The key making this piece of state unique in the room. Note that it is often an empty string.

State keys starting with an @ are reserved for referencing user IDs, such as room members. With the exception of a few events, state events set with a given user’s ID as the state key MUST only be set by that user.

type string

Required: The type of the event.

unsigned UnsignedData

Contains optional extra information about the event.

UnsignedData
Name Type Description
age integer

The time in milliseconds that has elapsed since the event was sent. This field is generated by the local homeserver, and may be incorrect if the local time on at least one of the two servers is out of sync, which can cause the age to either be negative or greater than it actually is.

membership string

The room membership of the user making the request, at the time of the event.

This property is the value of the membership property of the requesting user’s m.room.member state at the point of the event, including any changes caused by the event. If the user had yet to join the room at the time of the event (i.e, they have no m.room.member state), this property is set to leave.

Homeservers SHOULD populate this property wherever practical, but they MAY omit it if necessary (for example, if calculating the value is expensive, servers might choose to only implement it in encrypted rooms). The property is not normally populated in events pushed to application services via the application service transaction API (where there is no clear definition of “requesting user”).

Added in v1.11

prev_content EventContent

The previous content for this event. This field is generated by the local homeserver, and is only returned if the event is a state event, and the client has permission to see the previous content.

Changed in v1.2: Previously, this field was specified at the top level of returned events rather than in unsigned (with the exception of the GET .../notifications endpoint), though in practice no known server implementations honoured this.

redacted_because ClientEvent

The event that redacted this event, if any.

transaction_id string

The client-supplied transaction ID, for example, provided via PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}, if the client being given the event is the same one which sent it.

PaginationChunk
Name Type Description
chunk [ClientEvent]

Required: If the user is a member of the room this will be a list of the most recent messages for this room. If the user has left the room this will be the messages that preceded them leaving. This array will consist of at most limit elements.

end string

Required: A token which correlates to the end of chunk. Can be passed to /rooms/<room_id>/messages to retrieve later events.

start string

A token which correlates to the start of chunk. Can be passed to /rooms/<room_id>/messages to retrieve earlier events.

If no earlier events are available, this property may be omitted from the response.

ClientEvent
Name Type Description
content object

Required: The body of this event, as created by the client which sent it.

event_id string

Required: The globally unique identifier for this event.

origin_server_ts integer

Required: Timestamp (in milliseconds since the unix epoch) on originating homeserver when this event was sent.

room_id string

Required: The ID of the room associated with this event.

sender string

Required: Contains the fully-qualified ID of the user who sent this event.

state_key string

Present if, and only if, this event is a state event. The key making this piece of state unique in the room. Note that it is often an empty string.

State keys starting with an @ are reserved for referencing user IDs, such as room members. With the exception of a few events, state events set with a given user’s ID as the state key MUST only be set by that user.

type string

Required: The type of the event.

unsigned UnsignedData

Contains optional extra information about the event.

{
  "account_data": [
    {
      "content": {
        "custom_config_key": "custom_config_value"
      },
      "type": "org.example.custom.config"
    }
  ],
  "end": "s3456_9_0",
  "presence": [
    {
      "content": {
        "avatar_url": "mxc://localhost/wefuiwegh8742w",
        "currently_active": false,
        "last_active_ago": 2478593,
        "presence": "online",
        "status_msg": "Making cupcakes"
      },
      "sender": "@example:localhost",
      "type": "m.presence"
    }
  ],
  "rooms": [
    {
      "account_data": [
        {
          "content": {
            "tags": {
              "work": {
                "order": 1
              }
            }
          },
          "type": "m.tag"
        },
        {
          "content": {
            "custom_config_key": "custom_config_value"
          },
          "type": "org.example.custom.room.config"
        }
      ],
      "membership": "join",
      "messages": {
        "chunk": [
          {
            "content": {
              "body": "This is an example text message",
              "format": "org.matrix.custom.html",
              "formatted_body": "<b>This is an example text message</b>",
              "msgtype": "m.text"
            },
            "event_id": "$143273582443PhrSn:example.org",
            "origin_server_ts": 1432735824653,
            "room_id": "!TmaZBKYIFrIPVGoUYp:localhost",
            "sender": "@example:example.org",
            "type": "m.room.message",
            "unsigned": {
              "age": 1234,
              "membership": "join"
            }
          },
          {
            "content": {
              "body": "Gangnam Style",
              "info": {
                "duration": 2140786,
                "h": 320,
                "mimetype": "video/mp4",
                "size": 1563685,
                "thumbnail_info": {
                  "h": 300,
                  "mimetype": "image/jpeg",
                  "size": 46144,
                  "w": 300
                },
                "thumbnail_url": "mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe",
                "w": 480
              },
              "msgtype": "m.video",
              "url": "mxc://example.org/a526eYUSFFxlgbQYZmo442"
            },
            "event_id": "$143273582443PhrSn:example.org",
            "origin_server_ts": 1432735824653,
            "room_id": "!TmaZBKYIFrIPVGoUYp:localhost",
            "sender": "@example:example.org",
            "type": "m.room.message",
            "unsigned": {
              "age": 1234,
              "membership": "join"
            }
          }
        ],
        "end": "s3456_9_0",
        "start": "t44-3453_9_0"
      },
      "room_id": "!TmaZBKYIFrIPVGoUYp:localhost",
      "state": [
        {
          "content": {
            "join_rule": "public"
          },
          "event_id": "$143273582443PhrSn:example.org",
          "origin_server_ts": 1432735824653,
          "room_id": "!TmaZBKYIFrIPVGoUYp:localhost",
          "sender": "@example:example.org",
          "state_key": "",
          "type": "m.room.join_rules",
          "unsigned": {
            "age": 1234,
            "membership": "join"
          }
        },
        {
          "content": {
            "avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
            "displayname": "Alice Margatroid",
            "membership": "join",
            "reason": "Looking for support"
          },
          "event_id": "$143273582443PhrSn:example.org",
          "origin_server_ts": 1432735824653,
          "room_id": "!TmaZBKYIFrIPVGoUYp:localhost",
          "sender": "@example:example.org",
          "state_key": "@alice:example.org",
          "type": "m.room.member",
          "unsigned": {
            "age": 1234,
            "membership": "join"
          }
        },
        {
          "content": {
            "m.federate": true,
            "predecessor": {
              "event_id": "$something:example.org",
              "room_id": "!oldroom:example.org"
            },
            "room_version": "11"
          },
          "event_id": "$143273582443PhrSn:example.org",
          "origin_server_ts": 1432735824653,
          "room_id": "!TmaZBKYIFrIPVGoUYp:localhost",
          "sender": "@example:example.org",
          "state_key": "",
          "type": "m.room.create",
          "unsigned": {
            "age": 1234,
            "membership": "join"
          }
        },
        {
          "content": {
            "ban": 50,
            "events": {
              "m.room.name": 100,
              "m.room.power_levels": 100
            },
            "events_default": 0,
            "invite": 50,
            "kick": 50,
            "notifications": {
              "room": 20
            },
            "redact": 50,
            "state_default": 50,
            "users": {
              "@example:localhost": 100
            },
            "users_default": 0
          },
          "event_id": "$143273582443PhrSn:example.org",
          "origin_server_ts": 1432735824653,
          "room_id": "!TmaZBKYIFrIPVGoUYp:localhost",
          "sender": "@example:example.org",
          "state_key": "",
          "type": "m.room.power_levels",
          "unsigned": {
            "age": 1234,
            "membership": "join"
          }
        }
      ],
      "visibility": "private"
    }
  ]
}

Getting events for a room

There are several APIs provided to GET events for a room:

GET /_matrix/client/v3/rooms/{roomId}/event/{eventId}


Get a single event based on roomId/eventId. You must have permission to retrieve this event e.g. by being a member in the room for this event.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
eventId string

Required: The event ID to get.

roomId string

Required: The ID of the room the event is in.


Responses

Status Description
200 The full event.
404 The event was not found or you do not have permission to read this event.

200 response

ClientEvent
Name Type Description
content object

Required: The body of this event, as created by the client which sent it.

event_id string

Required: The globally unique identifier for this event.

origin_server_ts integer

Required: Timestamp (in milliseconds since the unix epoch) on originating homeserver when this event was sent.

room_id string

Required: The ID of the room associated with this event.

sender string

Required: Contains the fully-qualified ID of the user who sent this event.

state_key string

Present if, and only if, this event is a state event. The key making this piece of state unique in the room. Note that it is often an empty string.

State keys starting with an @ are reserved for referencing user IDs, such as room members. With the exception of a few events, state events set with a given user’s ID as the state key MUST only be set by that user.

type string

Required: The type of the event.

unsigned UnsignedData

Contains optional extra information about the event.

UnsignedData
Name Type Description
age integer

The time in milliseconds that has elapsed since the event was sent. This field is generated by the local homeserver, and may be incorrect if the local time on at least one of the two servers is out of sync, which can cause the age to either be negative or greater than it actually is.

membership string

The room membership of the user making the request, at the time of the event.

This property is the value of the membership property of the requesting user’s m.room.member state at the point of the event, including any changes caused by the event. If the user had yet to join the room at the time of the event (i.e, they have no m.room.member state), this property is set to leave.

Homeservers SHOULD populate this property wherever practical, but they MAY omit it if necessary (for example, if calculating the value is expensive, servers might choose to only implement it in encrypted rooms). The property is not normally populated in events pushed to application services via the application service transaction API (where there is no clear definition of “requesting user”).

Added in v1.11

prev_content EventContent

The previous content for this event. This field is generated by the local homeserver, and is only returned if the event is a state event, and the client has permission to see the previous content.

Changed in v1.2: Previously, this field was specified at the top level of returned events rather than in unsigned (with the exception of the GET .../notifications endpoint), though in practice no known server implementations honoured this.

redacted_because ClientEvent

The event that redacted this event, if any.

transaction_id string

The client-supplied transaction ID, for example, provided via PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}, if the client being given the event is the same one which sent it.

{
  "content": {
    "body": "This is an example text message",
    "format": "org.matrix.custom.html",
    "formatted_body": "<b>This is an example text message</b>",
    "msgtype": "m.text"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!636q39766251:matrix.org",
  "sender": "@example:example.org",
  "type": "m.room.message",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND",
  "error": "Event not found."
}

GET /_matrix/client/v3/rooms/{roomId}/joined_members


This API returns a map of MXIDs to member info objects for members of the room. The current user must be in the room for it to work, unless it is an Application Service in which case any of the AS’s users must be in the room. This API is primarily for Application Services and should be faster to respond than /members as it can be implemented more efficiently on the server.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string

Required: The room to get the members of.


Responses

Status Description
200 A map of MXID to room member objects.
403 You aren’t a member of the room.

200 response

Name Type Description
joined {User ID: RoomMember}

A map from user ID to a RoomMember object.

RoomMember
Name Type Description
avatar_url URI

The avatar of the user this object is representing, as an mxc:// URI.

display_name string

The display name of the user this object is representing.

{
  "joined": {
    "@bar:example.com": {
      "avatar_url": "mxc://riot.ovh/printErCATzZijQsSDWorRaK",
      "display_name": "Bar"
    }
  }
}

GET /_matrix/client/v3/rooms/{roomId}/members


Get the list of members for this room.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string

Required: The room to get the member events for.

query parameters
Name Type Description
at string

The point in time (pagination token) to return members for in the room. This token can be obtained from a prev_batch token returned for each room by the sync API. Defaults to the current state of the room, as determined by the server.

membership string

The kind of membership to filter for. Defaults to no filtering if unspecified. When specified alongside not_membership, the two parameters create an ‘or’ condition: either the membership is the same as membership or is not the same as not_membership.

One of: [join, invite, knock, leave, ban].

not_membership string

The kind of membership to exclude from the results. Defaults to no filtering if unspecified.

One of: [join, invite, knock, leave, ban].


Responses

Status Description
200 A list of members of the room. If you are joined to the room then this will be the current members of the room. If you have left the room then this will be the members of the room when you left.
403 You aren’t a member of the room and weren’t previously a member of the room.

200 response

Name Type Description
chunk [ClientEvent]
ClientEvent
Name Type Description
content object

Required: The body of this event, as created by the client which sent it.

event_id string

Required: The globally unique identifier for this event.

origin_server_ts integer

Required: Timestamp (in milliseconds since the unix epoch) on originating homeserver when this event was sent.

room_id string

Required: The ID of the room associated with this event.

sender string

Required: Contains the fully-qualified ID of the user who sent this event.

state_key string

Present if, and only if, this event is a state event. The key making this piece of state unique in the room. Note that it is often an empty string.

State keys starting with an @ are reserved for referencing user IDs, such as room members. With the exception of a few events, state events set with a given user’s ID as the state key MUST only be set by that user.

type string

Required: The type of the event.

unsigned UnsignedData

Contains optional extra information about the event.

UnsignedData
Name Type Description
age integer

The time in milliseconds that has elapsed since the event was sent. This field is generated by the local homeserver, and may be incorrect if the local time on at least one of the two servers is out of sync, which can cause the age to either be negative or greater than it actually is.

membership string

The room membership of the user making the request, at the time of the event.

This property is the value of the membership property of the requesting user’s m.room.member state at the point of the event, including any changes caused by the event. If the user had yet to join the room at the time of the event (i.e, they have no m.room.member state), this property is set to leave.

Homeservers SHOULD populate this property wherever practical, but they MAY omit it if necessary (for example, if calculating the value is expensive, servers might choose to only implement it in encrypted rooms). The property is not normally populated in events pushed to application services via the application service transaction API (where there is no clear definition of “requesting user”).

Added in v1.11

prev_content EventContent

The previous content for this event. This field is generated by the local homeserver, and is only returned if the event is a state event, and the client has permission to see the previous content.

Changed in v1.2: Previously, this field was specified at the top level of returned events rather than in unsigned (with the exception of the GET .../notifications endpoint), though in practice no known server implementations honoured this.

redacted_because ClientEvent

The event that redacted this event, if any.

transaction_id string

The client-supplied transaction ID, for example, provided via PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}, if the client being given the event is the same one which sent it.

{
  "chunk": [
    {
      "content": {
        "avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
        "displayname": "Alice Margatroid",
        "membership": "join",
        "reason": "Looking for support"
      },
      "event_id": "$143273582443PhrSn:example.org",
      "origin_server_ts": 1432735824653,
      "room_id": "!636q39766251:example.com",
      "sender": "@example:example.org",
      "state_key": "@alice:example.org",
      "type": "m.room.member",
      "unsigned": {
        "age": 1234,
        "membership": "join"
      }
    }
  ]
}

GET /_matrix/client/v3/rooms/{roomId}/state


Get the state events for the current state of a room.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string

Required: The room to look up the state for.


Responses

Status Description
200 The current state of the room
403 You aren’t a member of the room and weren’t previously a member of the room.

200 response

Array of ClientEvent.

ClientEvent
Name Type Description
content object

Required: The body of this event, as created by the client which sent it.

event_id string

Required: The globally unique identifier for this event.

origin_server_ts integer

Required: Timestamp (in milliseconds since the unix epoch) on originating homeserver when this event was sent.

room_id string

Required: The ID of the room associated with this event.

sender string

Required: Contains the fully-qualified ID of the user who sent this event.

state_key string

Present if, and only if, this event is a state event. The key making this piece of state unique in the room. Note that it is often an empty string.

State keys starting with an @ are reserved for referencing user IDs, such as room members. With the exception of a few events, state events set with a given user’s ID as the state key MUST only be set by that user.

type string

Required: The type of the event.

unsigned UnsignedData

Contains optional extra information about the event.

UnsignedData
Name Type Description
age integer

The time in milliseconds that has elapsed since the event was sent. This field is generated by the local homeserver, and may be incorrect if the local time on at least one of the two servers is out of sync, which can cause the age to either be negative or greater than it actually is.

membership string

The room membership of the user making the request, at the time of the event.

This property is the value of the membership property of the requesting user’s m.room.member state at the point of the event, including any changes caused by the event. If the user had yet to join the room at the time of the event (i.e, they have no m.room.member state), this property is set to leave.

Homeservers SHOULD populate this property wherever practical, but they MAY omit it if necessary (for example, if calculating the value is expensive, servers might choose to only implement it in encrypted rooms). The property is not normally populated in events pushed to application services via the application service transaction API (where there is no clear definition of “requesting user”).

Added in v1.11

prev_content EventContent

The previous content for this event. This field is generated by the local homeserver, and is only returned if the event is a state event, and the client has permission to see the previous content.

Changed in v1.2: Previously, this field was specified at the top level of returned events rather than in unsigned (with the exception of the GET .../notifications endpoint), though in practice no known server implementations honoured this.

redacted_because ClientEvent

The event that redacted this event, if any.

transaction_id string

The client-supplied transaction ID, for example, provided via PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}, if the client being given the event is the same one which sent it.

[
  {
    "content": {
      "join_rule": "public"
    },
    "event_id": "$143273582443PhrSn:example.org",
    "origin_server_ts": 1432735824653,
    "room_id": "!636q39766251:example.com",
    "sender": "@example:example.org",
    "state_key": "",
    "type": "m.room.join_rules",
    "unsigned": {
      "age": 1234,
      "membership": "join"
    }
  },
  {
    "content": {
      "avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
      "displayname": "Alice Margatroid",
      "membership": "join",
      "reason": "Looking for support"
    },
    "event_id": "$143273582443PhrSn:example.org",
    "origin_server_ts": 1432735824653,
    "room_id": "!636q39766251:example.com",
    "sender": "@example:example.org",
    "state_key": "@alice:example.org",
    "type": "m.room.member",
    "unsigned": {
      "age": 1234,
      "membership": "join"
    }
  },
  {
    "content": {
      "m.federate": true,
      "predecessor": {
        "event_id": "$something:example.org",
        "room_id": "!oldroom:example.org"
      },
      "room_version": "11"
    },
    "event_id": "$143273582443PhrSn:example.org",
    "origin_server_ts": 1432735824653,
    "room_id": "!636q39766251:example.com",
    "sender": "@example:example.org",
    "state_key": "",
    "type": "m.room.create",
    "unsigned": {
      "age": 1234,
      "membership": "join"
    }
  },
  {
    "content": {
      "ban": 50,
      "events": {
        "m.room.name": 100,
        "m.room.power_levels": 100
      },
      "events_default": 0,
      "invite": 50,
      "kick": 50,
      "notifications": {
        "room": 20
      },
      "redact": 50,
      "state_default": 50,
      "users": {
        "@example:localhost": 100
      },
      "users_default": 0
    },
    "event_id": "$143273582443PhrSn:example.org",
    "origin_server_ts": 1432735824653,
    "room_id": "!636q39766251:example.com",
    "sender": "@example:example.org",
    "state_key": "",
    "type": "m.room.power_levels",
    "unsigned": {
      "age": 1234,
      "membership": "join"
    }
  }
]

GET /_matrix/client/v3/rooms/{roomId}/state/{eventType}/{stateKey}


Looks up the contents of a state event in a room. If the user is joined to the room then the state is taken from the current state of the room. If the user has left the room then the state is taken from the state of the room when they left.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
eventType string

Required: The type of state to look up.

roomId string

Required: The room to look up the state in.

stateKey string

Required: The key of the state to look up. Defaults to an empty string. When an empty string, the trailing slash on this endpoint is optional.


Responses

Status Description
200 The content of the state event.
403 You aren’t a member of the room and weren’t previously a member of the room.
404 The room has no state with the given type or key.

200 response

{
  "name": "Example room name"
}

GET /_matrix/client/v3/rooms/{roomId}/messages


This API returns a list of message and state events for a room. It uses pagination query parameters to paginate history in the room.

Note: This endpoint supports lazy-loading of room member events. See Lazy-loading room members for more information.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string

Required: The room to get events from.

query parameters
Name Type Description
dir string

Required: The direction to return events from. If this is set to f, events will be returned in chronological order starting at from. If it is set to b, events will be returned in reverse chronological order, again starting at from.

One of: [b, f].

filter string

A JSON RoomEventFilter to filter returned events with.

from string

The token to start returning events from. This token can be obtained from a prev_batch or next_batch token returned by the /sync endpoint, or from an end token returned by a previous request to this endpoint.

This endpoint can also accept a value returned as a start token by a previous request to this endpoint, though servers are not required to support this. Clients should not rely on the behaviour.

If it is not provided, the homeserver shall return a list of messages from the first or last (per the value of the dir parameter) visible event in the room history for the requesting user.

Changed in v1.3: Previously, this field was required and paginating from the first or last visible event in the room history wasn’t supported.

limit integer

The maximum number of events to return. Default: 10.

to string

The token to stop returning events at. This token can be obtained from a prev_batch or next_batch token returned by the /sync endpoint, or from an end token returned by a previous request to this endpoint.


Responses

Status Description
200 A list of messages with a new token to request more.
403 You aren’t a member of the room.

200 response

Name Type Description
chunk [ClientEvent]

Required: A list of room events. The order depends on the dir parameter. For dir=b events will be in reverse-chronological order, for dir=f in chronological order. (The exact definition of chronological is dependent on the server implementation.)

Note that an empty chunk does not necessarily imply that no more events are available. Clients should continue to paginate until no end property is returned.

end string

A token corresponding to the end of chunk. This token can be passed back to this endpoint to request further events.

If no further events are available (either because we have reached the start of the timeline, or because the user does not have permission to see any more events), this property is omitted from the response.

start string

Required: A token corresponding to the start of chunk. This will be the same as the value given in from.

state [ClientEvent]

A list of state events relevant to showing the chunk. For example, if lazy_load_members is enabled in the filter then this may contain the membership events for the senders of events in the chunk.

Unless include_redundant_members is true, the server may remove membership events which would have already been sent to the client in prior calls to this endpoint, assuming the membership of those members has not changed.

ClientEvent
Name Type Description
content object

Required: The body of this event, as created by the client which sent it.

event_id string

Required: The globally unique identifier for this event.

origin_server_ts integer

Required: Timestamp (in milliseconds since the unix epoch) on originating homeserver when this event was sent.

room_id string

Required: The ID of the room associated with this event.

sender string

Required: Contains the fully-qualified ID of the user who sent this event.

state_key string

Present if, and only if, this event is a state event. The key making this piece of state unique in the room. Note that it is often an empty string.

State keys starting with an @ are reserved for referencing user IDs, such as room members. With the exception of a few events, state events set with a given user’s ID as the state key MUST only be set by that user.

type string

Required: The type of the event.

unsigned UnsignedData

Contains optional extra information about the event.

UnsignedData
Name Type Description
age integer

The time in milliseconds that has elapsed since the event was sent. This field is generated by the local homeserver, and may be incorrect if the local time on at least one of the two servers is out of sync, which can cause the age to either be negative or greater than it actually is.

membership string

The room membership of the user making the request, at the time of the event.

This property is the value of the membership property of the requesting user’s m.room.member state at the point of the event, including any changes caused by the event. If the user had yet to join the room at the time of the event (i.e, they have no m.room.member state), this property is set to leave.

Homeservers SHOULD populate this property wherever practical, but they MAY omit it if necessary (for example, if calculating the value is expensive, servers might choose to only implement it in encrypted rooms). The property is not normally populated in events pushed to application services via the application service transaction API (where there is no clear definition of “requesting user”).

Added in v1.11

prev_content EventContent

The previous content for this event. This field is generated by the local homeserver, and is only returned if the event is a state event, and the client has permission to see the previous content.

Changed in v1.2: Previously, this field was specified at the top level of returned events rather than in unsigned (with the exception of the GET .../notifications endpoint), though in practice no known server implementations honoured this.

redacted_because ClientEvent

The event that redacted this event, if any.

transaction_id string

The client-supplied transaction ID, for example, provided via PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}, if the client being given the event is the same one which sent it.

{
  "chunk": [
    {
      "content": {
        "body": "This is an example text message",
        "format": "org.matrix.custom.html",
        "formatted_body": "<b>This is an example text message</b>",
        "msgtype": "m.text"
      },
      "event_id": "$143273582443PhrSn:example.org",
      "origin_server_ts": 1432735824653,
      "room_id": "!636q39766251:example.com",
      "sender": "@example:example.org",
      "type": "m.room.message",
      "unsigned": {
        "age": 1234,
        "membership": "join"
      }
    },
    {
      "content": {
        "name": "The room name"
      },
      "event_id": "$143273582443PhrSn:example.org",
      "origin_server_ts": 1432735824653,
      "room_id": "!636q39766251:example.com",
      "sender": "@example:example.org",
      "state_key": "",
      "type": "m.room.name",
      "unsigned": {
        "age": 1234,
        "membership": "join"
      }
    },
    {
      "content": {
        "body": "Gangnam Style",
        "info": {
          "duration": 2140786,
          "h": 320,
          "mimetype": "video/mp4",
          "size": 1563685,
          "thumbnail_info": {
            "h": 300,
            "mimetype": "image/jpeg",
            "size": 46144,
            "w": 300
          },
          "thumbnail_url": "mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe",
          "w": 480
        },
        "msgtype": "m.video",
        "url": "mxc://example.org/a526eYUSFFxlgbQYZmo442"
      },
      "event_id": "$143273582443PhrSn:example.org",
      "origin_server_ts": 1432735824653,
      "room_id": "!636q39766251:example.com",
      "sender": "@example:example.org",
      "type": "m.room.message",
      "unsigned": {
        "age": 1234,
        "membership": "join"
      }
    }
  ],
  "end": "t47409-4357353_219380_26003_2265",
  "start": "t47429-4392820_219380_26003_2265"
}

GET /_matrix/client/v1/rooms/{roomId}/timestamp_to_event


Added in v1.6

Get the ID of the event closest to the given timestamp, in the direction specified by the dir parameter.

If the server does not have all of the room history and does not have an event suitably close to the requested timestamp, it can use the corresponding federation endpoint to ask other servers for a suitable event.

After calling this endpoint, clients can call /rooms/{roomId}/context/{eventId} to obtain a pagination token to retrieve the events around the returned event.

The event returned by this endpoint could be an event that the client cannot render, and so may need to paginate in order to locate an event that it can display, which may end up being outside of the client’s suitable range. Clients can employ different strategies to display something reasonable to the user. For example, the client could try paginating in one direction for a while, while looking at the timestamps of the events that it is paginating through, and if it exceeds a certain difference from the target timestamp, it can try paginating in the opposite direction. The client could also simply paginate in one direction and inform the user that the closest event found in that direction is outside of the expected range.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string

Required: The ID of the room to search

query parameters
Name Type Description
dir string

Required: The direction in which to search. f for forwards, b for backwards.

One of: [f, b].

ts integer

Required: The timestamp to search from, as given in milliseconds since the Unix epoch.


Responses

Status Description
200 An event was found matching the search parameters.
404 No event was found.
429 This request was rate-limited.

200 response

Name Type Description
event_id string

Required: The ID of the event found

origin_server_ts integer

Required: The event’s timestamp, in milliseconds since the Unix epoch. This makes it easy to do a quick comparison to see if the event_id fetched is too far out of range to be useful for your use case.

{
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653
}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND",
  "error": "Unable to find event from 1432684800000 in forward direction"
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

GET /_matrix/client/v3/rooms/{roomId}/initialSync


This API is deprecated and will be removed from a future release.

Get a copy of the current state and the most recent messages in a room.

This endpoint was deprecated in r0 of this specification. There is no direct replacement; the relevant information is returned by the /sync API. See the migration guide.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string

Required: The room to get the data.


Responses

Status Description
200 The current state of the room
403 You aren’t a member of the room and weren’t previously a member of the room.

200 response

RoomInfo
Name Type Description
account_data [Event]

The private data that this user has attached to this room.

membership string

The user’s membership state in this room.

One of: [invite, join, leave, ban].

messages PaginationChunk

The pagination chunk for this room.

room_id string

Required: The ID of this room.

state [ClientEvent]

If the user is a member of the room this will be the current state of the room as a list of events. If the user has left the room this will be the state of the room when they left it.

visibility string

Whether this room is visible to the /publicRooms API or not."

One of: [private, public].

Event
Name Type Description
content object

Required: The fields in this object will vary depending on the type of event. When interacting with the REST API, this is the HTTP body.

type string

Required: The type of event. This SHOULD be namespaced similar to Java package naming conventions e.g. ‘com.example.subdomain.event.type’

PaginationChunk
Name Type Description
chunk [ClientEvent]

Required: If the user is a member of the room this will be a list of the most recent messages for this room. If the user has left the room this will be the messages that preceded them leaving. This array will consist of at most limit elements.

end string

Required: A token which correlates to the end of chunk. Can be passed to /rooms/<room_id>/messages to retrieve later events.

start string

A token which correlates to the start of chunk. Can be passed to /rooms/<room_id>/messages to retrieve earlier events.

If no earlier events are available, this property may be omitted from the response.

ClientEvent
Name Type Description
content object

Required: The body of this event, as created by the client which sent it.

event_id string

Required: The globally unique identifier for this event.

origin_server_ts integer

Required: Timestamp (in milliseconds since the unix epoch) on originating homeserver when this event was sent.

room_id string

Required: The ID of the room associated with this event.

sender string

Required: Contains the fully-qualified ID of the user who sent this event.

state_key string

Present if, and only if, this event is a state event. The key making this piece of state unique in the room. Note that it is often an empty string.

State keys starting with an @ are reserved for referencing user IDs, such as room members. With the exception of a few events, state events set with a given user’s ID as the state key MUST only be set by that user.

type string

Required: The type of the event.

unsigned UnsignedData

Contains optional extra information about the event.

UnsignedData
Name Type Description
age integer

The time in milliseconds that has elapsed since the event was sent. This field is generated by the local homeserver, and may be incorrect if the local time on at least one of the two servers is out of sync, which can cause the age to either be negative or greater than it actually is.

membership string

The room membership of the user making the request, at the time of the event.

This property is the value of the membership property of the requesting user’s m.room.member state at the point of the event, including any changes caused by the event. If the user had yet to join the room at the time of the event (i.e, they have no m.room.member state), this property is set to leave.

Homeservers SHOULD populate this property wherever practical, but they MAY omit it if necessary (for example, if calculating the value is expensive, servers might choose to only implement it in encrypted rooms). The property is not normally populated in events pushed to application services via the application service transaction API (where there is no clear definition of “requesting user”).

Added in v1.11

prev_content EventContent

The previous content for this event. This field is generated by the local homeserver, and is only returned if the event is a state event, and the client has permission to see the previous content.

Changed in v1.2: Previously, this field was specified at the top level of returned events rather than in unsigned (with the exception of the GET .../notifications endpoint), though in practice no known server implementations honoured this.

redacted_because ClientEvent

The event that redacted this event, if any.

transaction_id string

The client-supplied transaction ID, for example, provided via PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}, if the client being given the event is the same one which sent it.

{
  "account_data": [
    {
      "content": {
        "tags": {
          "work": {
            "order": "1"
          }
        }
      },
      "type": "m.tag"
    }
  ],
  "membership": "join",
  "messages": {
    "chunk": [
      {
        "content": {
          "body": "This is an example text message",
          "format": "org.matrix.custom.html",
          "formatted_body": "<b>This is an example text message</b>",
          "msgtype": "m.text"
        },
        "event_id": "$143273582443PhrSn:example.org",
        "origin_server_ts": 1432735824653,
        "room_id": "!636q39766251:example.com",
        "sender": "@example:example.org",
        "type": "m.room.message",
        "unsigned": {
          "age": 1234,
          "membership": "join"
        }
      },
      {
        "content": {
          "body": "something-important.doc",
          "filename": "something-important.doc",
          "info": {
            "mimetype": "application/msword",
            "size": 46144
          },
          "msgtype": "m.file",
          "url": "mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe"
        },
        "event_id": "$143273582443PhrSn:example.org",
        "origin_server_ts": 1432735824653,
        "room_id": "!636q39766251:example.com",
        "sender": "@example:example.org",
        "type": "m.room.message",
        "unsigned": {
          "age": 1234,
          "membership": "join"
        }
      }
    ],
    "end": "s3456_9_0",
    "start": "t44-3453_9_0"
  },
  "room_id": "!636q39766251:example.com",
  "state": [
    {
      "content": {
        "join_rule": "public"
      },
      "event_id": "$143273582443PhrSn:example.org",
      "origin_server_ts": 1432735824653,
      "room_id": "!636q39766251:example.com",
      "sender": "@example:example.org",
      "state_key": "",
      "type": "m.room.join_rules",
      "unsigned": {
        "age": 1234,
        "membership": "join"
      }
    },
    {
      "content": {
        "avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
        "displayname": "Alice Margatroid",
        "membership": "join",
        "reason": "Looking for support"
      },
      "event_id": "$143273582443PhrSn:example.org",
      "origin_server_ts": 1432735824653,
      "room_id": "!636q39766251:example.com",
      "sender": "@example:example.org",
      "state_key": "@alice:example.org",
      "type": "m.room.member",
      "unsigned": {
        "age": 1234,
        "membership": "join"
      }
    },
    {
      "content": {
        "m.federate": true,
        "predecessor": {
          "event_id": "$something:example.org",
          "room_id": "!oldroom:example.org"
        },
        "room_version": "11"
      },
      "event_id": "$143273582443PhrSn:example.org",
      "origin_server_ts": 1432735824653,
      "room_id": "!636q39766251:example.com",
      "sender": "@example:example.org",
      "state_key": "",
      "type": "m.room.create",
      "unsigned": {
        "age": 1234,
        "membership": "join"
      }
    },
    {
      "content": {
        "ban": 50,
        "events": {
          "m.room.name": 100,
          "m.room.power_levels": 100
        },
        "events_default": 0,
        "invite": 50,
        "kick": 50,
        "notifications": {
          "room": 20
        },
        "redact": 50,
        "state_default": 50,
        "users": {
          "@example:localhost": 100
        },
        "users_default": 0
      },
      "event_id": "$143273582443PhrSn:example.org",
      "origin_server_ts": 1432735824653,
      "room_id": "!636q39766251:example.com",
      "sender": "@example:example.org",
      "state_key": "",
      "type": "m.room.power_levels",
      "unsigned": {
        "age": 1234,
        "membership": "join"
      }
    }
  ],
  "visibility": "private"
}

Sending events to a room

[Added in v1.3]

Servers might need to post-process some events if they relate to another event. The event’s relationship type (rel_type) determines any restrictions which might apply, such as the user only being able to send one event of a given type in relation to another.

PUT /_matrix/client/v3/rooms/{roomId}/state/{eventType}/{stateKey}


State events can be sent using this endpoint. These events will be overwritten if <room id>, <event type> and <state key> all match.

Requests to this endpoint cannot use transaction IDs like other PUT paths because they cannot be differentiated from the state_key. Furthermore, POST is unsupported on state paths.

The body of the request should be the content object of the event; the fields in this object will vary depending on the type of event. See Room Events for the m. event specification.

If the event type being sent is m.room.canonical_alias servers SHOULD ensure that any new aliases being listed in the event are valid per their grammar/syntax and that they point to the room ID where the state event is to be sent. Servers do not validate aliases which are being removed or are already present in the state event.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
eventType string

Required: The type of event to send.

roomId string

Required: The room to set the state in

stateKey string

Required: The state_key for the state to send. Defaults to the empty string. When an empty string, the trailing slash on this endpoint is optional.

Request body

Request body example

{
  "avatar_url": "mxc://localhost/SEsfnsuifSDFSSEF",
  "displayname": "Alice Margatroid",
  "membership": "join"
}

Responses

Status Description
200 An ID for the sent event.
400

The sender’s request is malformed.

Some example error codes include:

  • M_INVALID_PARAM: One or more aliases within the m.room.canonical_alias event have invalid syntax.

  • M_BAD_ALIAS: One or more aliases within the m.room.canonical_alias event do not point to the room ID for which the state event is to be sent to.

403 The sender doesn’t have permission to send the event into the room.

200 response

Name Type Description
event_id string

Required: A unique identifier for the event.

{
  "event_id": "$YUwRidLecu:example.com"
}

400 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_BAD_ALIAS",
  "error": "The alias '#hello:example.org' does not point to this room."
}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_FORBIDDEN",
  "error": "You do not have permission to send the event."
}

Examples

Valid requests look like:

PUT /rooms/!roomid:domain/state/m.example.event
{ "key" : "without a state key" }
PUT /rooms/!roomid:domain/state/m.another.example.event/foo
{ "key" : "with 'foo' as the state key" }

In contrast, these requests are invalid:

POST /rooms/!roomid:domain/state/m.example.event/
{ "key" : "cannot use POST here" }
PUT /rooms/!roomid:domain/state/m.another.example.event/foo/11
{ "key" : "txnIds are not supported" }

Care should be taken to avoid setting the wrong state key:

PUT /rooms/!roomid:domain/state/m.another.example.event/11
{ "key" : "with '11' as the state key, but was probably intended to be a txnId" }

The state_key is often used to store state about individual users, by using the user ID as the state_key value. For example:

PUT /rooms/!roomid:domain/state/m.favorite.animal.event/%40my_user%3Aexample.org
{ "animal" : "cat", "reason": "fluffy" }

In some cases, there may be no need for a state_key, so it can be omitted:

PUT /rooms/!roomid:domain/state/m.room.bgd.color
{ "color": "red", "hex": "#ff0000" }

PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}


This endpoint is used to send a message event to a room. Message events allow access to historical events and pagination, making them suited for “once-off” activity in a room.

The body of the request should be the content object of the event; the fields in this object will vary depending on the type of event. See Room Events for the m. event specification.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
eventType string

Required: The type of event to send.

roomId string

Required: The room to send the event to.

txnId string

Required: The transaction ID for this event. Clients should generate an ID unique across requests with the same access token; it will be used by the server to ensure idempotency of requests.

Request body

Request body example

{
  "body": "hello",
  "msgtype": "m.text"
}

Responses

Status Description
200 An ID for the sent event.
400

The request is invalid. A standard error response will be returned. As well as the normal common error codes, other reasons for rejection include:

200 response

Name Type Description
event_id string

Required: A unique identifier for the event.

{
  "event_id": "$YUwRidLecu:example.com"
}

400 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_UNKNOWN",
  "error": "An unknown error occurred"
}

Redactions

Since events are extensible it is possible for malicious users and/or servers to add keys that are, for example offensive or illegal. Since some events cannot be simply deleted, e.g. membership events, we instead ‘redact’ events. This involves removing all keys from an event that are not required by the protocol. This stripped down event is thereafter returned anytime a client or remote server requests it. Redacting an event cannot be undone, allowing server owners to delete the offending content from the databases. Servers should include a copy of the m.room.redaction event under unsigned as redacted_because when serving the redacted event to clients.

The exact algorithm to apply against an event is defined in the room version specification, as are the criteria homeservers should use when deciding whether to accept a redaction event from a remote homeserver.

When a client receives an m.room.redaction event, it should change the affected event in the same way a server does.

Redacted events can still affect the state of the room. When redacted, state events behave as though their properties were simply not specified, except those protected by the redaction algorithm. For example, a redacted join event will still result in the user being considered joined. Similarly, a redacted topic does not necessarily cause the topic to revert to what it was prior to the event - it causes the topic to be removed from the room.

Events

m.room.redaction


This event is created by the server to describe which event has been redacted, by whom, and optionally why. The event that has been redacted is specified in the redacts event level key. Redacting an event means that all keys not required by the protocol are stripped off, allowing messages to be hidden or allowing admins to remove offensive or illegal content.

Event type: Message event

Content

Name Type Description
reason string

The reason for the redaction, if any.

redacts string

The event ID that was redacted. Required for, and present starting in, room version 11.

Examples

{
  "content": {
    "reason": "Spamming",
    "redacts": "$fukweghifu23:localhost"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "type": "m.room.redaction",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

Client behaviour

PUT /_matrix/client/v3/rooms/{roomId}/redact/{eventId}/{txnId}


Strips all information out of an event which isn’t critical to the integrity of the server-side representation of the room.

This cannot be undone.

Any user with a power level greater than or equal to the m.room.redaction event power level may send redaction events in the room. If the user’s power level greater is also greater than or equal to the redact power level of the room, the user may redact events sent by other users.

Server administrators may redact events sent by users on their server.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
eventId string

Required: The ID of the event to redact

roomId string

Required: The room from which to redact the event.

txnId string

Required: The transaction ID for this event. Clients should generate a unique ID; it will be used by the server to ensure idempotency of requests.

Request body

Name Type Description
reason string

The reason for the event being redacted.

Request body example

{
  "reason": "Indecent material"
}

Responses

Status Description
200 An ID for the redaction event.

200 response

Name Type Description
event_id string

A unique identifier for the event.

{
  "event_id": "$YUwQidLecu:example.com"
}

Forming relationships between events

[Changed in v1.3]

In some cases it is desirable to logically associate one event’s contents with another event’s contents — for example, when replying to a message, editing an event, or simply looking to add context for an event’s purpose.

Events are related to each other in a parent/child structure, where any event can become a parent by simply having a child event point at it. Parent events do not define their children, instead relying on the children to describe their parent.

The relationship between a child and its parent event is described in the child event’s content as m.relates_to (defined below). A child event can point at any other event, including another child event, to build the relationship so long as both events are in the same room, however additional restrictions might be imposed by the type of the relationship (the rel_type).

Child events can point at other child events, forming a chain of events. These chains can naturally take the shape of a tree if two independent children point at a single parent event, for example.

To allow the server to aggregate and find child events for a parent, the m.relates_to key of an event MUST be included in the cleartext portion of the event. It cannot be exclusively recorded in the encrypted payload as the server cannot decrypt the event for processing.

If an encrypted event contains an m.relates_to in its payload, it should be ignored and instead favour the cleartext m.relates_to copy (including when there is no cleartext copy). This is to ensure the client’s behaviour matches the server’s capability to handle relationships.

Relationships which don’t match the schema, or which break the rules of a relationship, are simply ignored. An example might be the parent and child being in different rooms, or the relationship missing properties required by the schema below. Clients handling such invalid relationships should show the events independently of each other, optionally with an error message.

m.relates_to is defined as follows:

m.relates_to


Describes the relationship of an event to its parent. This is contained within the event’s content alongside other fields for the relevant event type.

m.relates_to
Name Type Description
event_id string

Required: The event ID of the event that this event relates to.

rel_type string

Required: The namespaced relationship type. Values must use the Common Namespaced Identifier Grammar.

The relationship type determines how clients should perceive the event, and in what context. Some relationship types are processed server-side for “bundling”, though not all relationships require such behaviour. For example, an m.thread relationship type denotes that the event is part of a “thread” of messages and should be rendered as such.

Examples

{
  "m.relates_to": {
    "event_id": "$an_event",
    "rel_type": "org.example.relationship"
  }
}

Relationship types

This specification describes the following relationship types:

Aggregations of child events

[Added in v1.3]

Some child events can be “aggregated” by the server, depending on their rel_type. This can allow a set of child events to be summarised to the client without the client needing the child events themselves.

An example of this might be that a rel_type requires an extra key field which, when appropriately specified, would mean that the client receives a total count for the number of times that key was used by child events.

The actual aggregation format depends on the rel_type.

When an event is served to the client through the APIs listed below, a m.relations property is included under unsigned if the event has child events which can be aggregated and point at it. The m.relations property is an object keyed by rel_type and value being the type-specific aggregated format for that rel_type. This m.relations property is known as a “bundled aggregation”.

For example (unimportant fields not included):

{
  "event_id": "$my_event",
  "unsigned": {
    "m.relations": {
      "org.example.possible_annotations": [
        {
          "key": "👍",
          "origin_server_ts": 1562763768320,
          "count": 3
        },
        {
          "key": "👎",
          "origin_server_ts": 1562763768320,
          "count": 1
        }
      ],
      "org.example.possible_thread": {
        "current_server_participated": true,
        "count": 7,
        "latest_event": {
          "event_id": "$another_event",
          "content": {
            "body": "Hello world"
          }
        }
      }
    }
  }
}

Note how the org.example.possible_annotations aggregation is an array, while in the org.example.possible_thread aggregation where the server is summarising the state of the relationship in a single object. Both are valid ways to aggregate: the format of an aggregation depends on the rel_type.

State events do not currently receive bundled aggregations. This is not necessarily a deliberate design decision, and MSCs which aim to fix this are welcome.

The endpoints where the server should include bundled aggregations are:

The server is not required to return bundled aggregations on deprecated endpoints such as /initialSync.

While this functionality allows the client to see what was known to the server at the time of handling, the client should continue to aggregate locally if it is aware of the relationship type’s behaviour. For example, a client might internally increment a count in a parent event’s aggregation data if it saw a new child event which referenced that parent.

The aggregation provided by the server only includes child events which were known at the time the client would receive the aggregation. For example, in a single /sync response with the parent and multiple child events the child events would have already been included on the parent’s m.relations field. Events received in future syncs would need to be aggregated manually by the client.

Events from ignored users do not appear in the aggregation from the server, however clients might still have events from ignored users cached. Like with normal events, clients will need to de-aggregate child events sent by ignored users to avoid them being considered in counts. Servers must additionally ensure they do not consider child events from ignored users when preparing an aggregation for the client.

When a parent event is redacted, the child events which pointed to that parent remain, however when a child event is redacted then the relationship is broken. Therefore, the server needs to de-aggregate or disassociate the event once the relationship is lost. Clients with local aggregation or which handle redactions locally should do the same.

It is suggested that clients perform local echo on aggregations — for instance, aggregating a new child event into a parent event optimistically until the server returns a failure or the client gives up on sending the event, at which point the event should be de-aggregated and an error or similar shown. The client should be cautious to not aggregate an event twice if it has already optimistically aggregated the event. Clients are encouraged to take this a step further to additionally track child events which target unsent/pending events, likely using the transaction ID as a temporary event ID until a proper event ID is known.

Due to history visibility restrictions, child events might not be visible to the user if they are in a section of history the user cannot see. This means any aggregations which would normally include those events will be lacking them and the client will not be able to locally aggregate the events either — relating events of importance (such as votes) should take into consideration history visibility.

Additionally, if the server is missing portions of the room history then it may not be able to accurately aggregate the events.

Relationships API

[Added in v1.3]

To retrieve the child events for a parent from the server, the client can call the following endpoint.

This endpoint is particularly useful if the client has lost context on the aggregation for a parent event and needs to rebuild/verify it.

When using the recurse parameter, note that there is no way for a client to control how far the server recurses. If the client decides that the server’s recursion level is insufficient, it could, for example, perform the recursion itself, or disable whatever feature requires more recursion.

Filters specified via event_type or rel_type will be applied to all events returned, whether direct or indirect relations. Events that would match the filter, but whose only relation to the original given event is through a non-matching intermediate event, will not be included. This means that supplying a rel_type parameter of m.thread is not appropriate for fetching all events in a thread since relations to the threaded events would be filtered out. For this purpose, clients should omit the rel_type parameter and perform any necessary filtering on the client side.

Because replies do not use rel_type, they will not be accessible via this API.

GET /_matrix/client/v1/rooms/{roomId}/relations/{eventId}


Retrieve all of the child events for a given parent event.

Note that when paginating the from token should be “after” the to token in terms of topological ordering, because it is only possible to paginate “backwards” through events, starting at from.

For example, passing a from token from page 2 of the results, and a to token from page 1, would return the empty set. The caller can use a from token from page 1 and a to token from page 2 to paginate over the same range, however.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
eventId string

Required: The ID of the parent event whose child events are to be returned.

roomId string

Required: The ID of the room containing the parent event.

query parameters
Name Type Description
dir string

Optional (default b) direction to return events from. If this is set to f, events will be returned in chronological order starting at from. If it is set to b, events will be returned in reverse chronological order, again starting at from.

One of: [b, f].

Added in v1.4

from string

The pagination token to start returning results from. If not supplied, results start at the most recent topological event known to the server.

Can be a next_batch or prev_batch token from a previous call, or a returned start token from /messages, or a next_batch token from /sync.

limit integer

The maximum number of results to return in a single chunk. The server can and should apply a maximum value to this parameter to avoid large responses.

Similarly, the server should apply a default value when not supplied.

recurse boolean

Whether to additionally include events which only relate indirectly to the given event, i.e. events related to the given event via two or more direct relationships.

If set to false, only events which have a direct relation with the given event will be included.

If set to true, events which have an indirect relation with the given event will be included additionally up to a certain depth level. Homeservers SHOULD traverse at least 3 levels of relationships. Implementations MAY perform more but MUST be careful to not infinitely recurse.

The default value is false.

Added in v1.10

to string

The pagination token to stop returning results at. If not supplied, results continue up to limit or until there are no more events.

Like from, this can be a previous token from a prior call to this endpoint or from /messages or /sync.


Responses

Status Description
200 The paginated child events which point to the parent. If no events are pointing to the parent or the pagination yields no results, an empty chunk is returned.
404 The parent event was not found or the user does not have permission to read this event (it might be contained in history that is not accessible to the user).

200 response

Name Type Description
chunk [ClientEvent]

Required: The child events of the requested event, ordered topologically most-recent first.

next_batch string

An opaque string representing a pagination token. The absence of this token means there are no more results to fetch and the client should stop paginating.

prev_batch string

An opaque string representing a pagination token. The absence of this token means this is the start of the result set, i.e. this is the first batch/page.

recursion_depth integer

If the recurse parameter was supplied by the client, this response field is mandatory and gives the actual depth to which the server recursed. If the client did not specify the recurse parameter, this field must be absent.

ClientEvent
Name Type Description
content object

Required: The body of this event, as created by the client which sent it.

event_id string

Required: The globally unique identifier for this event.

origin_server_ts integer

Required: Timestamp (in milliseconds since the unix epoch) on originating homeserver when this event was sent.

room_id string

Required: The ID of the room associated with this event.

sender string

Required: Contains the fully-qualified ID of the user who sent this event.

state_key string

Present if, and only if, this event is a state event. The key making this piece of state unique in the room. Note that it is often an empty string.

State keys starting with an @ are reserved for referencing user IDs, such as room members. With the exception of a few events, state events set with a given user’s ID as the state key MUST only be set by that user.

type string

Required: The type of the event.

unsigned UnsignedData

Contains optional extra information about the event.

UnsignedData
Name Type Description
age integer

The time in milliseconds that has elapsed since the event was sent. This field is generated by the local homeserver, and may be incorrect if the local time on at least one of the two servers is out of sync, which can cause the age to either be negative or greater than it actually is.

membership string

The room membership of the user making the request, at the time of the event.

This property is the value of the membership property of the requesting user’s m.room.member state at the point of the event, including any changes caused by the event. If the user had yet to join the room at the time of the event (i.e, they have no m.room.member state), this property is set to leave.

Homeservers SHOULD populate this property wherever practical, but they MAY omit it if necessary (for example, if calculating the value is expensive, servers might choose to only implement it in encrypted rooms). The property is not normally populated in events pushed to application services via the application service transaction API (where there is no clear definition of “requesting user”).

Added in v1.11

prev_content EventContent

The previous content for this event. This field is generated by the local homeserver, and is only returned if the event is a state event, and the client has permission to see the previous content.

Changed in v1.2: Previously, this field was specified at the top level of returned events rather than in unsigned (with the exception of the GET .../notifications endpoint), though in practice no known server implementations honoured this.

redacted_because ClientEvent

The event that redacted this event, if any.

transaction_id string

The client-supplied transaction ID, for example, provided via PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}, if the client being given the event is the same one which sent it.

{
  "chunk": [
    {
      "content": {
        "m.relates_to": {
          "event_id": "$asfDuShaf7Gafaw",
          "rel_type": "org.example.my_relation"
        }
      },
      "event_id": "$143273582443PhrSn:example.org",
      "origin_server_ts": 1432735824653,
      "room_id": "!636q39766251:matrix.org",
      "sender": "@example:example.org",
      "type": "m.room.message",
      "unsigned": {
        "age": 1234,
        "membership": "join"
      }
    }
  ],
  "next_batch": "page2_token",
  "prev_batch": "page1_token"
}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND",
  "error": "Event not found."
}

GET /_matrix/client/v1/rooms/{roomId}/relations/{eventId}/{relType}


Retrieve all of the child events for a given parent event which relate to the parent using the given relType.

Note that when paginating the from token should be “after” the to token in terms of topological ordering, because it is only possible to paginate “backwards” through events, starting at from.

For example, passing a from token from page 2 of the results, and a to token from page 1, would return the empty set. The caller can use a from token from page 1 and a to token from page 2 to paginate over the same range, however.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
eventId string

Required: The ID of the parent event whose child events are to be returned.

relType string

Required: The relationship type to search for.

roomId string

Required: The ID of the room containing the parent event.

query parameters
Name Type Description
dir string

Optional (default b) direction to return events from. If this is set to f, events will be returned in chronological order starting at from. If it is set to b, events will be returned in reverse chronological order, again starting at from.

One of: [b, f].

Added in v1.4

from string

The pagination token to start returning results from. If not supplied, results start at the most recent topological event known to the server.

Can be a next_batch or prev_batch token from a previous call, or a returned start token from /messages, or a next_batch token from /sync.

limit integer

The maximum number of results to return in a single chunk. The server can and should apply a maximum value to this parameter to avoid large responses.

Similarly, the server should apply a default value when not supplied.

recurse boolean

Whether to additionally include events which only relate indirectly to the given event, i.e. events related to the given event via two or more direct relationships.

If set to false, only events which have a direct relation with the given event will be included.

If set to true, events which have an indirect relation with the given event will be included additionally up to a certain depth level. Homeservers SHOULD traverse at least 3 levels of relationships. Implementations MAY perform more but MUST be careful to not infinitely recurse.

The default value is false.

Added in v1.10

to string

The pagination token to stop returning results at. If not supplied, results continue up to limit or until there are no more events.

Like from, this can be a previous token from a prior call to this endpoint or from /messages or /sync.


Responses

Status Description
200 The paginated child events which point to the parent. If no events are pointing to the parent or the pagination yields no results, an empty chunk is returned.
404 The parent event was not found or the user does not have permission to read this event (it might be contained in history that is not accessible to the user).

200 response

Name Type Description
chunk [ClientEvent]

Required: The child events of the requested event, ordered topologically most-recent first. The events returned will match the relType supplied in the URL.

next_batch string

An opaque string representing a pagination token. The absence of this token means there are no more results to fetch and the client should stop paginating.

prev_batch string

An opaque string representing a pagination token. The absence of this token means this is the start of the result set, i.e. this is the first batch/page.

recursion_depth integer

If the recurse parameter was supplied by the client, this response field is mandatory and gives the actual depth to which the server recursed. If the client did not specify the recurse parameter, this field must be absent.

ClientEvent
Name Type Description
content object

Required: The body of this event, as created by the client which sent it.

event_id string

Required: The globally unique identifier for this event.

origin_server_ts integer

Required: Timestamp (in milliseconds since the unix epoch) on originating homeserver when this event was sent.

room_id string

Required: The ID of the room associated with this event.

sender string

Required: Contains the fully-qualified ID of the user who sent this event.

state_key string

Present if, and only if, this event is a state event. The key making this piece of state unique in the room. Note that it is often an empty string.

State keys starting with an @ are reserved for referencing user IDs, such as room members. With the exception of a few events, state events set with a given user’s ID as the state key MUST only be set by that user.

type string

Required: The type of the event.

unsigned UnsignedData

Contains optional extra information about the event.

UnsignedData
Name Type Description
age integer

The time in milliseconds that has elapsed since the event was sent. This field is generated by the local homeserver, and may be incorrect if the local time on at least one of the two servers is out of sync, which can cause the age to either be negative or greater than it actually is.

membership string

The room membership of the user making the request, at the time of the event.

This property is the value of the membership property of the requesting user’s m.room.member state at the point of the event, including any changes caused by the event. If the user had yet to join the room at the time of the event (i.e, they have no m.room.member state), this property is set to leave.

Homeservers SHOULD populate this property wherever practical, but they MAY omit it if necessary (for example, if calculating the value is expensive, servers might choose to only implement it in encrypted rooms). The property is not normally populated in events pushed to application services via the application service transaction API (where there is no clear definition of “requesting user”).

Added in v1.11

prev_content EventContent

The previous content for this event. This field is generated by the local homeserver, and is only returned if the event is a state event, and the client has permission to see the previous content.

Changed in v1.2: Previously, this field was specified at the top level of returned events rather than in unsigned (with the exception of the GET .../notifications endpoint), though in practice no known server implementations honoured this.

redacted_because ClientEvent

The event that redacted this event, if any.

transaction_id string

The client-supplied transaction ID, for example, provided via PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}, if the client being given the event is the same one which sent it.

{
  "chunk": [
    {
      "content": {
        "m.relates_to": {
          "event_id": "$asfDuShaf7Gafaw",
          "rel_type": "org.example.my_relation"
        }
      },
      "event_id": "$143273582443PhrSn:example.org",
      "origin_server_ts": 1432735824653,
      "room_id": "!636q39766251:matrix.org",
      "sender": "@example:example.org",
      "type": "m.room.message",
      "unsigned": {
        "age": 1234,
        "membership": "join"
      }
    }
  ],
  "next_batch": "page2_token",
  "prev_batch": "page1_token"
}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND",
  "error": "Event not found."
}

GET /_matrix/client/v1/rooms/{roomId}/relations/{eventId}/{relType}/{eventType}


Retrieve all of the child events for a given parent event which relate to the parent using the given relType and have the given eventType.

Note that when paginating the from token should be “after” the to token in terms of topological ordering, because it is only possible to paginate “backwards” through events, starting at from.

For example, passing a from token from page 2 of the results, and a to token from page 1, would return the empty set. The caller can use a from token from page 1 and a to token from page 2 to paginate over the same range, however.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
eventId string

Required: The ID of the parent event whose child events are to be returned.

eventType string

Required: The event type of child events to search for.

Note that in encrypted rooms this will typically always be m.room.encrypted regardless of the event type contained within the encrypted payload.

relType string

Required: The relationship type to search for.

roomId string

Required: The ID of the room containing the parent event.

query parameters
Name Type Description
dir string

Optional (default b) direction to return events from. If this is set to f, events will be returned in chronological order starting at from. If it is set to b, events will be returned in reverse chronological order, again starting at from.

One of: [b, f].

Added in v1.4

from string

The pagination token to start returning results from. If not supplied, results start at the most recent topological event known to the server.

Can be a next_batch or prev_batch token from a previous call, or a returned start token from /messages, or a next_batch token from /sync.

limit integer

The maximum number of results to return in a single chunk. The server can and should apply a maximum value to this parameter to avoid large responses.

Similarly, the server should apply a default value when not supplied.

recurse boolean

Whether to additionally include events which only relate indirectly to the given event, i.e. events related to the given event via two or more direct relationships.

If set to false, only events which have a direct relation with the given event will be included.

If set to true, events which have an indirect relation with the given event will be included additionally up to a certain depth level. Homeservers SHOULD traverse at least 3 levels of relationships. Implementations MAY perform more but MUST be careful to not infinitely recurse.

The default value is false.

Added in v1.10

to string

The pagination token to stop returning results at. If not supplied, results continue up to limit or until there are no more events.

Like from, this can be a previous token from a prior call to this endpoint or from /messages or /sync.


Responses

Status Description
200 The paginated child events which point to the parent. If no events are pointing to the parent or the pagination yields no results, an empty chunk is returned.
404 The parent event was not found or the user does not have permission to read this event (it might be contained in history that is not accessible to the user).

200 response

Name Type Description
chunk [ClientEvent]

Required: The child events of the requested event, ordered topologically most-recent first. The events returned will match the relType and eventType supplied in the URL.

next_batch string

An opaque string representing a pagination token. The absence of this token means there are no more results to fetch and the client should stop paginating.

prev_batch string

An opaque string representing a pagination token. The absence of this token means this is the start of the result set, i.e. this is the first batch/page.

recursion_depth integer

If the recurse parameter was supplied by the client, this response field is mandatory and gives the actual depth to which the server recursed. If the client did not specify the recurse parameter, this field must be absent.

ClientEvent
Name Type Description
content object

Required: The body of this event, as created by the client which sent it.

event_id string

Required: The globally unique identifier for this event.

origin_server_ts integer

Required: Timestamp (in milliseconds since the unix epoch) on originating homeserver when this event was sent.

room_id string

Required: The ID of the room associated with this event.

sender string

Required: Contains the fully-qualified ID of the user who sent this event.

state_key string

Present if, and only if, this event is a state event. The key making this piece of state unique in the room. Note that it is often an empty string.

State keys starting with an @ are reserved for referencing user IDs, such as room members. With the exception of a few events, state events set with a given user’s ID as the state key MUST only be set by that user.

type string

Required: The type of the event.

unsigned UnsignedData

Contains optional extra information about the event.

UnsignedData
Name Type Description
age integer

The time in milliseconds that has elapsed since the event was sent. This field is generated by the local homeserver, and may be incorrect if the local time on at least one of the two servers is out of sync, which can cause the age to either be negative or greater than it actually is.

membership string

The room membership of the user making the request, at the time of the event.

This property is the value of the membership property of the requesting user’s m.room.member state at the point of the event, including any changes caused by the event. If the user had yet to join the room at the time of the event (i.e, they have no m.room.member state), this property is set to leave.

Homeservers SHOULD populate this property wherever practical, but they MAY omit it if necessary (for example, if calculating the value is expensive, servers might choose to only implement it in encrypted rooms). The property is not normally populated in events pushed to application services via the application service transaction API (where there is no clear definition of “requesting user”).

Added in v1.11

prev_content EventContent

The previous content for this event. This field is generated by the local homeserver, and is only returned if the event is a state event, and the client has permission to see the previous content.

Changed in v1.2: Previously, this field was specified at the top level of returned events rather than in unsigned (with the exception of the GET .../notifications endpoint), though in practice no known server implementations honoured this.

redacted_because ClientEvent

The event that redacted this event, if any.

transaction_id string

The client-supplied transaction ID, for example, provided via PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}, if the client being given the event is the same one which sent it.

{
  "chunk": [
    {
      "content": {
        "m.relates_to": {
          "event_id": "$asfDuShaf7Gafaw",
          "rel_type": "org.example.my_relation"
        }
      },
      "event_id": "$143273582443PhrSn:example.org",
      "origin_server_ts": 1432735824653,
      "room_id": "!636q39766251:matrix.org",
      "sender": "@example:example.org",
      "type": "m.room.message",
      "unsigned": {
        "age": 1234,
        "membership": "join"
      }
    }
  ],
  "next_batch": "page2_token",
  "prev_batch": "page1_token"
}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND",
  "error": "Event not found."
}

Rooms

Types

[Added in v1.2]

Optionally, rooms can have types to denote their intended function. A room without a type does not necessarily mean it has a specific default function, though commonly these rooms will be for conversational purposes.

Room types are best applied when a client might need to differentiate between two different rooms, such as conversation-holding and data-holding. If a room has a type, it is specified in the type key of an m.room.create event. To specify a room’s type, provide it as part of creation_content on the create room request.

In this specification the following room types are specified:

Unspecified room types are permitted through the use of Namespaced Identifiers.

Creation

The homeserver will create an m.room.create event when a room is created, which serves as the root of the event graph for this room. This event also has a creator key which contains the user ID of the room creator. It will also generate several other events in order to manage permissions in this room. This includes:

  • m.room.power_levels : Sets the power levels of users and required power levels for various actions within the room such as sending events.

  • m.room.join_rules : Whether the room is “invite-only” or not.

See Room Events for more information on these events. To create a room, a client has to use the following API.

POST /_matrix/client/v3/createRoom


Create a new room with various configuration options.

The server MUST apply the normal state resolution rules when creating the new room, including checking power levels for each event. It MUST apply the events implied by the request in the following order:

  1. The m.room.create event itself. Must be the first event in the room.

  2. An m.room.member event for the creator to join the room. This is needed so the remaining events can be sent.

  3. A default m.room.power_levels event, giving the room creator (and not other members) permission to send state events. Overridden by the power_level_content_override parameter.

  4. An m.room.canonical_alias event if room_alias_name is given.

  5. Events set by the preset. Currently these are the m.room.join_rules, m.room.history_visibility, and m.room.guest_access state events.

  6. Events listed in initial_state, in the order that they are listed.

  7. Events implied by name and topic (m.room.name and m.room.topic state events).

  8. Invite events implied by invite and invite_3pid (m.room.member with membership: invite and m.room.third_party_invite).

The available presets do the following with respect to room state:

Preset join_rules history_visibility guest_access Other
private_chat invite shared can_join
trusted_private_chat invite shared can_join All invitees are given the same power level as the room creator.
public_chat public shared forbidden

The server will create a m.room.create event in the room with the requesting user as the creator, alongside other keys provided in the creation_content.

Rate-limited: No
Requires authentication: Yes

Request

Request body

Name Type Description
creation_content CreationContent

Extra keys, such as m.federate, to be added to the content of the m.room.create event. The server will overwrite the following keys: creator, room_version. Future versions of the specification may allow the server to overwrite other keys.

initial_state [StateEvent]

A list of state events to set in the new room. This allows the user to override the default state events set in the new room. The expected format of the state events are an object with type, state_key and content keys set.

Takes precedence over events set by preset, but gets overridden by name and topic keys.

invite [string]

A list of user IDs to invite to the room. This will tell the server to invite everyone in the list to the newly created room.

invite_3pid [Invite3pid]

A list of objects representing third-party IDs to invite into the room.

is_direct boolean

This flag makes the server set the is_direct flag on the m.room.member events sent to the users in invite and invite_3pid. See Direct Messaging for more information.

name string

If this is included, an m.room.name event will be sent into the room to indicate the name of the room. See Room Events for more information on m.room.name.

power_level_content_override Power Level Event Content

The power level content to override in the default power level event. This object is applied on top of the generated m.room.power_levels event content prior to it being sent to the room. Defaults to overriding nothing.

preset string

Convenience parameter for setting various default state events based on a preset.

If unspecified, the server should use the visibility to determine which preset to use. A visibility of public equates to a preset of public_chat and private visibility equates to a preset of private_chat.

One of: [private_chat, public_chat, trusted_private_chat].

room_alias_name string

The desired room alias local part. If this is included, a room alias will be created and mapped to the newly created room. The alias will belong on the same homeserver which created the room. For example, if this was set to “foo” and sent to the homeserver “example.com” the complete room alias would be #foo:example.com.

The complete room alias will become the canonical alias for the room and an m.room.canonical_alias event will be sent into the room.

room_version string

The room version to set for the room. If not provided, the homeserver is to use its configured default. If provided, the homeserver will return a 400 error with the errcode M_UNSUPPORTED_ROOM_VERSION if it does not support the room version.

topic string

If this is included, an m.room.topic event will be sent into the room to indicate the topic for the room. See Room Events for more information on m.room.topic.

visibility string

A public visibility indicates that the room will be shown in the published room list. A private visibility will hide the room from the published room list. Rooms default to private visibility if this key is not included. NB: This should not be confused with join_rules which also uses the word public.

One of: [public, private].

StateEvent
Name Type Description
content object

Required: The content of the event.

state_key string

The state_key of the state event. Defaults to an empty string.

type string

Required: The type of event to send.

Invite3pid
Name Type Description
address string

Required: The invitee’s third-party identifier.

id_access_token string

Required: An access token previously registered with the identity server. Servers can treat this as optional to distinguish between r0.5-compatible clients and this specification version.

id_server string

Required: The hostname+port of the identity server which should be used for third-party identifier lookups.

medium string

Required: The kind of address being passed in the address field, for example email (see the list of recognised values).

Request body example

{
  "creation_content": {
    "m.federate": false
  },
  "name": "The Grand Duke Pub",
  "preset": "public_chat",
  "room_alias_name": "thepub",
  "topic": "All about happy hour"
}

Responses

Status Description
200 Information about the newly created room.
400

The request is invalid. A meaningful errcode and description error text will be returned. Example reasons for rejection include:

  • The request body is malformed (errcode set to M_BAD_JSON or M_NOT_JSON).

  • The room alias specified is already taken (errcode set to M_ROOM_IN_USE).

  • The initial state implied by the parameters to the request is invalid: for example, the user’s power_level is set below that necessary to set the room name (errcode set to M_INVALID_ROOM_STATE).

  • The homeserver doesn’t support the requested room version, or one or more users being invited to the new room are residents of a homeserver which does not support the requested room version. The errcode will be M_UNSUPPORTED_ROOM_VERSION in these cases.

200 response

Name Type Description
room_id string

Required: The created room’s ID.

{
  "room_id": "!sefiuhWgwghwWgh:example.com"
}

400 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_UNKNOWN",
  "error": "An unknown error occurred"
}

Room aliases

Servers may host aliases for rooms with human-friendly names. Aliases take the form #friendlyname:server.name.

As room aliases are scoped to a particular homeserver domain name, it is likely that a homeserver will reject attempts to maintain aliases on other domain names. This specification does not provide a way for homeservers to send update requests to other servers. However, homeservers MUST handle GET requests to resolve aliases on other servers; they should do this using the federation API if necessary.

Rooms do not store a list of all aliases present on a room, though members of the room with relevant permissions may publish preferred aliases through the m.room.canonical_alias state event. The aliases in the state event should point to the room ID they are published within, however room aliases can and do drift to other room IDs over time. Clients SHOULD NOT treat the aliases as accurate. They SHOULD be checked before they are used or shared with another user. If a room appears to have a room alias of #alias:example.com, this SHOULD be checked to make sure that the room’s ID matches the room_id returned from the request.

GET /_matrix/client/v3/directory/room/{roomAlias}


Requests that the server resolve a room alias to a room ID.

The server will use the federation API to resolve the alias if the domain part of the alias does not correspond to the server’s own domain.

Rate-limited: No
Requires authentication: No

Request

Request parameters

path parameters
Name Type Description
roomAlias string

Required: The room alias. Its format is defined in the appendices.


Responses

Status Description
200 The room ID and other information for this alias.
400 The given roomAlias is not a valid room alias.
404 There is no mapped room ID for this room alias.

200 response

Name Type Description
room_id string

The room ID for this room alias.

servers [string]

A list of servers that are aware of this room alias.

{
  "room_id": "!abnjk1jdasj98:capuchins.com",
  "servers": [
    "capuchins.com",
    "matrix.org",
    "another.com"
  ]
}

400 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_INVALID_PARAM",
  "error": "Room alias invalid"
}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND",
  "error": "Room alias #monkeys:matrix.org not found."
}

PUT /_matrix/client/v3/directory/room/{roomAlias}


Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomAlias string

Required: The room alias to set. Its format is defined in the appendices.

Request body

Name Type Description
room_id string

Required: The room ID to set.

Request body example

{
  "room_id": "!abnjk1jdasj98:capuchins.com"
}

Responses

Status Description
200 The mapping was created.
400 The given roomAlias is not a valid room alias.
409 A room alias with that name already exists.

200 response

{}

400 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_INVALID_PARAM",
  "error": "Room alias invalid"
}

409 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_UNKNOWN",
  "error": "Room alias #monkeys:matrix.org already exists."
}

DELETE /_matrix/client/v3/directory/room/{roomAlias}


Remove a mapping of room alias to room ID.

Servers may choose to implement additional access control checks here, for instance that room aliases can only be deleted by their creator or a server administrator.

Note: Servers may choose to update the alt_aliases for the m.room.canonical_alias state event in the room when an alias is removed. Servers which choose to update the canonical alias event are recommended to, in addition to their other relevant permission checks, delete the alias and return a successful response even if the user does not have permission to update the m.room.canonical_alias event.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomAlias string

Required: The room alias to remove. Its format is defined in the appendices.


Responses

Status Description
200 The mapping was deleted.
404 There is no mapped room ID for this room alias.

200 response

{}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND",
  "error": "Room alias #monkeys:example.org not found."
}

GET /_matrix/client/v3/rooms/{roomId}/aliases


Get a list of aliases maintained by the local server for the given room.

This endpoint can be called by users who are in the room (external users receive an M_FORBIDDEN error response). If the room’s m.room.history_visibility maps to world_readable, any user can call this endpoint.

Servers may choose to implement additional access control checks here, such as allowing server administrators to view aliases regardless of membership.

Note: Clients are recommended not to display this list of aliases prominently as they are not curated, unlike those listed in the m.room.canonical_alias state event.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string

Required: The room ID to find local aliases of.


Responses

Status Description
200 The list of local aliases for the room.
400 The given roomAlias is not a valid room alias.
403 The user is not permitted to retrieve the list of local aliases for the room.
429 This request was rate-limited.

200 response

Name Type Description
aliases [string]

Required: The server’s local aliases on the room. Can be empty.

{
  "aliases": [
    "#somewhere:example.com",
    "#another:example.com",
    "#hat_trick:example.com"
  ]
}

400 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_INVALID_PARAM",
  "error": "Room alias invalid"
}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_FORBIDDEN",
  "error": "You are not a member of the room."
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

Permissions

This section is a work in progress.

Permissions for rooms are done via the concept of power levels - to do any action in a room a user must have a suitable power level. Power levels are stored as state events in a given room. The power levels required for operations and the power levels for users are defined in m.room.power_levels, where both a default and specific users’ power levels can be set. By default all users have a power level of 0, other than the room creator whose power level defaults to 100. Users can grant other users increased power levels up to their own power level. For example, user A with a power level of 50 could increase the power level of user B to a maximum of level 50. Power levels for users are tracked per-room even if the user is not present in the room. The keys contained in m.room.power_levels determine the levels required for certain operations such as kicking, banning and sending state events. See m.room.power_levels for more information.

Clients may wish to assign names to particular power levels. A suggested mapping is as follows: - 0 User - 50 Moderator - 100 Admin

Room membership

Users need to be a member of a room in order to send and receive events in that room. There are several states in which a user may be, in relation to a room:

  • Unrelated (the user cannot send or receive events in the room)
  • Knocking (the user has requested to participate in the room, but has not yet been allowed to)
  • Invited (the user has been invited to participate in the room, but is not yet participating)
  • Joined (the user can send and receive events in the room)
  • Banned (the user is not allowed to join the room)

There are a few notable exceptions which allow non-joined members of the room to send events in the room:

  • Users wishing to reject an invite would send m.room.member events with content.membership of leave. They must have been invited first.

  • If the room allows, users can send m.room.member events with content.membership of knock to knock on the room. This is a request for an invite by the user.

  • To retract a previous knock, a user would send a leave event similar to rejecting an invite.

Some rooms require that users be invited to it before they can join; others allow anyone to join. Whether a given room is an “invite-only” room is determined by the room config key m.room.join_rules. It can have one of the following values:

public This room is free for anyone to join without an invite.

invite This room can only be joined if you were invited.

knock This room can only be joined if you were invited, and allows anyone to request an invite to the room. Note that this join rule is only available in room versions which support knocking.

[Added in v1.2] restricted This room can be joined if you were invited or if you are a member of another room listed in the join rules. If the server cannot verify membership for any of the listed rooms then you can only join with an invite. Note that this rule is only expected to work in room versions which support it.

[Added in v1.3] knock_restricted This room can be joined as though it was restricted or knock. If you interact with the room using knocking, the knock rule takes effect whereas trying to join the room without an invite applies the restricted join rule. Note that this rule is only expected to work in room versions which support it.

The allowable state transitions of membership are:

Diagram presenting the possible membership state transitions

GET /_matrix/client/v3/joined_rooms


This API returns a list of the user’s current rooms.

Rate-limited: No
Requires authentication: Yes

Request

No request parameters or request body.


Responses

Status Description
200 A list of the rooms the user is in.

200 response

Name Type Description
joined_rooms [string]

Required: The ID of each room in which the user has joined membership.

{
  "joined_rooms": [
    "!foo:example.com"
  ]
}

Joining rooms

POST /_matrix/client/v3/rooms/{roomId}/invite


Note that there are two forms of this API, which are documented separately. This version of the API requires that the inviter knows the Matrix identifier of the invitee. The other is documented in the third-party invites section.

This API invites a user to participate in a particular room. They do not start participating in the room until they actually join the room.

Only users currently in a particular room can invite other users to join that room.

If the user was invited to the room, the homeserver will append a m.room.member event to the room.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string

Required: The room identifier (not alias) to which to invite the user.

Request body

Name Type Description
reason string

Optional reason to be included as the reason on the subsequent membership event.

Added in v1.1

user_id string

Required: The fully qualified user ID of the invitee.

Request body example

{
  "reason": "Welcome to the team!",
  "user_id": "@cheeky_monkey:matrix.org"
}

Responses

Status Description
200 The user has been invited to join the room, or was already invited to the room.
400

The request is invalid. A meaningful errcode and description error text will be returned. Example reasons for rejection include:

  • The request body is malformed (errcode set to M_BAD_JSON or M_NOT_JSON).

  • One or more users being invited to the room are residents of a homeserver which does not support the requested room version. The errcode will be M_UNSUPPORTED_ROOM_VERSION in these cases.

403

You do not have permission to invite the user to the room. A meaningful errcode and description error text will be returned. Example reasons for rejections are:

  • The invitee has been banned from the room.
  • The invitee is already a member of the room.
  • The inviter is not currently in the room.
  • The inviter’s power level is insufficient to invite users to the room.
429 This request was rate-limited.

200 response

{}

400 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_UNKNOWN",
  "error": "An unknown error occurred"
}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_FORBIDDEN",
  "error": "@cheeky_monkey:matrix.org is banned from the room"
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

POST /_matrix/client/v3/join/{roomIdOrAlias}


Note that this API takes either a room ID or alias, unlike /rooms/{roomId}/join.

This API starts a user’s participation in a particular room, if that user is allowed to participate in that room. After this call, the client is allowed to see all current state events in the room, and all subsequent events associated with the room until the user leaves the room.

After a user has joined a room, the room will appear as an entry in the response of the /initialSync and /sync APIs.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomIdOrAlias string

Required: The room identifier or alias to join.

query parameters
Name Type Description
server_name [string]

The servers to attempt to join the room through. One of the servers must be participating in the room.

Changed in v1.12: This parameter has been deprecated in favour of via and will be removed in a future version of the spec.

Clients SHOULD use via when the homeserver they’re talking to supports it. To do this, they MAY either detect server support through the supported spec versions in /_matrix/client/versions or always include both parameters with identical values.

Homeservers MUST ignore all server_name parameters if any via parameters are supplied.

via [string]

The servers to attempt to join the room through. One of the servers must be participating in the room.

Added in v1.12

Request body

Name Type Description
reason string

Optional reason to be included as the reason on the subsequent membership event.

Added in v1.1

third_party_signed Third-party Signed

If a third_party_signed was supplied, the homeserver must verify that it matches a pending m.room.third_party_invite event in the room, and perform key validity checking if required by the event.

Third-party Signed
Name Type Description
mxid string

Required: The Matrix ID of the invitee.

sender string

Required: The Matrix ID of the user who issued the invite.

signatures {string: {string: string}}

Required: A signatures object containing a signature of the entire signed object.

token string

Required: The state key of the m.third_party_invite event.

Request body example

{
  "reason": "Looking for support",
  "third_party_signed": {
    "mxid": "@bob:example.org",
    "sender": "@alice:example.org",
    "signatures": {
      "example.org": {
        "ed25519:0": "some9signature"
      }
    },
    "token": "random8nonce"
  }
}

Responses

Status Description
200

The room has been joined.

The joined room ID must be returned in the room_id field.

403

You do not have permission to join the room. A meaningful errcode and description error text will be returned. Example reasons for rejection are:

  • The room is invite-only and the user was not invited.
  • The user has been banned from the room.
  • The room is restricted and the user failed to satisfy any of the conditions.
429 This request was rate-limited.

200 response

Name Type Description
room_id string

Required: The joined room ID.

{
  "room_id": "!d41d8cd:matrix.org"
}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_FORBIDDEN",
  "error": "You are not invited to this room."
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

POST /_matrix/client/v3/rooms/{roomId}/join


Note that this API requires a room ID, not alias. /join/{roomIdOrAlias} exists if you have a room alias.

This API starts a user’s participation in a particular room, if that user is allowed to participate in that room. After this call, the client is allowed to see all current state events in the room, and all subsequent events associated with the room until the user leaves the room.

After a user has joined a room, the room will appear as an entry in the response of the /initialSync and /sync APIs.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string

Required: The room identifier (not alias) to join.

Request body

Name Type Description
reason string

Optional reason to be included as the reason on the subsequent membership event.

Added in v1.1

third_party_signed Third-party Signed

If supplied, the homeserver must verify that it matches a pending m.room.third_party_invite event in the room, and perform key validity checking if required by the event.

Third-party Signed
Name Type Description
mxid string

Required: The Matrix ID of the invitee.

sender string

Required: The Matrix ID of the user who issued the invite.

signatures {string: {string: string}}

Required: A signatures object containing a signature of the entire signed object.

token string

Required: The state key of the m.third_party_invite event.

Request body example

{
  "reason": "Looking for support",
  "third_party_signed": {
    "mxid": "@bob:example.org",
    "sender": "@alice:example.org",
    "signatures": {
      "example.org": {
        "ed25519:0": "some9signature"
      }
    },
    "token": "random8nonce"
  }
}

Responses

Status Description
200

The room has been joined.

The joined room ID must be returned in the room_id field.

403

You do not have permission to join the room. A meaningful errcode and description error text will be returned. Example reasons for rejection are:

  • The room is invite-only and the user was not invited.
  • The user has been banned from the room.
  • The room is restricted and the user failed to satisfy any of the conditions.
429 This request was rate-limited.

200 response

Name Type Description
room_id string

Required: The joined room ID.

{
  "room_id": "!d41d8cd:matrix.org"
}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_FORBIDDEN",
  "error": "You are not invited to this room."
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}
Knocking on rooms

[Added in v1.1] [Changed in v1.3]

As of v1.3, it is possible to knock on a restricted room if the room supports and is using the knock_restricted join rule.

Note that knock_restricted is only expected to work in room versions which support it.

If the join rules allow, external users to the room can /knock on it to request permission to join. Users with appropriate permissions within the room can then approve (/invite) or deny (/kick, /ban, or otherwise set membership to leave) the knock. Knocks can be retracted by calling /leave or otherwise setting membership to leave.

Users who are currently in the room, already invited, or banned cannot knock on the room.

To accept another user’s knock, the user must have permission to invite users to the room. To reject another user’s knock, the user must have permission to either kick or ban users (whichever is being performed). Note that setting another user’s membership to leave is kicking them.

The knocking homeserver should assume that an invite to the room means that the knock was accepted, even if the invite is not explicitly related to the knock.

Homeservers are permitted to automatically accept invites as a result of knocks as they should be aware of the user’s intent to join the room. If the homeserver is not auto-accepting invites (or there was an unrecoverable problem with accepting it), the invite is expected to be passed down normally to the client to handle. Clients can expect to see the join event if the server chose to auto-accept.

POST /_matrix/client/v3/knock/{roomIdOrAlias}


Added in v1.1

Note that this API takes either a room ID or alias, unlike other membership APIs.

This API “knocks” on the room to ask for permission to join, if the user is allowed to knock on the room. Acceptance of the knock happens out of band from this API, meaning that the client will have to watch for updates regarding the acceptance/rejection of the knock.

If the room history settings allow, the user will still be able to see history of the room while being in the “knock” state. The user will have to accept the invitation to join the room (acceptance of knock) to see messages reliably. See the /join endpoints for more information about history visibility to the user.

The knock will appear as an entry in the response of the /sync API.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomIdOrAlias string

Required: The room identifier or alias to knock upon.

query parameters
Name Type Description
server_name [string]

The servers to attempt to knock on the room through. One of the servers must be participating in the room.

Changed in v1.12: This parameter has been deprecated in favour of via and will be removed in a future version of the spec.

Clients SHOULD use via when the homeserver they’re talking to supports it. To do this, they MAY either detect server support through the supported spec versions in /_matrix/client/versions or always include both parameters with identical values.

Homeservers MUST ignore all server_name parameters if any via parameters are supplied.

via [string]

The servers to attempt to knock on the room through. One of the servers must be participating in the room.

Added in v1.12

Request body

Name Type Description
reason string

Optional reason to be included as the reason on the subsequent membership event.

Request body example

{
  "reason": "Looking for support"
}

Responses

Status Description
200

The room has been knocked upon.

The knocked room ID must be returned in the room_id field.

403

You do not have permission to knock on the room. A meaningful errcode and description error text will be returned. Example reasons for rejection are:

  • The room is not set up for knocking.
  • The user has been banned from the room.
404 The room could not be found or resolved to a room ID.
429 This request was rate-limited.

200 response

Name Type Description
room_id string

Required: The knocked room ID.

{
  "room_id": "!d41d8cd:matrix.org"
}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_FORBIDDEN",
  "error": "You are not allowed to knock on this room."
}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND",
  "error": "That room does not appear to exist."
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}
Restricted rooms

[Added in v1.2] [Changed in v1.3]

As of v1.3, it is possible to knock on a restricted room if the room supports and is using the knock_restricted join rule.

Note that knock_restricted is only expected to work in room versions which support it.

Restricted rooms are rooms with a join_rule of restricted. These rooms are accompanied by “allow conditions” as described in the m.room.join_rules state event.

If the user has an invite to the room then the restrictions will not affect them. They should be able to join by simply accepting the invite.

When joining without an invite, the server MUST verify that the requesting user meets at least one of the conditions. If no conditions can be verified or no conditions are satisfied, the user will not be able to join. When the join is happening over federation, the remote server will check the conditions before accepting the join. See the Server-Server Spec for more information.

The user does not need to maintain the conditions in order to stay a member of the room: the conditions are only checked/evaluated during the join process.

Conditions

Currently there is only one condition available: m.room_membership. This condition requires the user trying to join the room to be a joined member of another room (specifically, the room_id accompanying the condition). For example, if !restricted:example.org wanted to allow joined members of !other:example.org to join, !restricted:example.org would have the following content for m.room.join_rules:

{
  "join_rule": "restricted",
  "allow": [
    {
      "room_id": "!other:example.org",
      "type": "m.room_membership"
    }
  ]
}

Leaving rooms

A user can leave a room to stop receiving events for that room. A user must have been invited to or have joined the room before they are eligible to leave the room. Leaving a room to which the user has been invited rejects the invite, and can retract a knock. Once a user leaves a room, it will no longer appear in the response to the /sync API unless it is explicitly requested via a filter with the include_leave field set to true.

Whether or not they actually joined the room, if the room is an “invite-only” room the user will need to be re-invited before they can re-join the room.

A user can also forget a room which they have left. Rooms which have been forgotten will never appear the response to the /sync API, until the user re-joins, is re-invited, or knocks.

A user may wish to force another user to leave a room. This can be done by ‘kicking’ the other user. To do so, the user performing the kick MUST have the required power level. Once a user has been kicked, the behaviour is the same as if they had left of their own accord. In particular, the user is free to re-join if the room is not “invite-only”.

POST /_matrix/client/v3/rooms/{roomId}/forget


This API stops a user remembering about a particular room.

In general, history is a first class citizen in Matrix. After this API is called, however, a user will no longer be able to retrieve history for this room. If all users on a homeserver forget a room, the room is eligible for deletion from that homeserver.

If the user is currently joined to the room, they must leave the room before calling this API.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string

Required: The room identifier to forget.


Responses

Status Description
200 The room has been forgotten.
400 The user has not left the room
429 This request was rate-limited.

200 response

{}

400 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_UNKNOWN",
  "error": "User @example:matrix.org is in room !au1ba7o:matrix.org"
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

POST /_matrix/client/v3/rooms/{roomId}/leave


This API stops a user participating in a particular room.

If the user was already in the room, they will no longer be able to see new events in the room. If the room requires an invite to join, they will need to be re-invited before they can re-join.

If the user was invited to the room, but had not joined, this call serves to reject the invite.

The user will still be allowed to retrieve history from the room which they were previously allowed to see.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string

Required: The room identifier to leave.

Request body

Name Type Description
reason string

Optional reason to be included as the reason on the subsequent membership event.

Added in v1.1

Request body example

{
  "reason": "Saying farewell - thanks for the support!"
}

Responses

Status Description
200 The room has been left.
429 This request was rate-limited.

200 response

{}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

POST /_matrix/client/v3/rooms/{roomId}/kick


Kick a user from the room.

The caller must have the required power level in order to perform this operation.

Kicking a user adjusts the target member’s membership state to be leave with an optional reason. Like with other membership changes, a user can directly adjust the target member’s state by making a request to /rooms/<room id>/state/m.room.member/<user id>.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string

Required: The room identifier (not alias) from which the user should be kicked.

Request body

Name Type Description
reason string

The reason the user has been kicked. This will be supplied as the reason on the target’s updated m.room.member event.

user_id string

Required: The fully qualified user ID of the user being kicked.

Request body example

{
  "reason": "Telling unfunny jokes",
  "user_id": "@cheeky_monkey:matrix.org"
}

Responses

Status Description
200 The user has been kicked from the room.
403

You do not have permission to kick the user from the room. A meaningful errcode and description error text will be returned. Example reasons for rejections are:

  • The kicker is not currently in the room.
  • The kickee is not currently in the room.
  • The kicker’s power level is insufficient to kick users from the room.

200 response

{}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_FORBIDDEN",
  "error": "You do not have a high enough power level to kick from this room."
}
Banning users in a room

A user may decide to ban another user in a room. ‘Banning’ forces the target user to leave the room and prevents them from re-joining the room. A banned user will not be treated as a joined user, and so will not be able to send or receive events in the room. In order to ban someone, the user performing the ban MUST have the required power level. To ban a user, a request should be made to /rooms/<room_id>/ban with:

{
  "user_id": "<user id to ban>",
  "reason": "string: <reason for the ban>"
}

Banning a user adjusts the banned member’s membership state to ban. Like with other membership changes, a user can directly adjust the target member’s state, by making a request to /rooms/<room id>/state/m.room.member/<user id>:

{
  "membership": "ban"
}

A user must be explicitly unbanned with a request to /rooms/<room_id>/unban before they can re-join the room or be re-invited.

POST /_matrix/client/v3/rooms/{roomId}/ban


Ban a user in the room. If the user is currently in the room, also kick them.

When a user is banned from a room, they may not join it or be invited to it until they are unbanned.

The caller must have the required power level in order to perform this operation.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string

Required: The room identifier (not alias) from which the user should be banned.

Request body

Name Type Description
reason string

The reason the user has been banned. This will be supplied as the reason on the target’s updated m.room.member event.

Added in v1.1

user_id string

Required: The fully qualified user ID of the user being banned.

Request body example

{
  "reason": "Telling unfunny jokes",
  "user_id": "@cheeky_monkey:matrix.org"
}

Responses

Status Description
200 The user has been kicked and banned from the room.
403

You do not have permission to ban the user from the room. A meaningful errcode and description error text will be returned. Example reasons for rejections are:

  • The banner is not currently in the room.
  • The banner’s power level is insufficient to ban users from the room.

200 response

{}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_FORBIDDEN",
  "error": "You do not have a high enough power level to ban from this room."
}

POST /_matrix/client/v3/rooms/{roomId}/unban


Unban a user from the room. This allows them to be invited to the room, and join if they would otherwise be allowed to join according to its join rules.

The caller must have the required power level in order to perform this operation.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string

Required: The room identifier (not alias) from which the user should be unbanned.

Request body

Name Type Description
reason string

Optional reason to be included as the reason on the subsequent membership event.

Added in v1.1

user_id string

Required: The fully qualified user ID of the user being unbanned.

Request body example

{
  "reason": "They've been banned long enough",
  "user_id": "@cheeky_monkey:matrix.org"
}

Responses

Status Description
200 The user has been unbanned from the room.
403

You do not have permission to unban the user from the room. A meaningful errcode and description error text will be returned. Example reasons for rejections are:

  • The unbanner’s power level is insufficient to unban users from the room.

200 response

{}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_FORBIDDEN",
  "error": "You do not have a high enough power level to unban from this room."
}

Listing rooms

GET /_matrix/client/v3/directory/list/room/{roomId}


Gets the visibility of a given room on the server’s public room directory.

Rate-limited: No
Requires authentication: No

Request

Request parameters

path parameters
Name Type Description
roomId string

Required: The room ID.


Responses

Status Description
200 The visibility of the room in the directory
404 The room is not known to the server

200 response

Name Type Description
visibility string

The visibility of the room in the directory.

One of: [private, public].

{
  "visibility": "public"
}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND",
  "error": "Room not found"
}

PUT /_matrix/client/v3/directory/list/room/{roomId}


Sets the visibility of a given room in the server’s public room directory.

Servers may choose to implement additional access control checks here, for instance that room visibility can only be changed by the room creator or a server administrator.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string

Required: The room ID.

Request body

Name Type Description
visibility string

The new visibility setting for the room. Defaults to ‘public’.

One of: [private, public].

Request body example

{
  "visibility": "public"
}

Responses

Status Description
200 The visibility was updated, or no change was needed.
404 The room is not known to the server

200 response

{}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND",
  "error": "Room not found"
}

GET /_matrix/client/v3/publicRooms


Lists the public rooms on the server.

This API returns paginated responses. The rooms are ordered by the number of joined members, with the largest rooms first.

Rate-limited: No
Requires authentication: No

Request

Request parameters

query parameters
Name Type Description
limit integer

Limit the number of results returned.

server string

The server to fetch the public room lists from. Defaults to the local server. Case sensitive.

since string

A pagination token from a previous request, allowing clients to get the next (or previous) batch of rooms. The direction of pagination is specified solely by which token is supplied, rather than via an explicit flag.


Responses

Status Description
200 A list of the rooms on the server.

200 response

Name Type Description
chunk [PublicRoomsChunk]

Required: A paginated chunk of public rooms.

next_batch string

A pagination token for the response. The absence of this token means there are no more results to fetch and the client should stop paginating.

prev_batch string

A pagination token that allows fetching previous results. The absence of this token means there are no results before this batch, i.e. this is the first batch.

total_room_count_estimate integer

An estimate on the total number of public rooms, if the server has an estimate.

PublicRoomsChunk
Name Type Description
avatar_url URI

The URL for the room’s avatar, if one is set.

canonical_alias string

The canonical alias of the room, if any.

guest_can_join boolean

Required: Whether guest users may join the room and participate in it. If they can, they will be subject to ordinary power level rules like any other user.

join_rule string

The room’s join rule. When not present, the room is assumed to be public. Note that rooms with invite join rules are not expected here, but rooms with knock rules are given their near-public nature.

name string

The name of the room, if any.

num_joined_members integer

Required: The number of members joined to the room.

room_id string

Required: The ID of the room.

room_type string

The type of room (from m.room.create), if any.

Added in v1.4

topic string

The topic of the room, if any.

world_readable boolean

Required: Whether the room may be viewed by guest users without joining.

{
  "chunk": [
    {
      "avatar_url": "mxc://bleecker.street/CHEDDARandBRIE",
      "guest_can_join": false,
      "join_rule": "public",
      "name": "CHEESE",
      "num_joined_members": 37,
      "room_id": "!ol19s:bleecker.street",
      "room_type": "m.space",
      "topic": "Tasty tasty cheese",
      "world_readable": true
    }
  ],
  "next_batch": "p190q",
  "prev_batch": "p1902",
  "total_room_count_estimate": 115
}

POST /_matrix/client/v3/publicRooms


Lists the public rooms on the server, with optional filter.

This API returns paginated responses. The rooms are ordered by the number of joined members, with the largest rooms first.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

query parameters
Name Type Description
server string

The server to fetch the public room lists from. Defaults to the local server. Case sensitive.

Request body

Name Type Description
filter Filter

Filter to apply to the results.

include_all_networks boolean

Whether or not to include all known networks/protocols from application services on the homeserver. Defaults to false.

limit integer

Limit the number of results returned.

since string

A pagination token from a previous request, allowing clients to get the next (or previous) batch of rooms. The direction of pagination is specified solely by which token is supplied, rather than via an explicit flag.

third_party_instance_id string

The specific third-party network/protocol to request from the homeserver. Can only be used if include_all_networks is false.

Filter
Name Type Description
generic_search_term string

An optional string to search for in the room metadata, e.g. name, topic, canonical alias, etc.

room_types [string|null]

An optional list of room types to search for. To include rooms without a room type, specify null within this list. When not specified, all applicable rooms (regardless of type) are returned.

Added in v1.4

Request body example

{
  "filter": {
    "generic_search_term": "foo",
    "room_types": [
      null,
      "m.space"
    ]
  },
  "include_all_networks": false,
  "limit": 10,
  "third_party_instance_id": "irc"
}

Responses

Status Description
200 A list of the rooms on the server.

200 response

Name Type Description
chunk [PublicRoomsChunk]

Required: A paginated chunk of public rooms.

next_batch string

A pagination token for the response. The absence of this token means there are no more results to fetch and the client should stop paginating.

prev_batch string

A pagination token that allows fetching previous results. The absence of this token means there are no results before this batch, i.e. this is the first batch.

total_room_count_estimate integer

An estimate on the total number of public rooms, if the server has an estimate.

PublicRoomsChunk
Name Type Description
avatar_url URI

The URL for the room’s avatar, if one is set.

canonical_alias string

The canonical alias of the room, if any.

guest_can_join boolean

Required: Whether guest users may join the room and participate in it. If they can, they will be subject to ordinary power level rules like any other user.

join_rule string

The room’s join rule. When not present, the room is assumed to be public. Note that rooms with invite join rules are not expected here, but rooms with knock rules are given their near-public nature.

name string

The name of the room, if any.

num_joined_members integer

Required: The number of members joined to the room.

room_id string

Required: The ID of the room.

room_type string

The type of room (from m.room.create), if any.

Added in v1.4

topic string

The topic of the room, if any.

world_readable boolean

Required: Whether the room may be viewed by guest users without joining.

{
  "chunk": [
    {
      "avatar_url": "mxc://bleecker.street/CHEDDARandBRIE",
      "guest_can_join": false,
      "join_rule": "public",
      "name": "CHEESE",
      "num_joined_members": 37,
      "room_id": "!ol19s:bleecker.street",
      "room_type": "m.space",
      "topic": "Tasty tasty cheese",
      "world_readable": true
    }
  ],
  "next_batch": "p190q",
  "prev_batch": "p1902",
  "total_room_count_estimate": 115
}

User Data

User Directory

POST /_matrix/client/v3/user_directory/search


Performs a search for users. The homeserver may determine which subset of users are searched, however the homeserver MUST at a minimum consider the users the requesting user shares a room with and those who reside in public rooms (known to the homeserver). The search MUST consider local users to the homeserver, and SHOULD query remote users as part of the search.

The search is performed case-insensitively on user IDs and display names preferably using a collation determined based upon the Accept-Language header provided in the request, if present.

Rate-limited: Yes
Requires authentication: Yes

Request

Request body

Name Type Description
limit integer

The maximum number of results to return. Defaults to 10.

search_term string

Required: The term to search for

Request body example

{
  "limit": 10,
  "search_term": "foo"
}

Responses

Status Description
200 The results of the search.
429 This request was rate-limited.

200 response

Name Type Description
limited boolean

Required: Indicates if the result list has been truncated by the limit.

results [User]

Required: Ordered by rank and then whether or not profile info is available.

User
Name Type Description
avatar_url URI

The avatar url, as an mxc:// URI, if one exists.

display_name string

The display name of the user, if one exists.

user_id string

Required: The user’s matrix user ID.

{
  "limited": false,
  "results": [
    {
      "avatar_url": "mxc://bar.com/foo",
      "display_name": "Foo",
      "user_id": "@foo:bar.com"
    }
  ]
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

Profiles

GET /_matrix/client/v3/profile/{userId}


Get the combined profile information for this user. This API may be used to fetch the user’s own profile information or other users; either locally or on remote homeservers.

Rate-limited: No
Requires authentication: No

Request

Request parameters

path parameters
Name Type Description
userId string

Required: The user whose profile information to get.


Responses

Status Description
200 The profile information for this user.
403 The server is unwilling to disclose whether the user exists and/or has profile information.
404 There is no profile information for this user or this user does not exist.

200 response

Name Type Description
avatar_url URI

The user’s avatar URL if they have set one, otherwise not present.

displayname string

The user’s display name if they have set one, otherwise not present.

{
  "avatar_url": "mxc://matrix.org/SDGdghriugerRg",
  "displayname": "Alice Margatroid"
}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_FORBIDDEN",
  "error": "Profile lookup is disabled on this homeserver"
}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND",
  "error": "Profile not found"
}

GET /_matrix/client/v3/profile/{userId}/avatar_url


Get the user’s avatar URL. This API may be used to fetch the user’s own avatar URL or to query the URL of other users; either locally or on remote homeservers.

Rate-limited: No
Requires authentication: No

Request

Request parameters

path parameters
Name Type Description
userId string

Required: The user whose avatar URL to get.


Responses

Status Description
200 The avatar URL for this user.
403 The server is unwilling to disclose whether the user exists and/or has an avatar URL.
404 There is no avatar URL for this user or this user does not exist.

200 response

Name Type Description
avatar_url URI

The user’s avatar URL if they have set one, otherwise not present.

{
  "avatar_url": "mxc://matrix.org/SDGdghriugerRg"
}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_FORBIDDEN",
  "error": "Profile lookup is disabled on this homeserver"
}

PUT /_matrix/client/v3/profile/{userId}/avatar_url


This API sets the given user’s avatar URL. You must have permission to set this user’s avatar URL, e.g. you need to have their access_token.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
userId string

Required: The user whose avatar URL to set.

Request body

Name Type Description
avatar_url URI

The new avatar URL for this user.

Request body example

{
  "avatar_url": "mxc://matrix.org/wefh34uihSDRGhw34"
}

Responses

Status Description
200 The avatar URL was set.
429 This request was rate-limited.

200 response

{}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

GET /_matrix/client/v3/profile/{userId}/displayname


Get the user’s display name. This API may be used to fetch the user’s own displayname or to query the name of other users; either locally or on remote homeservers.

Rate-limited: No
Requires authentication: No

Request

Request parameters

path parameters
Name Type Description
userId string

Required: The user whose display name to get.


Responses

Status Description
200 The display name for this user.
403 The server is unwilling to disclose whether the user exists and/or has a display name.
404 There is no display name for this user or this user does not exist.

200 response

Name Type Description
displayname string

The user’s display name if they have set one, otherwise not present.

{
  "displayname": "Alice Margatroid"
}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_FORBIDDEN",
  "error": "Profile lookup is disabled on this homeserver"
}

PUT /_matrix/client/v3/profile/{userId}/displayname


This API sets the given user’s display name. You must have permission to set this user’s display name, e.g. you need to have their access_token.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
userId string

Required: The user whose display name to set.

Request body

Name Type Description
displayname string

The new display name for this user.

Request body example

{
  "displayname": "Alice Margatroid"
}

Responses

Status Description
200 The display name was set.
429 This request was rate-limited.

200 response

{}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

Server behaviour

Homeservers MUST at a minimum allow profile look-up for:

  • users that share a room with the requesting user
  • users that reside in public rooms known to the homeserver

In all other cases, homeservers MAY deny profile look-up by responding with 403 and an error code of M_FORBIDDEN.

When a remote user is queried and the query is not denied per the above, homeservers SHOULD query the remote server for the user’s profile information. The remote server MAY itself deny profile queries over federation, however.

When the requested user does not exist, homeservers MAY choose whether to respond with 403 or 404. If the server denies profile look-up in all but the required cases, 403 is RECOMMENDED.

Events on Change of Profile Information

Because the profile display name and avatar information are likely to be used in many places of a client’s display, changes to these fields cause an automatic propagation event to occur, informing likely-interested parties of the new values. This change is conveyed using two separate mechanisms:

  • an m.room.member event (with a join membership) is sent to every room the user is a member of, to update the displayname and avatar_url.
  • an m.presence presence status update is sent, again containing the new values of the displayname and avatar_url keys, in addition to the required presence key containing the current presence state of the user.

Both of these should be done automatically by the homeserver when a user successfully changes their display name or avatar URL fields.

Additionally, when homeservers emit room membership events for their own users, they should include the display name and avatar URL fields in these events so that clients already have these details to hand, and do not have to perform extra round trips to query it.

Modules

Modules are parts of the Client-Server API which are not universal to all endpoints. Modules are strictly defined within this specification and should not be mistaken for experimental extensions or optional features. A compliant server implementation MUST support all modules and supporting specification (unless the implementation only targets clients of certain profiles, in which case only the required modules for those feature profiles MUST be implemented). A compliant client implementation MUST support all the required modules and supporting specification for the Feature Profile it targets.

Feature Profiles

Matrix supports many different kinds of clients: from embedded IoT devices to desktop clients. Not all clients can provide the same feature sets as other clients e.g. due to lack of physical hardware such as not having a screen. Clients can fall into one of several profiles and each profile contains a set of features that the client MUST support. This section details a set of “feature profiles”. Clients are expected to implement a profile in its entirety in order for it to be classified as that profile.

Summary

Module / Profile Web Mobile Desktop CLI Embedded
Content Repository Required Required Required Optional Optional
Direct Messaging Required Required Required Required Optional
Ignoring Users Required Required Required Optional Optional
Instant Messaging Required Required Required Required Optional
Presence Required Required Required Required Optional
Push Notifications Optional Required Optional Optional Optional
Receipts Required Required Required Required Optional
Room History Visibility Required Required Required Required Optional
Room Upgrades Required Required Required Required Optional
Third-party Invites Optional Required Optional Optional Optional
Typing Notifications Required Required Required Required Optional
User and Room Mentions Required Required Required Optional Optional
Voice over IP Required Required Required Optional Optional
Client Config Optional Optional Optional Optional Optional
Device Management Optional Optional Optional Optional Optional
End-to-End Encryption Optional Optional Optional Optional Optional
Event Annotations and reactions Optional Optional Optional Optional Optional
Event Context Optional Optional Optional Optional Optional
Event Replacements Optional Optional Optional Optional Optional
Read and Unread Markers Optional Optional Optional Optional Optional
Guest Access Optional Optional Optional Optional Optional
Moderation Policy Lists Optional Optional Optional Optional Optional
OpenID Optional Optional Optional Optional Optional
Reference Relations Optional Optional Optional Optional Optional
Reporting Content Optional Optional Optional Optional Optional
Rich replies Optional Optional Optional Optional Optional
Room Previews Optional Optional Optional Optional Optional
Room Tagging Optional Optional Optional Optional Optional
SSO Client Login/Authentication Optional Optional Optional Optional Optional
Secrets Optional Optional Optional Optional Optional
Send-to-Device Messaging Optional Optional Optional Optional Optional
Server Access Control Lists (ACLs) Optional Optional Optional Optional Optional
Server Administration Optional Optional Optional Optional Optional
Server Notices Optional Optional Optional Optional Optional
Server Side Search Optional Optional Optional Optional Optional
Spaces Optional Optional Optional Optional Optional
Sticker Messages Optional Optional Optional Optional Optional
Third-party Networks Optional Optional Optional Optional Optional
Threading Optional Optional Optional Optional Optional

Please see each module for more details on what clients need to implement.

Clients

Stand-alone web (Web)

This is a web page which heavily uses Matrix for communication. Single-page web apps would be classified as a stand-alone web client, as would multi-page web apps which use Matrix on nearly every page.

Mobile (Mobile)

This is a Matrix client specifically designed for consumption on mobile devices. This is typically a mobile app but need not be so provided the feature set can be reached (e.g. if a mobile site could display push notifications it could be classified as a mobile client).

Desktop (Desktop)

This is a native GUI application which can run in its own environment outside a browser.

Command Line Interface (CLI)

This is a client which is used via a text-based terminal.

Embedded (Embedded)

This is a client which is embedded into another application or an embedded device.

Application

This is a Matrix client which is embedded in another website, e.g. using iframes. These embedded clients are typically for a single purpose related to the website in question, and are not intended to be fully-fledged communication apps.

Device

This is a client which is typically running on an embedded device such as a kettle, fridge or car. These clients tend to perform a few operations and run in a resource constrained environment. Like embedded applications, they are not intended to be fully-fledged communication systems.

Instant Messaging

This module adds support for sending human-readable messages to a room. It also adds support for associating human-readable information with the room itself such as a room name and topic.

Events

m.room.message


This event is used when sending messages in a room. Messages are not limited to be text. The msgtype key outlines the type of message, e.g. text, audio, image, video, etc. The body key is text and MUST be used with every kind of msgtype as a fallback mechanism for when a client cannot render a message. This allows clients to display something even if it is just plain text.

Event type: Message event

Content

Name Type Description
body string

Required: The textual representation of this message.

msgtype string

Required: The type of message, e.g. m.image, m.text

Examples

{
  "content": {
    "body": "This is an example text message",
    "format": "org.matrix.custom.html",
    "formatted_body": "<b>This is an example text message</b>",
    "msgtype": "m.text"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "type": "m.room.message",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

m.room.name


A room has an opaque room ID which is not human-friendly to read. A room alias is human-friendly, but not all rooms have room aliases. The room name is a human-friendly string designed to be displayed to the end-user. The room name is not unique, as multiple rooms can have the same room name set.

If a room has an m.room.name event with an absent, null, or empty name field, it should be treated the same as a room with no m.room.name event.

An event of this type is automatically created when creating a room using /createRoom with the name key.

Event type: State event
State key A zero-length string.

Content

Name Type Description
name string

Required: The name of the room.

Examples

{
  "content": {
    "name": "The room name"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "state_key": "",
  "type": "m.room.name",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

m.room.topic


A topic is a short message detailing what is currently being discussed in the room. It can also be used as a way to display extra information about the room, which may not be suitable for the room name. The room topic can also be set when creating a room using /createRoom with the topic key.

Event type: State event
State key A zero-length string.

Content

Name Type Description
topic string

Required: The topic text.

Examples

{
  "content": {
    "topic": "A room topic"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "state_key": "",
  "type": "m.room.topic",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

m.room.avatar


A picture that is associated with the room. This can be displayed alongside the room information.

Event type: State event
State key A zero-length string.

Content

Name Type Description
info AvatarInfo

Metadata about the image referred to in url.

url string

The URL to the image. If this property is not present, the room has no avatar. This can be useful to remove a previous room avatar.

AvatarInfo
Name Type Description
h integer

The intended display height of the image in pixels. This may differ from the intrinsic dimensions of the image file.

mimetype string

The mimetype of the image, e.g. image/jpeg.

size integer

Size of the image in bytes.

thumbnail_info ThumbnailInfo

Metadata about the image referred to in thumbnail_url.

thumbnail_url string

The URL (typically mxc:// URI) to a thumbnail of the image.

w integer

The intended display width of the image in pixels. This may differ from the intrinsic dimensions of the image file.

ThumbnailInfo
Name Type Description
h integer

The intended display height of the image in pixels. This may differ from the intrinsic dimensions of the image file.

mimetype string

The mimetype of the image, e.g. image/jpeg.

size integer

Size of the image in bytes.

w integer

The intended display width of the image in pixels. This may differ from the intrinsic dimensions of the image file.

Examples

{
  "content": {
    "info": {
      "h": 398,
      "mimetype": "image/jpeg",
      "size": 31037,
      "w": 394
    },
    "url": "mxc://example.org/JWEIFJgwEIhweiWJE"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "state_key": "",
  "type": "m.room.avatar",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

m.room.pinned_events


This event is used to “pin” particular events in a room for other participants to review later. The order of the pinned events is guaranteed and based upon the order supplied in the event. Clients should be aware that the current user may not be able to see some of the events pinned due to visibility settings in the room. Clients are responsible for determining if a particular event in the pinned list is displayable, and have the option to not display it if it cannot be pinned in the client.

Event type: State event
State key A zero-length string.

Content

Name Type Description
pinned [string]

Required: An ordered list of event IDs to pin.

Examples

{
  "content": {
    "pinned": [
      "$someevent:example.org"
    ]
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "state_key": "",
  "type": "m.room.pinned_events",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}
m.room.message msgtypes

Each m.room.message MUST have a msgtype key which identifies the type of message being sent. Each type has their own required and optional keys, as outlined below. If a client cannot display the given msgtype then it SHOULD display the fallback plain text body key instead.

Some message types support HTML in the event content that clients should prefer to display if available. Currently m.text, m.emote, m.notice, m.image, m.file, m.audio, m.video and m.key.verification.request support an additional format parameter of org.matrix.custom.html. When this field is present, a formatted_body with the HTML must be provided. The plain text version of the HTML should be provided in the body.

[Changed in v1.10] In previous versions of the specification, the format and formatted fields were limited to m.text, m.emote, m.notice, and m.key.verification.request. This list is expanded to include m.image, m.file, m.audio and m.video for media captions.

Clients should limit the HTML they render to avoid Cross-Site Scripting, HTML injection, and similar attacks. The strongly suggested set of HTML tags to permit, denying the use and rendering of anything else, is: del, h1, h2, h3, h4, h5, h6, blockquote, p, a, ul, ol, sup, sub, li, b, i, u, strong, em, s, code, hr, br, div, table, thead, tbody, tr, th, td, caption, pre, span, img, details, summary.

[Added in v1.10] HTML features MAY be deprecated and replaced by their modern equivalent without requiring a Spec Change Proposal when they are deprecated in the WHATWG HTML Living Standard.

[Changed in v1.10] In previous versions of the specification, the font tag was suggested with the data-mx-bg-color, data-mx-color and color attributes. This tag is now deprecated in favor of the span tag with the data-mx-bg-color and data-mx-color attributes in new messages.

Not all attributes on those tags should be permitted as they may be avenues for other disruption attempts, such as adding onclick handlers or excessively large text. Clients should only permit the attributes listed for the tags below. Where data-mx-bg-color and data-mx-color are listed, clients should translate the value (a # character followed by a 6-character hex color code) to the appropriate CSS/attributes for the tag.

Tag Permitted Attributes
span data-mx-bg-color, data-mx-color, data-mx-spoiler (see spoiler messages), data-mx-maths (see mathematical messages)
a target, href (provided the value is not relative and has a scheme matching one of: https, http, ftp, mailto, magnet)
img width, height, alt, title, src (provided it is a Matrix Content (mxc://) URI)
ol start
code class (only classes which start with language- for syntax highlighting)
div data-mx-maths (see mathematical messages)

Additionally, web clients should ensure that all a tags get a rel="noopener" to prevent the target page from referencing the client’s tab/window.

Tags must not be nested more than 100 levels deep. Clients should only support the subset of tags they can render, falling back to other representations of the tags where possible. For example, a client may not be able to render tables correctly and instead could fall back to rendering tab-delimited text.

In addition to not rendering unsafe HTML, clients should not emit unsafe HTML in events. Likewise, clients should not generate HTML that is not needed, such as extra paragraph tags surrounding text due to Rich Text Editors. HTML included in events should otherwise be valid, such as having appropriate closing tags, appropriate attributes (considering the custom ones defined in this specification), and generally valid structure.

[Changed in v1.13] In previous versions of the specification, rich replies could use a special tag, mx-reply. This is no longer the case. Clients SHOULD strip this tag and its content. See the “Rich replies” section for more information.

A future iteration of the specification will support more powerful and extensible message formatting options, such as the proposal MSC1767.

m.text


This message is the most basic message and is used to represent text.

Event type: Message event

Content

Name Type Description
body string

Required: The body of the message.

format string

The format used in the formatted_body. Currently only org.matrix.custom.html is supported.

formatted_body string

The formatted version of the body. This is required if format is specified.

msgtype string

Required:

One of: [m.text].

Examples

{
  "content": {
    "body": "This is an example text message",
    "format": "org.matrix.custom.html",
    "formatted_body": "<b>This is an example text message</b>",
    "msgtype": "m.text"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "type": "m.room.message",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

m.emote


This message is similar to m.text except that the sender is ‘performing’ the action contained in the body key, similar to /me in IRC. This message should be prefixed by the name of the sender. This message could also be represented in a different colour to distinguish it from regular m.text messages.

Event type: Message event

Content

Name Type Description
body string

Required: The emote action to perform.

format string

The format used in the formatted_body. Currently only org.matrix.custom.html is supported.

formatted_body string

The formatted version of the body. This is required if format is specified.

msgtype string

Required:

One of: [m.emote].

Examples

{
  "content": {
    "body": "thinks this is an example emote",
    "format": "org.matrix.custom.html",
    "formatted_body": "thinks <b>this</b> is an example emote",
    "msgtype": "m.emote"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "type": "m.room.message",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

m.notice


The m.notice type is primarily intended for responses from automated clients. An m.notice message must be treated the same way as a regular m.text message with two exceptions. Firstly, clients should present m.notice messages to users in a distinct manner, and secondly, m.notice messages must never be automatically responded to. This helps to prevent infinite-loop situations where two automated clients continuously exchange messages.

Event type: Message event

Content

Name Type Description
body string

Required: The notice text to send.

format string

The format used in the formatted_body. Currently only org.matrix.custom.html is supported.

formatted_body string

The formatted version of the body. This is required if format is specified.

msgtype string

Required:

One of: [m.notice].

Examples

{
  "content": {
    "body": "This is an example notice",
    "format": "org.matrix.custom.html",
    "formatted_body": "This is an <strong>example</strong> notice",
    "msgtype": "m.notice"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "type": "m.room.message",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

m.image


This message represents a single image and an optional thumbnail.

Event type: Message event

Content

Name Type Description
body string

Required: If filename is not set or the value of both properties are identical, this is the filename of the original upload. Otherwise, this is a caption for the image.

Changed in v1.10: This property can act as a caption for the image.

file EncryptedFile

Required if the file is encrypted. Information on the encrypted file, as specified in End-to-end encryption.

filename string

The original filename of the uploaded file.

Added in v1.10

format string

The format used in the formatted_body. Currently only org.matrix.custom.html is supported.

Added in v1.10

formatted_body string

The formatted version of the body, when it acts as a caption. This is required if format is specified.

Added in v1.10

info ImageInfo

Metadata about the image referred to in url.

msgtype string

Required:

One of: [m.image].

url string

Required if the file is unencrypted. The URL (typically mxc:// URI) to the image.

ImageInfo
Name Type Description
h integer

The intended display height of the image in pixels. This may differ from the intrinsic dimensions of the image file.

mimetype string

The mimetype of the image, e.g. image/jpeg.

size integer

Size of the image in bytes.

thumbnail_file EncryptedFile

Information on the encrypted thumbnail file, as specified in End-to-end encryption. Only present if the thumbnail is encrypted.

thumbnail_info ThumbnailInfo

Metadata about the image referred to in thumbnail_url.

thumbnail_url string

The URL (typically mxc:// URI) to a thumbnail of the image. Only present if the thumbnail is unencrypted.

w integer

The intended display width of the image in pixels. This may differ from the intrinsic dimensions of the image file.

ThumbnailInfo
Name Type Description
h integer

The intended display height of the image in pixels. This may differ from the intrinsic dimensions of the image file.

mimetype string

The mimetype of the image, e.g. image/jpeg.

size integer

Size of the image in bytes.

w integer

The intended display width of the image in pixels. This may differ from the intrinsic dimensions of the image file.

Examples

{
  "content": {
    "body": "filename.jpg",
    "info": {
      "h": 398,
      "mimetype": "image/jpeg",
      "size": 31037,
      "w": 394
    },
    "msgtype": "m.image",
    "url": "mxc://example.org/JWEIFJgwEIhweiWJE"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "type": "m.room.message",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

m.file


This message represents a generic file.

Event type: Message event

Content

Name Type Description
body string

Required: If filename is not set or the value of both properties are identical, this is the filename of the original upload. Otherwise, this is a caption for the file.

Changed in v1.10: This property can act as a caption for the file.

file EncryptedFile

Required if the file is encrypted. Information on the encrypted file, as specified in End-to-end encryption.

filename string

The original filename of the uploaded file.

format string

The format used in the formatted_body. Currently only org.matrix.custom.html is supported.

Added in v1.10

formatted_body string

The formatted version of the body, when it acts as a caption. This is required if format is specified.

Added in v1.10

info FileInfo

Information about the file referred to in url.

msgtype string

Required:

One of: [m.file].

url string

Required if the file is unencrypted. The URL (typically mxc:// URI) to the file.

FileInfo
Name Type Description
mimetype string

The mimetype of the file e.g. application/msword.

size integer

The size of the file in bytes.

thumbnail_file EncryptedFile

Information on the encrypted thumbnail file, as specified in End-to-end encryption. Only present if the thumbnail is encrypted.

thumbnail_info ThumbnailInfo

Metadata about the image referred to in thumbnail_url.

thumbnail_url string

The URL to the thumbnail of the file. Only present if the thumbnail is unencrypted.

ThumbnailInfo
Name Type Description
h integer

The intended display height of the image in pixels. This may differ from the intrinsic dimensions of the image file.

mimetype string

The mimetype of the image, e.g. image/jpeg.

size integer

Size of the image in bytes.

w integer

The intended display width of the image in pixels. This may differ from the intrinsic dimensions of the image file.

Examples

{
  "content": {
    "body": "something-important.doc",
    "filename": "something-important.doc",
    "info": {
      "mimetype": "application/msword",
      "size": 46144
    },
    "msgtype": "m.file",
    "url": "mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "type": "m.room.message",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

m.audio


This message represents a single audio clip.

Event type: Message event

Content

Name Type Description
body string

Required: If filename is not set or the value of both properties are identical, this is the filename of the original upload. Otherwise, this is a caption for the audio.

Changed in v1.10: This property can act as a caption for the audio.

file EncryptedFile

Required if the file is encrypted. Information on the encrypted file, as specified in End-to-end encryption.

filename string

The original filename of the uploaded file.

Added in v1.10

format string

The format used in the formatted_body. Currently only org.matrix.custom.html is supported.

Added in v1.10

formatted_body string

The formatted version of the body, when it acts as a caption. This is required if format is specified.

Added in v1.10

info AudioInfo

Metadata for the audio clip referred to in url.

msgtype string

Required:

One of: [m.audio].

url string

Required if the file is unencrypted. The URL (typically mxc:// URI) to the audio clip.

AudioInfo
Name Type Description
duration integer

The duration of the audio in milliseconds.

mimetype string

The mimetype of the audio e.g. audio/aac.

size integer

The size of the audio clip in bytes.

Examples

{
  "content": {
    "body": "Bee Gees - Stayin' Alive",
    "info": {
      "duration": 2140786,
      "mimetype": "audio/mpeg",
      "size": 1563685
    },
    "msgtype": "m.audio",
    "url": "mxc://example.org/ffed755USFFxlgbQYZGtryd"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "type": "m.room.message",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

m.location


This message represents a real-world location.

Event type: Message event

Content

Name Type Description
body string

Required: A description of the location e.g. ‘Big Ben, London, UK’, or some kind of content description for accessibility e.g. ’location attachment’.

geo_uri string

Required: A geo URI (RFC5870) representing this location.

info LocationInfo
msgtype string

Required:

One of: [m.location].

LocationInfo
Name Type Description
thumbnail_file EncryptedFile

Information on the encrypted thumbnail file, as specified in End-to-end encryption. Only present if the thumbnail is encrypted.

thumbnail_info ThumbnailInfo

Metadata about the image referred to in thumbnail_url.

thumbnail_url string

The URL to a thumbnail of the location being represented. Only present if the thumbnail is unencrypted.

ThumbnailInfo
Name Type Description
h integer

The intended display height of the image in pixels. This may differ from the intrinsic dimensions of the image file.

mimetype string

The mimetype of the image, e.g. image/jpeg.

size integer

Size of the image in bytes.

w integer

The intended display width of the image in pixels. This may differ from the intrinsic dimensions of the image file.

Examples

{
  "content": {
    "body": "Big Ben, London, UK",
    "geo_uri": "geo:51.5008,0.1247",
    "info": {
      "thumbnail_info": {
        "h": 300,
        "mimetype": "image/jpeg",
        "size": 46144,
        "w": 300
      },
      "thumbnail_url": "mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe"
    },
    "msgtype": "m.location"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "type": "m.room.message",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

m.video


This message represents a single video clip.

Event type: Message event

Content

Name Type Description
body string

Required: If filename is not set or the value of both properties are identical, this is the filename of the original upload. Otherwise, this is a caption for the video.

Changed in v1.10: This property can act as a caption for the video.

file EncryptedFile

Required if the file is encrypted. Information on the encrypted file, as specified in End-to-end encryption.

filename string

The original filename of the uploaded file.

Added in v1.10

format string

The format used in the formatted_body. Currently only org.matrix.custom.html is supported.

Added in v1.10

formatted_body string

The formatted version of the body, when it acts as a caption. This is required if format is specified.

Added in v1.10

info VideoInfo

Metadata about the video clip referred to in url.

msgtype string

Required:

One of: [m.video].

url string

Required if the file is unencrypted. The URL (typically mxc:// URI) to the video clip.

VideoInfo
Name Type Description
duration integer

The duration of the video in milliseconds.

h integer

The height of the video in pixels.

mimetype string

The mimetype of the video e.g. video/mp4.

size integer

The size of the video in bytes.

thumbnail_file EncryptedFile

Information on the encrypted thumbnail file, as specified in End-to-end encryption. Only present if the thumbnail is encrypted.

thumbnail_info ThumbnailInfo

Metadata about the image referred to in thumbnail_url.

thumbnail_url string

The URL (typically mxc:// URI) to an image thumbnail of the video clip. Only present if the thumbnail is unencrypted.

w integer

The width of the video in pixels.

ThumbnailInfo
Name Type Description
h integer

The intended display height of the image in pixels. This may differ from the intrinsic dimensions of the image file.

mimetype string

The mimetype of the image, e.g. image/jpeg.

size integer

Size of the image in bytes.

w integer

The intended display width of the image in pixels. This may differ from the intrinsic dimensions of the image file.

Examples

{
  "content": {
    "body": "Gangnam Style",
    "info": {
      "duration": 2140786,
      "h": 320,
      "mimetype": "video/mp4",
      "size": 1563685,
      "thumbnail_info": {
        "h": 300,
        "mimetype": "image/jpeg",
        "size": 46144,
        "w": 300
      },
      "thumbnail_url": "mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe",
      "w": 480
    },
    "msgtype": "m.video",
    "url": "mxc://example.org/a526eYUSFFxlgbQYZmo442"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "type": "m.room.message",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

Client behaviour

Clients SHOULD verify the structure of incoming events to ensure that the expected keys exist and that they are of the right type. Clients can discard malformed events or display a placeholder message to the user. Redacted m.room.message events MUST be removed from the client. This can either be replaced with placeholder text (e.g. “[REDACTED]”) or the redacted message can be removed entirely from the messages view.

Events which have attachments (e.g. m.image, m.file) SHOULD be uploaded using the content repository module where available. The resulting mxc:// URI can then be used in the url key.

Clients MAY include a client generated thumbnail image for an attachment under a info.thumbnail_url key. The thumbnail SHOULD also be a mxc:// URI. Clients displaying events with attachments can either use the client generated thumbnail or ask its homeserver to generate a thumbnail from the original attachment using the content repository module.

Recommendations when sending messages

In the event of send failure, clients SHOULD retry requests using an exponential-backoff algorithm for a certain amount of time T. It is recommended that T is no longer than 5 minutes. After this time, the client should stop retrying and mark the message as “unsent”. Users should be able to manually resend unsent messages.

Users may type several messages at once and send them all in quick succession. Clients SHOULD preserve the order in which they were sent by the user. This means that clients should wait for the response to the previous request before sending the next request. This can lead to head-of-line blocking. In order to reduce the impact of head-of-line blocking, clients should use a queue per room rather than a global queue, as ordering is only relevant within a single room rather than between rooms.

Local echo

Messages SHOULD appear immediately in the message view when a user presses the “send” button. This should occur even if the message is still sending. This is referred to as “local echo”. Clients SHOULD implement “local echo” of messages. Clients MAY display messages in a different format to indicate that the server has not processed the message. This format should be removed when the server responds.

Clients need to be able to match the message they are sending with the same message which they receive from the event stream. The echo of the same message from the event stream is referred to as “remote echo”. Both echoes need to be identified as the same message in order to prevent duplicate messages being displayed. Ideally this pairing would occur transparently to the user: the UI would not flicker as it transitions from local to remote. Flickering can be reduced through clients making use of the transaction ID they used to send a particular event. The transaction ID used will be included in the event’s unsigned data as transaction_id when it arrives through the event stream.

Clients unable to make use of the transaction ID are likely to experience flickering when the remote echo arrives on the event stream before the request to send the message completes. In that case the event arrives before the client has obtained an event ID, making it impossible to identify it as a remote echo. This results in the client displaying the message twice for some time (depending on the server responsiveness) before the original request to send the message completes. Once it completes, the client can take remedial actions to remove the duplicate event by looking for duplicate event IDs.

Calculating the display name for a user

Clients may wish to show the human-readable display name of a room member as part of a membership list, or when they send a message. However, different members may have conflicting display names. Display names MUST be disambiguated before showing them to the user, in order to prevent spoofing of other users.

To ensure this is done consistently across clients, clients SHOULD use the following algorithm to calculate a disambiguated display name for a given user:

  1. Inspect the m.room.member state event for the relevant user id.
  2. If the m.room.member state event has no displayname field, or if that field has a null value, use the raw user id as the display name. Otherwise:
  3. If the m.room.member event has a displayname which is unique among members of the room with membership: join or membership: invite, use the given displayname as the user-visible display name. Otherwise:
  4. The m.room.member event has a non-unique displayname. This should be disambiguated using the user id, for example “display name (@id:homeserver.org)”.

Developers should take note of the following when implementing the above algorithm:

  • The user-visible display name of one member can be affected by changes in the state of another member. For example, if @user1:matrix.org is present in a room, with displayname: Alice, then when @user2:example.com joins the room, also with displayname: Alice, both users must be given disambiguated display names. Similarly, when one of the users then changes their display name, there is no longer a clash, and both users can be given their chosen display name. Clients should be alert to this possibility and ensure that all affected users are correctly renamed.
  • The display name of a room may also be affected by changes in the membership list. This is due to the room name sometimes being based on user display names (see Calculating the display name for a room).
  • If the entire membership list is searched for clashing display names, this leads to an O(N^2) implementation for building the list of room members. This will be very inefficient for rooms with large numbers of members. It is recommended that client implementations maintain a hash table mapping from displayname to a list of room members using that name. Such a table can then be used for efficient calculation of whether disambiguation is needed.
Displaying membership information with messages

Clients may wish to show the display name and avatar URL of the room member who sent a message. This can be achieved by inspecting the m.room.member state event for that user ID (see Calculating the display name for a user).

When a user paginates the message history, clients may wish to show the historical display name and avatar URL for a room member. This is possible because older m.room.member events are returned when paginating. This can be implemented efficiently by keeping two sets of room state: old and current. As new events arrive and/or the user paginates back in time, these two sets of state diverge from each other. New events update the current state and paginated events update the old state. When paginated events are processed sequentially, the old state represents the state of the room at the time the event was sent. This can then be used to set the historical display name and avatar URL.

Calculating the display name for a room

Clients may wish to show a human-readable name for a room. There are a number of possibilities for choosing a useful name. To ensure that rooms are named consistently across clients, clients SHOULD use the following algorithm to choose a name:

  1. If the room has an m.room.name state event with a non-empty name field, use the name given by that field.
  2. If the room has an m.room.canonical_alias state event with a valid alias field, use the alias given by that field as the name. Note that clients should avoid using alt_aliases when calculating the room name.
  3. If none of the above conditions are met, a name should be composed based on the members of the room. Clients should consider m.room.member events for users other than the logged-in user, as defined below.
    1. If the number of m.heroes for the room are greater or equal to m.joined_member_count + m.invited_member_count - 1, then use the membership events for the heroes to calculate display names for the users (disambiguating them if required) and concatenating them. For example, the client may choose to show “Alice, Bob, and Charlie (@charlie:example.org)” as the room name. The client may optionally limit the number of users it uses to generate a room name.
    2. If there are fewer heroes than m.joined_member_count + m.invited_member_count - 1, and m.joined_member_count + m.invited_member_count is greater than 1, the client should use the heroes to calculate display names for the users (disambiguating them if required) and concatenating them alongside a count of the remaining users. For example, “Alice, Bob, and 1234 others”.
    3. If m.joined_member_count + m.invited_member_count is less than or equal to 1 (indicating the member is alone), the client should use the rules above to indicate that the room was empty. For example, “Empty Room (was Alice)”, “Empty Room (was Alice and 1234 others)”, or “Empty Room” if there are no heroes.

Clients SHOULD internationalise the room name to the user’s language when using the m.heroes to calculate the name. Clients SHOULD use minimum 5 heroes to calculate room names where possible, but may use more or less to fit better with their user experience.

Spoiler messages

[Added in v1.1]

Parts of a message can be hidden visually from the user through use of spoilers. This does not affect the server’s representation of the event content - it is simply a visual cue to the user that the message may reveal important information about something, spoiling any relevant surprise.

To send spoilers clients MUST use the formatted_body and therefore the org.matrix.custom.html format, described above. This makes spoilers valid on any msgtype which can support this format appropriately.

Spoilers themselves are contained with span tags, with the reason (optionally) being in the data-mx-spoiler attribute. Spoilers without a reason must at least specify the attribute, though the value may be empty/undefined.

An example of a spoiler is:

{
  "msgtype": "m.text",
  "format": "org.matrix.custom.html",
  "body": "Alice [Spoiler](mxc://example.org/abc123) in the movie.",
  "formatted_body": "Alice <span data-mx-spoiler>lived happily ever after</span> in the movie."
}

If a reason were to be supplied, it would look like:

{
  "msgtype": "m.text",
  "format": "org.matrix.custom.html",
  "body": "Alice [Spoiler for health of Alice](mxc://example.org/abc123) in the movie.",
  "formatted_body": "Alice <span data-mx-spoiler='health of alice'>lived happily ever after</span> in the movie."
}

When sending a spoiler, clients SHOULD provide the fallback in the body as shown above (including the reason). The fallback SHOULD NOT include the text containing spoilers since body might show up in text-only clients or in notifications. To prevent spoilers showing up in such situations, clients are strongly encouraged to first upload the text containing spoilers to the media repository, then reference the mxc:// URI in a markdown-style link, as shown above.

Clients SHOULD render spoilers differently with some sort of disclosure. For example, the client could blur the actual text and ask the user to click on it for it to be revealed.

Media captions

[Added in v1.10]

Media messages, comprised of m.image, m.file, m.audio and m.video, can include a caption to convey additional information about the media.

To send captions, clients MUST use the filename and the body, and optionally the formatted_body with the org.matrix.custom.html format, described above.

If the filename is present, and its value is different than body, then body is considered to be a caption, otherwise body is a filename. format and formatted_body are only used for captions.

In previous versions of the specification, body was usually used to set the filename of the uploaded file, and filename was only present on m.file with the same purpose.

An example of a media message with a caption is:

{
    "msgtype": "m.image",
    "url": "mxc://example.org/abc123",
    "filename": "dog.jpg",
    "body": "this is a ~~cat~~ picture :3",
    "format": "org.matrix.custom.html",
    "formatted_body": "this is a <s>cat</s> picture :3",
    "info": {
        "w": 479,
        "h": 640,
        "mimetype": "image/jpeg",
        "size": 27253
    },
    "m.mentions": {}
}

Clients MUST render the caption alongside the media and SHOULD prefer its formatted representation.

Mathematical messages

[Added in v1.11]

Users might want to send mathematical notations in their messages.

To send mathematical notations clients MUST use the formatted_body and therefore the org.matrix.custom.html format, described above. This makes mathematical notations valid on any msgtype which can support this format appropriately.

Mathematical notations themselves use the span or div tags, depending whether the notation should be presented inline or not. The mathematical notation is written in LaTeX format using the data-mx-maths attribute.

The contents of the tag should be a fallback representation for clients that cannot render the LaTeX format. The fallback representation could be, for example, an image, or an HTML approximation, or the raw LaTeX source. When using an image as a fallback, the sending client should be aware of issues that may arise from the receiving client using a different background colour. The body should include a textual representation of the notation.

An example of a mathematical notation is:

{
  "msgtype": "m.text",
  "format": "org.matrix.custom.html",
  "body": "This is an equation: sin(x)=a/b.",
  "formatted_body": "This is an equation:
      <span data-mx-maths=\"\\sin(x)=\\frac{a}{b}\">
        sin(<i>x</i>)=<sup><i>a</i></sup>/<sub><i>b</i></sub>
      </span>"
}

The LaTeX format is poorly defined and has several extensions, so if a client encounters syntax that it cannot render, it SHOULD present the fallback representation instead. Clients SHOULD, however, aim to support, at minimum, the basic LaTeX2e maths commands and the TeX maths commands, with the possible exception of commands that could be security risks.

In general, LaTeX places a heavy burden on client authors to ensure that it is processed safely. Certain commands, such as those that can create macros, are potentially dangerous. Clients should either decline to process those commands, or should take care to ensure that they are handled in safe ways (such as by limiting recursion). In general, LaTeX commands should be filtered by allowing known-good commands rather than forbidding known-bad commands.

Therefore, clients should not render mathematics by calling a LaTeX compiler without proper sandboxing, as those executables were not written to handle untrusted input. Some LaTeX rendering libraries are better suited for that by allowing only a subset of LaTeX and enforcing recursion limits.

Server behaviour

Homeservers SHOULD reject m.room.message events which don’t have a msgtype key, or which don’t have a textual body key, with an HTTP status code of 400.

Security considerations

Messages sent using this module are not encrypted, although end to end encryption is in development (see E2E module).

Clients should sanitise all displayed keys for unsafe HTML to prevent Cross-Site Scripting (XSS) attacks. This includes room names and topics.

Rich replies

Rich replies are a special kind of relationship which effectively quotes the referenced event for the client to render/process how it wishes. They are normally used with m.room.message events.

[Changed in v1.3] Until v1.3 of the spec, rich replies were limited to m.room.message events which could represent an HTML-formatted body. As of v1.3 this is now expanded to all event types by dropping the requirement that an HTML-formatted body be included.

Additionally, a rich reply can reference any other event type as of v1.3. Previously, a rich reply could only reference another m.room.message event.

[Changed in v1.13] In previous versions of the specification, rich replies could include a fallback representation of the original message message in the body (using a prefix sequence) and formatted_body (using a custom HTML element) for clients that do not support rich replies. This is no longer the case, but clients SHOULD still remove this fallback before rendering the event.

To strip the fallback on the body, the client should iterate over each line of the string, removing any lines that start with the fallback prefix sequence (> , including the trailing space) and stopping when a line is encountered without the prefix.

To strip the fallback on the formatted_body of an m.room.message event with a format of org.matrix.custom.html: if theformatted_body begins with an <mx-reply> start tag, the client should remove the entirety of the <mx-reply> element.

Though rich replies form a relationship to another event, they do not use rel_type to create this relationship. Instead, a subkey named m.in_reply_to is used to describe the reply’s relationship, leaving the other properties of m.relates_to to describe the primary relationship of the event. This means that if an event is simply in reply to another event, without further relationship, the rel_type and event_id properties of m.relates_to become optional.

An example reply would be:

{
  "content": {
    "m.relates_to": {
      "m.in_reply_to": {
        "event_id": "$another_event"
      }
    },
    "body": "That sounds like a great idea!"
  },
  // other fields as required by events
}

Note that the event_id of the m.in_reply_to object has the same requirements as if it were to be under m.relates_to directly instead.

Mentioning the replied to user

In order to notify users of the reply, it may be desirable to include the sender of the replied to event and any users mentioned in that event. See user and room mentions for additional information.

An example including mentioning the original sender and other users:

{
  "content": {
    "m.relates_to": {
      "m.in_reply_to": {
        "event_id": "$another_event"
      }
    },
    "body": "That sounds like a great idea!",
    "m.mentions": {
      "user_ids": [
        // The sender of $another_event.
        "@alice:example.org",
        // Another Matrix ID copied from the m.mentions property of $another_event.
        "@bob:example.org"
      ]
    }
  },
  // other fields as required by events
}

Voice over IP

This module outlines how two users in a room can set up a Voice over IP (VoIP) call to each other. Voice and video calls are built upon the WebRTC 1.0 standard. Call signalling is achieved by sending message events to the room. In this version of the spec, only two-party communication is supported (e.g. between two peers, or between a peer and a multi-point conferencing unit). Calls can take place in rooms with multiple members, but only two devices can take part in the call.

All VoIP events have a version field. This is used to determine whether devices support this new version of the protocol. For example, clients can use this field to know whether to expect an m.call.select_answer event from their opponent. If clients see events with version other than 0 or "1" (including, for example, the numeric value 1), they should treat these the same as if they had version == "1".

Note that this implies any and all future versions of VoIP events should be backwards-compatible. If it does become necessary to introduce a non backwards-compatible VoIP spec, the intention would be for it to simply use a separate set of event types.

Party Identifiers

Whenever a client first participates in a new call, it generates a party_id for itself to use for the duration of the call. This needs to be long enough that the chance of a collision between multiple devices both generating an answer at the same time generating the same party ID is vanishingly small: 8 uppercase + lowercase alphanumeric characters is recommended. Parties in the call are identified by the tuple of (user_id, party_id).

The client adds a party_id field containing this ID to the top-level of the content of all VoIP events it sends on the call, including m.call.invite. Clients use this to identify remote echo of their own events: since a user may call themselves, they cannot simply ignore events from their own user. This field also identifies different answers sent by different clients to an invite, and matches m.call.candidates events to their respective answer/invite.

A client implementation may choose to use the device ID used in end-to-end cryptography for this purpose, or it may choose, for example, to use a different one for each call to avoid leaking information on which devices were used in a call (in an unencrypted room) or if a single device (ie. access token) were used to send signalling for more than one call party.

A grammar for party_id is defined below.

Politeness

In line with WebRTC perfect negotiation there are rules to establish which party is polite in the process of renegotiation. The callee is always the polite party. In a glare situation, the politeness of a party is therefore determined by whether the inbound or outbound call is used: if a client discards its outbound call in favour of an inbound call, it becomes the polite party.

Call Event Liveness

m.call.invite contains a lifetime field that indicates how long the offer is valid for. When a client receives an invite, it should use the event’s age field in the sync response plus the time since it received the event from the homeserver to determine whether the invite is still valid. The use of the age field ensures that incorrect clocks on client devices don’t break calls.

If the invite is still valid and will remain valid for long enough for the user to accept the call, it should signal an incoming call. The amount of time allowed for the user to accept the call may vary between clients. For example, it may be longer on a locked mobile device than on an unlocked desktop device.

The client should only signal an incoming call in a given room once it has completed processing the entire sync response and, for encrypted rooms, attempted to decrypt all encrypted events in the sync response for that room. This ensures that if the sync response contains subsequent events that indicate the call has been hung up, rejected, or answered elsewhere, the client does not signal it.

If on startup, after processing locally stored events, the client determines that there is an invite that is still valid, it should still signal it but only after it has completed a sync from the homeserver.

The minimal recommended lifetime is 90 seconds - this should give the user enough time to actually pick up the call.

ICE Candidate Batching

Clients should aim to send a small number of candidate events, with guidelines:

  • ICE candidates which can be discovered immediately or almost immediately in the invite/answer event itself (eg. host candidates). If server reflexive or relay candidates can be gathered in a sufficiently short period of time, these should be sent here too. A delay of around 200ms is suggested as a starting point.
  • The client should then allow some time for further candidates to be gathered in order to batch them, rather than sending each candidate as it arrives. A starting point of 2 seconds after sending the invite or 500ms after sending the answer is suggested as a starting point (since a delay is natural anyway after the invite whilst the client waits for the user to accept it).

End-of-candidates

An ICE candidate whose value is the empty string means that no more ICE candidates will be sent. Clients must send such a candidate in an m.call.candidates message. The WebRTC spec requires browsers to generate such a candidate, however note that at time of writing, not all browsers do (Chrome does not, but does generate an icegatheringstatechange event). The client should send any remaining candidates once candidate generation finishes, ignoring timeouts above. This allows bridges to batch the candidates together when bridging to protocols that don’t support trickle ICE.

DTMF

Matrix clients can send DTMF as specified by WebRTC. The WebRTC standard as of August 2020 does not support receiving DTMF but a Matrix client can receive and interpret the DTMF sent in the RTP payload.

Grammar for VoIP IDs

call_ids and party_id must follow the Opaque Identifier Grammar.

Behaviour on Room Leave

If the client sees the user it is in a call with leave the room, the client should treat this as a hangup event for any calls that are in progress. No specific requirement is given for the situation where a client has sent an invite and the invitee leaves the room, but the client may wish to treat it as a rejection if there are no more users in the room who could answer the call (eg. the user is now alone or the invitee field was set on the invite).

The same behaviour applies when a client is looking at historic calls.

Supported Codecs

The Matrix spec does not mandate particular audio or video codecs, but instead defers to the WebRTC spec. A compliant Matrix VoIP client will behave in the same way as a supported ‘browser’ in terms of what codecs it supports and what variants thereof. The latest WebRTC specification applies, so clients should keep up to date with new versions of the WebRTC specification whether or not there have been any changes to the Matrix spec.

Events

Common Fields

Call event


The content of all call events shares a set of common fields: those of room events and some additional VoIP specific fields.

Name Type Description
call_id string

Required: The ID of the call this event relates to.

party_id string

Required: This identifies the party that sent this event. A client may choose to re-use the device ID from end-to-end cryptography for the value of this field.

Added in v1.7

version string

Required: The version of the VoIP specification this message adheres to. This specification is version 1. This field is a string such that experimental implementations can use non-integer versions. This field was an integer in the previous spec version and implementations must accept an integer 0.

Events

m.call.invite


This event is sent by the caller when they wish to establish a call.

Event type: Message event

Content

CallEvent
Name Type Description
call_id string

Required: The ID of the call this event relates to.

invitee string

The ID of the user being called. If omitted, any user in the room can answer.

Added in v1.7

lifetime integer

Required: The time in milliseconds that the invite is valid for. Once the invite age exceeds this value, clients should discard it. They should also no longer show the call as awaiting an answer in the UI.

offer Offer

Required: The session description object

party_id string

Required: This identifies the party that sent this event. A client may choose to re-use the device ID from end-to-end cryptography for the value of this field.

Added in v1.7

sdp_stream_metadata {string: StreamMetadata}

Metadata describing the streams that will be sent.

This is a map of stream ID to metadata about the stream.

Added in v1.10

version string

Required: The version of the VoIP specification this message adheres to. This specification is version 1. This field is a string such that experimental implementations can use non-integer versions. This field was an integer in the previous spec version and implementations must accept an integer 0.

Offer
Name Type Description
sdp string

Required: The SDP text of the session description.

type string

Required: The type of session description.

One of: [offer].

StreamMetadata
Name Type Description
audio_muted boolean

Whether the audio track in the stream is muted.

Defaults to false if not present.

Added in v1.11

purpose string

Required: The purpose of the stream.

The possible values are:

  • m.usermedia: Stream that contains the webcam and/or microphone tracks.
  • m.screenshare: Stream with the screen-sharing tracks.

One of: [m.usermedia, m.screenshare].

video_muted boolean

Whether the video track in the stream is muted.

Defaults to false if not present.

Added in v1.11

Examples

{
  "content": {
    "call_id": "12345",
    "lifetime": 60000,
    "offer": {
      "sdp": "v=0\r\no=- 6584580628695956864 2 IN IP4 127.0.0.1[...]",
      "type": "offer"
    },
    "party_id": "67890",
    "sdp_stream_metadata": {
      "271828182845": {
        "purpose": "m.screenshare"
      },
      "314159265358": {
        "purpose": "m.usermedia"
      }
    },
    "version": "1"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "type": "m.call.invite",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

m.call.candidates


This event is sent by callers after sending an invite and by the callee after answering. Its purpose is to give the other party additional ICE candidates to try using to communicate.

Event type: Message event

Content

CallEvent
Name Type Description
call_id string

Required: The ID of the call this event relates to.

candidates [Candidate]

Required: Array of objects describing the candidates.

party_id string

Required: This identifies the party that sent this event. A client may choose to re-use the device ID from end-to-end cryptography for the value of this field.

Added in v1.7

version string

Required: The version of the VoIP specification this message adheres to. This specification is version 1. This field is a string such that experimental implementations can use non-integer versions. This field was an integer in the previous spec version and implementations must accept an integer 0.

Candidate
Name Type Description
candidate string

Required: The SDP ‘a’ line of the candidate.

If this is an end-of-candidates candidate, this is the empty string.

sdpMLineIndex number

The index of the SDP ’m’ line this candidate is intended for.

At least one of sdpMid or sdpMLineIndex is required, unless this an end-of-candidates candidate.

sdpMid string

The SDP media type this candidate is intended for.

At least one of sdpMid or sdpMLineIndex is required, unless this an end-of-candidates candidate.

Examples

{
  "content": {
    "call_id": "12345",
    "candidates": [
      {
        "candidate": "candidate:863018703 1 udp 2122260223 10.9.64.156 43670 typ host generation 0",
        "sdpMLineIndex": 0,
        "sdpMid": "audio"
      }
    ],
    "party_id": "67890",
    "version": "1"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "type": "m.call.candidates",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

m.call.reject


Added in v1.7

If the m.call.invite event has version "1", a client wishing to reject the call sends an m.call.reject event. This rejects the call on all devices, but if the calling device sees an answer before the reject, it disregards the reject event and carries on. The reject has a party_id just like an answer, and the caller sends a select_answer for it just like an answer. If another client had already sent an answer and sees the caller select the reject response instead of its answer, it ends the call. If the m.call.invite event has version 0, the callee sends an m.call.hangup event. If the calling user chooses to end the call before setup is complete, the client sends m.call.hangup as previously.

Note that, unlike m.call.hangup, this event has no reason field: the rejection of a call is always implicitly because the user chose not to answer it.

Event type: Message event

Content

CallEvent
Name Type Description
call_id string

Required: The ID of the call this event relates to.

party_id string

Required: This identifies the party that sent this event. A client may choose to re-use the device ID from end-to-end cryptography for the value of this field.

Added in v1.7

version string

Required: The version of the VoIP specification this message adheres to. This specification is version 1. This field is a string such that experimental implementations can use non-integer versions. This field was an integer in the previous spec version and implementations must accept an integer 0.

Examples

{
  "content": {
    "call_id": "12345",
    "party_id": "67890",
    "version": "1"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "type": "m.call.reject",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

m.call.answer


This event is sent by the callee when they wish to answer the call.

Event type: Message event

Content

CallEvent
Name Type Description
answer Answer

Required: The session description object

call_id string

Required: The ID of the call this event relates to.

party_id string

Required: This identifies the party that sent this event. A client may choose to re-use the device ID from end-to-end cryptography for the value of this field.

Added in v1.7

sdp_stream_metadata {string: StreamMetadata}

Metadata describing the streams that will be sent.

This is a map of stream ID to metadata about the stream.

Added in v1.10

version string

Required: The version of the VoIP specification this message adheres to. This specification is version 1. This field is a string such that experimental implementations can use non-integer versions. This field was an integer in the previous spec version and implementations must accept an integer 0.

Answer
Name Type Description
sdp string

Required: The SDP text of the session description.

type string

Required: The type of session description.

One of: [answer].

StreamMetadata
Name Type Description
audio_muted boolean

Whether the audio track in the stream is muted.

Defaults to false if not present.

Added in v1.11

purpose string

Required: The purpose of the stream.

The possible values are:

  • m.usermedia: Stream that contains the webcam and/or microphone tracks.
  • m.screenshare: Stream with the screen-sharing tracks.

One of: [m.usermedia, m.screenshare].

video_muted boolean

Whether the video track in the stream is muted.

Defaults to false if not present.

Added in v1.11

Examples

{
  "content": {
    "answer": {
      "sdp": "v=0\r\no=- 6584580628695956864 2 IN IP4 127.0.0.1[...]",
      "type": "answer"
    },
    "call_id": "12345",
    "party_id": "67890",
    "sdp_stream_metadata": {
      "271828182845": {
        "purpose": "m.screenshare"
      },
      "314159265358": {
        "purpose": "m.usermedia"
      }
    },
    "version": "1"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "type": "m.call.answer",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

m.call.select_answer


Added in v1.7

This event is sent by the caller’s client once it has decided which other client to talk to, by selecting one of multiple possible incoming m.call.answer events. Its selected_party_id field indicates the answer it’s chosen. The call_id and party_id of the caller is also included. If the callee’s client sees a select_answer for an answer with party ID other than the one it sent, it ends the call and informs the user the call was answered elsewhere. It does not send any events. Media can start flowing before this event is seen or even sent. Clients that implement previous versions of this specification will ignore this event and behave as they did before.

Event type: Message event

Content

CallEvent
Name Type Description
call_id string

Required: The ID of the call this event relates to.

party_id string

Required: This identifies the party that sent this event. A client may choose to re-use the device ID from end-to-end cryptography for the value of this field.

Added in v1.7

selected_party_id string

Required: The party_id field from the answer event that the caller chose.

version string

Required: The version of the VoIP specification this message adheres to. This specification is version 1. This field is a string such that experimental implementations can use non-integer versions. This field was an integer in the previous spec version and implementations must accept an integer 0.

Examples

{
  "content": {
    "call_id": "12345",
    "party_id": "67890",
    "selected_party_id": "111213",
    "version": "1"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "type": "m.call.select_answer",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

m.call.negotiate


Added in v1.7

Provides SDP negotiation semantics for media pause, hold/resume, ICE restarts and voice/video call up/downgrading. Clients should implement and honour hold functionality as per WebRTC’s recommendation.

If both the invite event and the accepted answer event have version equal to "1", either party may send m.call.negotiate with a description field to offer new SDP to the other party. This event has call_id with the ID of the call and party_id equal to the client’s party ID for that call. The caller ignores any negotiate events with party_id + user_id tuple not equal to that of the answer it accepted and the callee ignores any negotiate events with party_id + user_id tuple not equal to that of the caller. Clients should use the party_id field to ignore the remote echo of their own negotiate events.

This has a lifetime field as in m.call.invite, after which the sender of the negotiate event should consider the negotiation failed (timed out) and the recipient should ignore it.

The description field is the same as the offer field in m.call.invite and answer field in m.call.answer and is an RTCSessionDescriptionInit object as per https://www.w3.org/TR/webrtc/#dom-rtcsessiondescriptioninit.

Once an m.call.negotiate event is received, the client must respond with another m.call.negotiate event, with the SDP answer (with "type": "answer") in the description property.

In the m.call.invite and m.call.answer events, the offer and answer fields respectively are objects of type RTCSessionDescriptionInit. Hence the type field, whilst redundant in these events, is included for ease of working with the WebRTC API and is mandatory. Receiving clients should not attempt to validate the type field, but simply pass the object into the WebRTC API.

Event type: Message event

Content

CallEvent
Name Type Description
call_id string

Required: The ID of the call this event relates to.

description Description

Required: The session description object

lifetime integer

Required: The time in milliseconds that the negotiation is valid for. Once the negotiation age exceeds this value, clients should discard it.

party_id string

Required: This identifies the party that sent this event. A client may choose to re-use the device ID from end-to-end cryptography for the value of this field.

Added in v1.7

sdp_stream_metadata {string: StreamMetadata}

Metadata describing the streams that will be sent.

This is a map of stream ID to metadata about the stream.

Added in v1.10

version string

Required: The version of the VoIP specification this message adheres to. This specification is version 1. This field is a string such that experimental implementations can use non-integer versions. This field was an integer in the previous spec version and implementations must accept an integer 0.

Description
Name Type Description
sdp string

Required: The SDP text of the session description.

type string

Required: The type of session description.

One of: [offer, answer].

StreamMetadata
Name Type Description
audio_muted boolean

Whether the audio track in the stream is muted.

Defaults to false if not present.

Added in v1.11

purpose string

Required: The purpose of the stream.

The possible values are:

  • m.usermedia: Stream that contains the webcam and/or microphone tracks.
  • m.screenshare: Stream with the screen-sharing tracks.

One of: [m.usermedia, m.screenshare].

video_muted boolean

Whether the video track in the stream is muted.

Defaults to false if not present.

Added in v1.11

Examples

{
  "content": {
    "call_id": "12345",
    "description": {
      "sdp": "v=0\r\no=- 6584580628695956864 2 IN IP4 127.0.0.1[...]",
      "type": "offer"
    },
    "lifetime": 10000,
    "party_id": "67890",
    "sdp_stream_metadata": {
      "271828182845": {
        "purpose": "m.screenshare"
      },
      "314159265358": {
        "purpose": "m.usermedia"
      }
    },
    "version": "1"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "type": "m.call.negotiate",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

m.call.sdp_stream_metadata_changed


Added in v1.11

This event is sent by callers when they wish to update a stream’s metadata but no negotiation is required.

Event type: Message event

Content

CallEvent
Name Type Description
call_id string

Required: The ID of the call this event relates to.

party_id string

Required: This identifies the party that sent this event. A client may choose to re-use the device ID from end-to-end cryptography for the value of this field.

Added in v1.7

sdp_stream_metadata {string: StreamMetadata}

Required: Metadata describing the streams that will be sent.

This is a map of stream ID to metadata about the stream.

Added in v1.10

version string

Required: The version of the VoIP specification this message adheres to. This specification is version 1. This field is a string such that experimental implementations can use non-integer versions. This field was an integer in the previous spec version and implementations must accept an integer 0.

StreamMetadata
Name Type Description
audio_muted boolean

Whether the audio track in the stream is muted.

Defaults to false if not present.

Added in v1.11

purpose string

Required: The purpose of the stream.

The possible values are:

  • m.usermedia: Stream that contains the webcam and/or microphone tracks.
  • m.screenshare: Stream with the screen-sharing tracks.

One of: [m.usermedia, m.screenshare].

video_muted boolean

Whether the video track in the stream is muted.

Defaults to false if not present.

Added in v1.11

Examples

{
  "content": {
    "call_id": "1414213562373095",
    "party_id": "1732050807568877",
    "sdp_stream_metadata": {
      "2311546231": {
        "audio_muted:": true,
        "purpose": "m.usermedia",
        "video_muted": true
      }
    },
    "version": "1"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "type": "m.call.sdp_stream_metadata_changed",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

m.call.hangup


Sent by either party to signal their termination of the call. This can be sent either once the call has has been established or before to abort the call.

The meanings of the reason field are as follows:

  • ice_failed: ICE negotiation has failed and a media connection could not be established.
  • ice_timeout: The connection failed after some media was exchanged (as opposed to ice_failed which means no media connection could be established). Note that, in the case of an ICE renegotiation, a client should be sure to send ice_timeout rather than ice_failed if media had previously been received successfully, even if the ICE renegotiation itself failed.
  • invite_timeout: The other party did not answer in time.
  • user_hangup: Clients must now send this code when the user chooses to end the call, although for backwards compatibility with version 0, a clients should treat an absence of the reason field as user_hangup.
  • user_media_failed: The client was unable to start capturing media in such a way that it is unable to continue the call.
  • user_busy: The user is busy. Note that this exists primarily for bridging to other networks such as the PSTN. A Matrix client that receives a call whilst already in a call would not generally reject the new call unless the user had specifically chosen to do so.
  • unknown_error: Some other failure occurred that meant the client was unable to continue the call rather than the user choosing to end it.
Event type: Message event

Content

CallEvent
Name Type Description
call_id string

Required: The ID of the call this event relates to.

party_id string

Required: This identifies the party that sent this event. A client may choose to re-use the device ID from end-to-end cryptography for the value of this field.

Added in v1.7

reason string

Required: Reason for the hangup. Note that this was optional in previous previous versions of the spec, so a missing value should be treated as user_hangup.

One of: [ice_timeout, ice_failed, invite_timeout, user_hangup, user_media_failed, user_busy, unknown_error].

Changed in v1.7: Additional values were added.

version string

Required: The version of the VoIP specification this message adheres to. This specification is version 1. This field is a string such that experimental implementations can use non-integer versions. This field was an integer in the previous spec version and implementations must accept an integer 0.

Examples

{
  "content": {
    "call_id": "12345",
    "party_id": "67890",
    "reason": "user_hangup",
    "version": "1"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "type": "m.call.hangup",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

Client behaviour

A call is set up with message events exchanged as follows:

    Caller                    Callee
    [Place Call]
    m.call.invite ----------->
    m.call.candidate -------->
    [..candidates..] -------->
                            [Answers call]
           <--------------- m.call.answer
    m.call.select_answer ----------->
     [Call is active and ongoing]
           <--------------- m.call.hangup

Or a rejected call:

    Caller                      Callee
    m.call.invite ------------>
    m.call.candidate --------->
    [..candidates..] --------->
                             [Rejects call]
             <-------------- m.call.hangup

Calls are negotiated according to the WebRTC specification.

In response to an incoming invite, a client may do one of several things:

  • Attempt to accept the call by sending an m.call.answer.
  • Actively reject the call everywhere: send an m.call.reject as per above, which will stop the call from ringing on all the user’s devices and the caller’s client will inform them that the user has rejected their call.
  • Ignore the call: send no events, but stop alerting the user about the call. The user’s other devices will continue to ring, and the caller’s device will continue to indicate that the call is ringing, and will time the call out in the normal way if no other device responds.
Streams

Clients may send more than one stream in a VoIP call. The streams should be differentiated by including metadata in the m.call.invite, m.call.answer and m.call.negotiate events, using the sdp_stream_metadata property. An m.call.sdp_stream_metadata_changed event can be sent when the metadata changes but no negotiation is required.

Clients are recommended to not mute the audio of WebRTC tracks locally when an incoming stream has the audio_muted field set to true. This is because when the other user unmutes themselves, there may be a slight delay between their client sending audio and the m.call.sdp_stream_metadata_changed event arriving and any audio sent in between will not be heard. The other user will still stop transmitting audio once they mute on their side, so no audio is sent without the user’s knowledge.

The same suggestion does not apply to video_muted. Clients should mute video locally, so that the receiving side doesn’t see a black video.

If sdp_stream_metadata is present and an incoming stream is not listed in it, the stream should be ignored. If a stream has a purpose of an unknown type, it should also be ignored.

For backwards compatibility, if sdp_stream_metadata is not present in the initial m.call.invite or m.call.answer event sent by the other party, the client should assume that this property is not supported by the other party. It means that multiple streams cannot be differentiated: the client should only use the first incoming stream and shouldn’t send more than one stream.

Clients implementing this specification should ignore any streamless tracks.

Invitees

The invitee field should be added whenever the call is intended for one specific user, and should be set to the Matrix user ID of that user. Invites without an invitee field are defined to be intended for any member of the room other than the sender of the event.

Clients should consider an incoming call if they see a non-expired invite event where the invitee field is either absent or equal to their user’s Matrix ID, however they should evaluate whether or not to ring based on their user’s trust relationship with the callers and/or where the call was placed. As a starting point, it is suggested that clients ignore call invites from users in public rooms. It is strongly recommended that when clients do not ring for an incoming call invite, they still display the call invite in the room and annotate that it was ignored.

Glare

“Glare” is a problem which occurs when two users call each other at roughly the same time. This results in the call failing to set up as there already is an incoming/outgoing call. A glare resolution algorithm can be used to determine which call to hangup and which call to answer. If both clients implement the same algorithm then they will both select the same call and the call will be successfully connected.

As calls are “placed” to rooms rather than users, the glare resolution algorithm outlined below is only considered for calls which are to the same room. The algorithm is as follows:

  • If an m.call.invite to a room is received whilst the client is preparing to send an m.call.invite to the same room:
    • the client should cancel its outgoing call and instead automatically accept the incoming call on behalf of the user.
  • If an m.call.invite to a room is received after the client has sent an m.call.invite to the same room and is waiting for a response:
    • the client should perform a lexicographical comparison of the call IDs of the two calls and use the lesser of the two calls, aborting the greater. If the incoming call is the lesser, the client should accept this call on behalf of the user.

The call setup should appear seamless to the user as if they had simply placed a call and the other party had accepted. This means any media stream that had been setup for use on a call should be transferred and used for the call that replaces it.

Server behaviour

The homeserver MAY provide a TURN server which clients can use to contact the remote party. The following HTTP API endpoints will be used by clients in order to get information about the TURN server.

GET /_matrix/client/v3/voip/turnServer


This API provides credentials for the client to use when initiating calls.

Rate-limited: Yes
Requires authentication: Yes

Request

No request parameters or request body.


Responses

Status Description
200 The TURN server credentials.
429 This request was rate-limited.

200 response

Name Type Description
password string

Required: The password to use.

ttl integer

Required: The time-to-live in seconds

uris [string]

Required: A list of TURN URIs

username string

Required: The username to use.

{
  "password": "JlKfBy1QwLrO20385QyAtEyIv0=",
  "ttl": 86400,
  "uris": [
    "turn:turn.example.com:3478?transport=udp",
    "turn:10.20.30.40:3478?transport=tcp",
    "turns:10.20.30.40:443?transport=tcp"
  ],
  "username": "1443779631:@user:example.com"
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

Security considerations

Calls should only be placed to rooms with one other user in them. If they are placed to group chat rooms it is possible that another user will intercept and answer the call.

Typing Notifications

Users may wish to be informed when another user is typing in a room. This can be achieved using typing notifications. These are ephemeral events, so they do not form part of the Event Graph. Typing notifications are scoped to a room.

Events

m.typing


Informs the client of the list of users currently typing.

Event type: Message event

Content

Name Type Description
user_ids [string]

Required: The list of user IDs typing in this room, if any.

Examples

{
  "content": {
    "user_ids": [
      "@alice:matrix.org",
      "@bob:example.com"
    ]
  },
  "type": "m.typing"
}

Client behaviour

When a client receives an m.typing event, it MUST use the user ID list to REPLACE its knowledge of every user who is currently typing. The reason for this is that the server does not remember users who are not currently typing as that list gets big quickly. The client should mark as not typing any user ID who is not in that list.

It is recommended that clients store a boolean indicating whether the user is typing or not. Whilst this value is true a timer should fire periodically every N seconds to send a typing HTTP request. The value of N is recommended to be no more than 20-30 seconds. This request should be re-sent by the client to continue informing the server the user is still typing. As subsequent requests will replace older requests, a safety margin of 5 seconds before the expected timeout runs out is recommended. When the user stops typing, the state change of the boolean to false should trigger another HTTP request to inform the server that the user has stopped typing.

PUT /_matrix/client/v3/rooms/{roomId}/typing/{userId}


This tells the server that the user is typing for the next N milliseconds where N is the value specified in the timeout key. Alternatively, if typing is false, it tells the server that the user has stopped typing.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string

Required: The room in which the user is typing.

userId string

Required: The user who has started to type.

Request body

Name Type Description
timeout integer

The length of time in milliseconds to mark this user as typing.

typing boolean

Required: Whether the user is typing or not. If false, the timeout key can be omitted.

Request body example

{
  "timeout": 30000,
  "typing": true
}

Responses

Status Description
200 The new typing state was set.
429 This request was rate-limited.

200 response

{}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

Security considerations

Clients may not wish to inform everyone in a room that they are typing and instead only specific users in the room.

Receipts

[Changed in v1.4] Added private read receipts.

This module adds in support for receipts. These receipts are a form of acknowledgement of an event. This module defines the m.read receipt for indicating that the user has read up to a given event, and m.read.private to achieve the same purpose without any other user being aware. Primarily, m.read.private is intended to clear notifications without advertising read-up-to status to others.

Sending a receipt for each event can result in sending large amounts of traffic to a homeserver. To prevent this from becoming a problem, receipts are implemented using “up to” markers. This marker indicates that the acknowledgement applies to all events “up to and including” the event specified. For example, marking an event as “read” would indicate that the user had read all events up to the referenced event. See the Receiving notifications section for more information on how read receipts affect notification counts.

[Added in v1.4] Read receipts exist in three major forms:

  • Unthreaded: Denotes a read-up-to receipt regardless of threads. This is how pre-threading read receipts worked.
  • Threaded, main timeline: Denotes a read-up-to receipt for events not in a particular thread. Identified by the thread ID main.
  • Threaded, in a thread: Denotes a read-up-to receipt within a particular thread. Identified by the event ID of the thread root.

Threaded read receipts are discussed in further detail below.

Events

[Changed in v1.4] Each user_id, receipt_type, and categorisation (unthreaded, or thread_id) tuple must be associated with only a single event_id.

m.receipt


Changed in v1.4: Added m.read.private receipts to the event’s content.

Informs the client of new receipts.

Event type: Message event

Content

Type Description
{Event ID: Event Receipts}

The mapping of event ID to a collection of receipts for this event ID. The event ID is the ID of the event being acknowledged and not an ID for the receipt itself.

Event Receipts
Name Type Description
m.read {User ID: Receipt}

A collection of users who have sent m.read receipts for this event. The string key is the user ID the receipt belongs to.

m.read.private {User ID: Receipt}

Similar to m.read, the users who have sent m.read.private receipts for this event. Due to the nature of private read receipts, this should only ever have the current user’s ID.

Receipt
Name Type Description
thread_id string

The root thread event’s ID (or main) for which thread this receipt is intended to be under. If not specified, the read receipt is unthreaded (default).

Added in v1.4

ts integer

The timestamp the receipt was sent at.

Examples

{
  "content": {
    "$1435641916114394fHBLK:matrix.org": {
      "m.read": {
        "@erikj:jki.re": {
          "ts": 1436451550453
        }
      },
      "m.read.private": {
        "@self:example.org": {
          "ts": 1661384801651
        }
      }
    }
  },
  "type": "m.receipt"
}

Client behaviour

[Changed in v1.4] Altered to support threaded read receipts.

In /sync, receipts are listed under the ephemeral array of events for a given room. New receipts that come down the event streams are deltas which update existing mappings. Clients should replace older receipt acknowledgements based on user_id, receipt_type, and the thread_id (if present). For example:

Client receives m.receipt:
  user = @alice:example.com
  receipt_type = m.read
  event_id = $aaa:example.com
  thread_id = undefined

Client receives another m.receipt:
  user = @alice:example.com
  receipt_type = m.read
  event_id = $bbb:example.com
  thread_id = main

The client does not replace any acknowledgements, yet.

Client receives yet another m.receipt:
  user = @alice:example.com
  receipt_type = m.read
  event_id = $ccc:example.com
  thread_id = undefined

The client replaces the older acknowledgement for $aaa:example.com
with this new one for $ccc:example.com, but does not replace the
acknowledgement for $bbb:example.com because it belongs to a thread.

Client receives yet another m.receipt:
  user = @alice:example.com
  receipt_type = m.read
  event_id = $ddd:example.com
  thread_id = main

Now the client replaces the older $bbb:example.com acknowledgement with
this new $ddd:example.com acknowledgement. The client does NOT replace the
older acknowledgement for $ccc:example.com as it is unthreaded.

Clients should send read receipts when there is some certainty that the event in question has been displayed to the user. Simply receiving an event does not provide enough certainty that the user has seen the event. The user SHOULD need to take some action such as viewing the room that the event was sent to or dismissing a notification in order for the event to count as “read”. Clients SHOULD NOT send read receipts for events sent by their own user.

Similar to the rules for sending receipts, threaded receipts should appear in the context of the thread. If a thread is rendered behind a disclosure, the client hasn’t yet shown the event (or any applicable read receipts) to the user. Once they expand the thread though, a threaded read receipt would be sent and per-thread receipts from other users shown.

A client can update the markers for its user by interacting with the following HTTP APIs.

POST /_matrix/client/v3/rooms/{roomId}/receipt/{receiptType}/{eventId}


This API updates the marker for the given receipt type to the event ID specified.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
eventId string

Required: The event ID to acknowledge up to.

receiptType string

Required: The type of receipt to send. This can also be m.fully_read as an alternative to /read_markers.

Note that m.fully_read does not appear under m.receipt: this endpoint effectively calls /read_markers internally when presented with a receipt type of m.fully_read.

One of: [m.read, m.read.private, m.fully_read].

Changed in v1.4: Allow m.read.private receipts and m.fully_read markers to be set.

roomId string

Required: The room in which to send the event.

Request body

Name Type Description
thread_id string

The root thread event’s ID (or main) for which thread this receipt is intended to be under. If not specified, the read receipt is unthreaded (default).

Added in v1.4

Request body example

{
  "thread_id": "main"
}

Responses

Status Description
200 The receipt was sent.
400

The thread_id is invalid in some way. For example:

  • It is not a string.
  • It is empty.
  • It is provided for an incompatible receipt type.
  • The event_id is not related to the thread_id.
429 This request was rate-limited.

200 response

{}

400 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_INVALID_PARAM",
  "error": "thread_id field must be a non-empty string"
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}
Private read receipts

[Added in v1.4]

Some users would like to mark a room as read, clearing their notification counts, but not give away the fact that they’ve read a particular message yet. To achieve this, clients can send m.read.private receipts instead of m.read to do exactly that: clear notifications and not broadcast the receipt to other users.

Servers MUST NOT send the m.read.private receipt to any other user than the one which originally sent it.

Between m.read and m.read.private, the receipt which is more “ahead” or “recent” is used when determining the highest read-up-to mark. See the notifications section for more information on how this affects notification counts.

If a client sends an m.read receipt which is “behind” the m.read.private receipt, other users will see that change happen but the sending user will not have their notification counts rewound to that point in time. While uncommon, it is considered valid to have an m.read (public) receipt lag several messages behind the m.read.private receipt, for example.

Threaded read receipts

[Added in v1.4]

If a client does not use threading, then they will simply only send “unthreaded” read receipts which affect the whole room regardless of threads.

A threaded read receipt is simply one which has a thread_id on it, targeting either a thread root’s event ID or main for the main timeline.

Threading introduces a concept of multiple conversations being held in the same room and thus deserve their own read receipts and notification counts. An event is considered to be “in a thread” if:

  • It has a rel_type of m.thread, or
  • Following the event relationships, it has a parent event which references the thread root with a rel_type of m.thread. Implementations should not recurse infinitely, though: a maximum of 3 hops is recommended to cover indirect relationships.

Events not in a thread but still in the room are considered to be in the “main timeline”. When referring to the main timeline as a thread (e.g. in receipts and notifications counts) a special thread ID of main is used.

Thread roots are considered to be in the main timeline, as are events that are related to a thread root via non-thread relations.

The following is an example DAG for a room, with dotted lines showing event relationships and solid lines showing topological ordering.

Diagram presenting a DAG with thread relationships as a single timeline

This DAG can be represented as 3 threaded timelines, with A and B being thread roots:

Diagram presenting a DAG with thread relationships as 3 related timelines

With this, we can demonstrate that:

  • A threaded read receipt on I would mark A, B, and I as read.
  • A threaded read receipt on E would mark C and E as read.
  • An unthreaded read receipt on D would mark A, B, C, and D as read.

Note that marking A as read with a threaded read receipt would not mean that C, E, G, or H get marked as read: Thread A’s timeline would need its own threaded read receipt at H to accomplish that.

The read receipts for the above 3 examples would be:

{
  "$I": {
    "m.read": {
      "@user:example.org": {
        "ts": 1661384801651,
        "thread_id": "main" // because `I` is not in a thread, but is a threaded receipt
      }
    }
  },
  "$E": {
    "m.read": {
      "@user:example.org": {
        "ts": 1661384801651,
        "thread_id": "$A" // because `E` is in Thread `A`
      }
    }
  },
  "$D": {
    "m.read": {
      "@user:example.org": {
        "ts": 1661384801651
        // no `thread_id` because the receipt is *unthreaded*
      }
    }
  }
}

Conditions on sending read receipts apply similarly to threaded and unthreaded read receipts. For example, a client might send a private read receipt for a threaded event when the user expands that thread.

Server behaviour

For efficiency, receipts SHOULD be batched into one event per room and thread before delivering them to clients.

Some receipts are sent across federation as EDUs with type m.receipt. The format of the EDUs are:

{
    <room_id>: {
        <receipt_type>: {
            <user_id>: { <content (ts & thread_id, currently)> }
        },
        ...
    },
    ...
}

These are always sent as deltas to previously sent receipts. Currently only a single <receipt_type> should be used: m.read. m.read.private MUST NOT appear in this federated m.receipt EDU.

Security considerations

As receipts are sent outside the context of the event graph, there are no integrity checks performed on the contents of m.receipt events.

Read and unread markers

Fully read markers

The history for a given room may be split into three sections: messages the user has read (or indicated they aren’t interested in them), messages the user might have read some but not others, and messages the user hasn’t seen yet. The “fully read marker” (also known as a “read marker”) marks the last event of the first section, whereas the user’s read receipt marks the last event of the second section.

Events

The user’s fully read marker is kept as an event in the room’s account data. The event may be read to determine the user’s current fully read marker location in the room, and just like other account data events the event will be pushed down the event stream when updated.

The fully read marker is kept under an m.fully_read event. If the event does not exist on the user’s account data, the fully read marker should be considered to be the user’s read receipt location.

m.fully_read


The current location of the user’s read marker in a room. This event appears in the user’s room account data for the room the marker is applicable for.

Event type: Message event

Content

Name Type Description
event_id string

Required: The event the user’s read marker is located at in the room.

Examples

{
  "content": {
    "event_id": "$someplace:example.org"
  },
  "type": "m.fully_read"
}
Client behaviour

The client cannot update fully read markers by directly modifying the m.fully_read account data event. Instead, the client must make use of the read markers API to change the values.

[Changed in v1.4] m.read.private receipts can now be sent from /read_markers.

The read markers API can additionally update the user’s read receipt (m.read or m.read.private) location in the same operation as setting the fully read marker location. This is because read receipts and read markers are commonly updated at the same time, and therefore the client might wish to save an extra HTTP call. Providing m.read and/or m.read.private performs the same task as a request to /receipt/{receiptType}/{eventId}.

POST /_matrix/client/v3/rooms/{roomId}/read_markers


Sets the position of the read marker for a given room, and optionally the read receipt’s location.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string

Required: The room ID to set the read marker in for the user.

Request body

Name Type Description
m.fully_read string

The event ID the read marker should be located at. The event MUST belong to the room.

Changed in v1.4: This property is no longer required.

m.read string

The event ID to set the read receipt location at. This is equivalent to calling /receipt/m.read/$elsewhere:example.org and is provided here to save that extra call.

m.read.private string

The event ID to set the private read receipt location at. This equivalent to calling /receipt/m.read.private/$elsewhere:example.org and is provided here to save that extra call.

Added in v1.4

Request body example

{
  "m.fully_read": "$somewhere:example.org",
  "m.read": "$elsewhere:example.org",
  "m.read.private": "$elsewhere:example.org"
}

Responses

Status Description
200 The read marker, and read receipt(s) if provided, have been updated.
429 This request was rate-limited.

200 response

{}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}
Server behaviour

The server MUST prevent clients from setting m.fully_read directly in room account data. The server must additionally ensure that it treats the presence of m.read and m.read.private in the /read_markers request the same as how it would for a request to /receipt/{receiptType}/{eventId}.

Upon updating the m.fully_read event due to a request to /read_markers, the server MUST send the updated account data event through to the client via the event stream (eg: /sync), provided any applicable filters are also satisfied.

Unread markers

[Added in v1.12]

Clients may use “unread markers” to allow users to label rooms for later attention irrespective of read receipts or fully read markers.

Events

The user’s unread marker in a room is kept under an m.marked_unread event in the room’s account data. The event may be read to determine the user’s current unread marker state in the room. Just like other account data events, the event will be pushed down the event stream when updated.

m.marked_unread


The current state of the user’s unread marker in a room. This event appears in the user’s room account data for the room the marker is applicable for.

Event type: Message event

Content

Name Type Description
unread boolean

Required: Whether the room is marked unread or not.

Examples

{
  "content": {
    "unread": true
  },
  "type": "m.marked_unread"
}
Client behaviour

Clients MUST update unread markers by directly modifying the m.marked_unread room account data event. When marking a room as unread, clients SHOULD NOT change the m.fully_read marker, so that the user’s read position in the room is retained.

When the unread field is true, clients SHOULD visually annotate the room to indicate that it is unread. Exactly how this is achieved is left as an implementation detail. It is RECOMMENDED that clients use a treatment similar to how they represent rooms with unread notifications.

Clients SHOULD reset the unread marker by setting unread to false when opening a room to display its timeline.

Clients that offer functionality to mark a room as read by sending a read receipt for the last event, SHOULD reset the unread marker simultaneously.

If the m.marked_unread event does not exist on the user’s account data, clients MUST behave as if unread was false.

Server behaviour

Servers have no additional requirements placed on them by this submodule.

Presence

Each user has the concept of presence information. This encodes:

  • Whether the user is currently online
  • How recently the user was last active (as seen by the server)
  • Whether a given client considers the user to be currently idle
  • Arbitrary information about the user’s current status (e.g. “in a meeting”).

This information is collated from both per-device (online, idle, last_active) and per-user (status) data, aggregated by the user’s homeserver and transmitted as an m.presence event. Presence events are sent to interested parties where users share a room membership.

User’s presence state is represented by the presence key, which is an enum of one of the following:

  • online : The default state when the user is connected to an event stream.
  • unavailable : The user is not reachable at this time e.g. they are idle.
  • offline : The user is not connected to an event stream or is explicitly suppressing their profile information from being sent.

Events

m.presence


Informs the client of a user’s presence state change.

Event type: Message event

Content

Name Type Description
avatar_url string

The current avatar URL for this user, if any.

currently_active boolean

Whether the user is currently active

displayname string

The current display name for this user, if any.

last_active_ago number

The last time since this used performed some action, in milliseconds.

presence string

Required: The presence state for this user.

One of: [online, offline, unavailable].

status_msg string

An optional description to accompany the presence.

Examples

{
  "content": {
    "avatar_url": "mxc://localhost/wefuiwegh8742w",
    "currently_active": false,
    "last_active_ago": 2478593,
    "presence": "online",
    "status_msg": "Making cupcakes"
  },
  "sender": "@example:localhost",
  "type": "m.presence"
}

Client behaviour

Clients can manually set/get their presence using the HTTP APIs listed below.

GET /_matrix/client/v3/presence/{userId}/status


Get the given user’s presence state.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
userId string

Required: The user whose presence state to get.


Responses

Status Description
200 The presence state for this user.
403 You are not allowed to see this user’s presence status.
404 There is no presence state for this user. This user may not exist or isn’t exposing presence information to you.

200 response

Name Type Description
currently_active boolean

Whether the user is currently active

last_active_ago integer

The length of time in milliseconds since an action was performed by this user.

presence string

Required: This user’s presence.

One of: [online, offline, unavailable].

status_msg string|null

The state message for this user if one was set.

{
  "last_active_ago": 420845,
  "presence": "unavailable"
}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_FORBIDDEN",
  "error": "You are not allowed to see their presence"
}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_UNKNOWN",
  "error": "An unknown error occurred"
}

PUT /_matrix/client/v3/presence/{userId}/status


This API sets the given user’s presence state. When setting the status, the activity time is updated to reflect that activity; the client does not need to specify the last_active_ago field. You cannot set the presence state of another user.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
userId string

Required: The user whose presence state to update.

Request body

Name Type Description
presence string

Required: The new presence state.

One of: [online, offline, unavailable].

status_msg string

The status message to attach to this state.

Request body example

{
  "presence": "online",
  "status_msg": "I am here."
}

Responses

Status Description
200 The new presence state was set.
429 This request was rate-limited.

200 response

{}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}
Last active ago

The server maintains a timestamp of the last time it saw a pro-active event from the user. A pro-active event may be sending a message to a room or changing presence state to online. This timestamp is presented via a key called last_active_ago which gives the relative number of milliseconds since the pro-active event.

To reduce the number of presence updates sent to clients the server may include a currently_active boolean field when the presence state is online. When true, the server will not send further updates to the last active time until an update is sent to the client with either a) currently_active set to false or b) a presence state other than online. During this period clients must consider the user to be currently active, irrespective of the last active time.

The last active time must be up to date whenever the server gives a presence event to the client. The currently_active mechanism should purely be used by servers to stop sending continuous presence updates, as opposed to disabling last active tracking entirely. Thus clients can fetch up to date last active times by explicitly requesting the presence for a given user.

Idle timeout

The server will automatically set a user’s presence to unavailable if their last active time was over a threshold value (e.g. 5 minutes). Clients can manually set a user’s presence to unavailable. Any activity that bumps the last active time on any of the user’s clients will cause the server to automatically set their presence to online.

Security considerations

Presence information is shared with all users who share a room with the target user. In large public rooms this could be undesirable.

Content repository

The content repository (or “media repository”) allows users to upload files to their homeserver for later use. For example, files which the user wants to send to a room would be uploaded here, as would an avatar the user wants to use.

Uploads are POSTed to a resource on the user’s local homeserver which returns an mxc:// URI which can later be used to GET the download. Content is downloaded from the recipient’s local homeserver, which must first transfer the content from the origin homeserver using the same API (unless the origin and destination homeservers are the same).

When serving content, the server SHOULD provide a Content-Security-Policy header. The recommended policy is sandbox; default-src 'none'; script-src 'none'; plugin-types application/pdf; style-src 'unsafe-inline'; object-src 'self';.

[Added in v1.4] The server SHOULD additionally provide Cross-Origin-Resource-Policy: cross-origin when serving content to allow (web) clients to access restricted APIs such as SharedArrayBuffer when interacting with the media repository.

[Changed in v1.11] The unauthenticated download endpoints have been deprecated in favour of newer, authenticated, ones. This change includes updating the paths of all media endpoints from /_matrix/media/* to /_matrix/client/{version}/media/*, with the exception of the /upload and /create endpoints. The upload/create endpoints are expected to undergo a similar transition in a later version of the specification.

Matrix Content (mxc://) URIs

Content locations are represented as Matrix Content (mxc://) URIs. They look like:

mxc://<server-name>/<media-id>

<server-name> : The name of the homeserver where this content originated, e.g. matrix.org
<media-id> : An opaque ID which identifies the content.

Client behaviour

Clients can access the content repository using the following endpoints.

[Changed in v1.11] A number of endpoints under the /_matrix/media hierarchy have been deprecated and replaced with new endpoints which require authentication. The deprecated endpoints are marked in the section below.

By Matrix 1.12, servers SHOULD “freeze” the deprecated, unauthenticated, endpoints to prevent newly-uploaded media from being downloaded. This SHOULD mean that any media uploaded before the freeze remains accessible via the deprecated endpoints, and any media uploaded after (or during) the freeze SHOULD only be accessible through the new, authenticated, endpoints. For remote media, “newly-uploaded” is determined by the date the cache was populated. This may mean the media is older than the freeze, but because the server had to re-download it, it is now considered “new”.

Clients SHOULD update to support the authenticated endpoints before servers freeze unauthenticated access.

Servers SHOULD consider their local ecosystem impact before enacting a freeze. This could mean ensuring their users’ typical clients support the new endpoints when available, or updating bridges to start using media proxies.

In addition to the above, servers SHOULD exclude IdP icons used in the m.login.sso flow from the freeze. See the m.login.sso flow schema for details.

An example timeline for a server may be:

  • Matrix 1.11 release: Clients begin supporting authenticated media.
  • Matrix 1.12 release: Servers freeze unauthenticated media access.
    • Media uploaded prior to this point still works with the deprecated endpoints.
    • Newly uploaded (or cached) media only works on the authenticated endpoints.

Matrix 1.12 is expected to be released in the July-September 2024 calendar quarter.

GET /_matrix/client/v1/media/config


Added in v1.11

This endpoint allows clients to retrieve the configuration of the content repository, such as upload limitations. Clients SHOULD use this as a guide when using content repository endpoints. All values are intentionally left optional. Clients SHOULD follow the advice given in the field description when the field is not available.

Both clients and server administrators should be aware that proxies between the client and the server may affect the apparent behaviour of content repository APIs, for example, proxies may enforce a lower upload size limit than is advertised by the server on this endpoint.

Rate-limited: Yes
Requires authentication: Yes

Request

No request parameters or request body.


Responses

Status Description
200 The public content repository configuration for the matrix server.
429 This request was rate-limited.

200 response

Name Type Description
m.upload.size integer

The maximum size an upload can be in bytes. Clients SHOULD use this as a guide when uploading content. If not listed or null, the size limit should be treated as unknown.

{
  "m.upload.size": 50000000
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

GET /_matrix/client/v1/media/download/{serverName}/{mediaId}


Added in v1.11

Clients SHOULD NOT generate or use URLs which supply the access token in the query string. These URLs may be copied by users verbatim and provided in a chat message to another user, disclosing the sender’s access token.

Clients MAY be redirected using the 307/308 responses below to download the request object. This is typical when the homeserver uses a Content Delivery Network (CDN).

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
mediaId string

Required: The media ID from the mxc:// URI (the path component).

serverName Server Name

Required: The server name from the mxc:// URI (the authority component).

query parameters
Name Type Description
timeout_ms integer

The maximum number of milliseconds that the client is willing to wait to start receiving data, in the case that the content has not yet been uploaded. The default value is 20000 (20 seconds). The content repository SHOULD impose a maximum value for this parameter. The content repository MAY respond before the timeout.

Added in v1.7


Responses

Status Description
200 The content that was previously uploaded.
307 A redirect to the requested content.
308 A redirect to the requested content.
429 This request was rate-limited.
502 The content is too large for the server to serve.
504 The content is not yet available. A standard error response will be returned with the errcode M_NOT_YET_UPLOADED.

200 response

Headers
Name Type Description
Content-Disposition string

Required: The disposition of the returned content. MUST be one of inline or attachment, and SHOULD contain a file name.

If the Content-Type is allowed in the restrictions for serving inline content, servers SHOULD use inline, otherwise they SHOULD use attachment.

If the upload was made with a filename, this header MUST contain the same filename. Otherwise, filename is excluded from the header. If the media being downloaded is remote, the remote server’s filename in the Content-Disposition header is used as the filename instead. When the header is not supplied, or does not supply a filename, the local download response does not include a filename.

Changed in v1.12: This header became required.

Content-Type string

Required: The content type of the file that was previously uploaded.

The server MUST return a Content-Type which is either exactly the same as the original upload, or reasonably close. The bounds of “reasonable” are:

  • Adding a charset to text/* content types.
  • Detecting HTML and using text/html instead of text/plain.
  • Using application/octet-stream when the server determines the content type is obviously wrong. For example, an encrypted file being claimed as image/png.
  • Returning application/octet-stream when the media has an unknown/unprovided Content-Type. For example, being uploaded before the server tracked content types or when the remote server is non-compliantly omitting the header entirely.

Actions not in the spirit of the above are not considered “reasonable”.

Changed in v1.12: This header became required in order to support Content-Disposition, and the behaviour to compute its value was clarified.

Content-Type Description
application/octet-stream

Required. The bytes for the uploaded file.

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

502 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_TOO_LARGE",
  "error": "Content is too large to serve"
}

504 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_YET_UPLOADED",
  "error": "Content has not yet been uploaded"
}

GET /_matrix/client/v1/media/download/{serverName}/{mediaId}/{fileName}


Added in v1.11

This will download content from the content repository (same as the previous endpoint) but replaces the target file name with the one provided by the caller.

Clients SHOULD NOT generate or use URLs which supply the access token in the query string. These URLs may be copied by users verbatim and provided in a chat message to another user, disclosing the sender’s access token.

Clients MAY be redirected using the 307/308 responses below to download the request object. This is typical when the homeserver uses a Content Delivery Network (CDN).

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
fileName string

Required: A filename to give in the Content-Disposition header.

mediaId string

Required: The media ID from the mxc:// URI (the path component).

serverName Server Name

Required: The server name from the mxc:// URI (the authority component).

query parameters
Name Type Description
timeout_ms integer

The maximum number of milliseconds that the client is willing to wait to start receiving data, in the case that the content has not yet been uploaded. The default value is 20000 (20 seconds). The content repository SHOULD impose a maximum value for this parameter. The content repository MAY respond before the timeout.

Added in v1.7


Responses

Status Description
200 The content that was previously uploaded.
307 A redirect to the requested content.
308 A redirect to the requested content.
429 This request was rate-limited.
502 The content is too large for the server to serve.
504 The content is not yet available. A standard error response will be returned with the errcode M_NOT_YET_UPLOADED.

200 response

Headers
Name Type Description
Content-Disposition string

Required: The disposition of the returned content. MUST be one of inline or attachment, and MUST contain the file name requested in the path.

If the Content-Type is allowed in the restrictions for serving inline content, servers SHOULD use inline, otherwise they SHOULD use attachment.

Changed in v1.12: This header became required.

Content-Type string

Required: The content type of the file that was previously uploaded.

The server MUST return a Content-Type which is either exactly the same as the original upload, or reasonably close. The bounds of “reasonable” are:

  • Adding a charset to text/* content types.
  • Detecting HTML and using text/html instead of text/plain.
  • Using application/octet-stream when the server determines the content type is obviously wrong. For example, an encrypted file being claimed as image/png.
  • Returning application/octet-stream when the media has an unknown/unprovided Content-Type. For example, being uploaded before the server tracked content types or when the remote server is non-compliantly omitting the header entirely.

Actions not in the spirit of the above are not considered “reasonable”.

Changed in v1.12: This header became required in order to support Content-Disposition, and the behaviour to compute its value was clarified.

Content-Type Description
application/octet-stream

Required. The bytes for the uploaded file.

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

502 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_TOO_LARGE",
  "error": "Content is too large to serve"
}

504 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_YET_UPLOADED",
  "error": "Content has not yet been uploaded"
}

GET /_matrix/client/v1/media/preview_url


Added in v1.11

Get information about a URL for the client. Typically this is called when a client sees a URL in a message and wants to render a preview for the user.

Clients should consider avoiding this endpoint for URLs posted in encrypted rooms. Encrypted rooms often contain more sensitive information the users do not want to share with the homeserver, and this can mean that the URLs being shared should also not be shared with the homeserver.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

query parameters
Name Type Description
ts integer

The preferred point in time to return a preview for. The server may return a newer version if it does not have the requested version available.

url URI

Required: The URL to get a preview of.


Responses

Status Description
200 The OpenGraph data for the URL, which may be empty. Some values are replaced with matrix equivalents if they are provided in the response. The differences from the OpenGraph protocol are described here.
429 This request was rate-limited.

200 response

Name Type Description
matrix:image:size integer

The byte-size of the image. Omitted if there is no image attached.

og:image URI

An mxc:// URI to the image. Omitted if there is no image.

{
  "matrix:image:size": 102400,
  "og:description": "This is a really cool blog post from matrix.org",
  "og:image": "mxc://example.com/ascERGshawAWawugaAcauga",
  "og:image:height": 48,
  "og:image:type": "image/png",
  "og:image:width": 48,
  "og:title": "Matrix Blog Post"
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

GET /_matrix/client/v1/media/thumbnail/{serverName}/{mediaId}


Added in v1.11

Download a thumbnail of content from the content repository. See the Thumbnails section for more information.

Clients SHOULD NOT generate or use URLs which supply the access token in the query string. These URLs may be copied by users verbatim and provided in a chat message to another user, disclosing the sender’s access token.

Clients MAY be redirected using the 307/308 responses below to download the request object. This is typical when the homeserver uses a Content Delivery Network (CDN).

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
mediaId string

Required: The media ID from the mxc:// URI (the path component).

serverName Server Name

Required: The server name from the mxc:// URI (the authority component).

query parameters
Name Type Description
animated boolean

Indicates preference for an animated thumbnail from the server, if possible. Animated thumbnails typically use the content types image/gif, image/png (with APNG format), image/apng, and image/webp instead of the common static image/png or image/jpeg content types.

When true, the server SHOULD return an animated thumbnail if possible and supported. When false, the server MUST NOT return an animated thumbnail. For example, returning a static image/png or image/jpeg thumbnail. When not provided, the server SHOULD NOT return an animated thumbnail.

Servers SHOULD prefer to return image/webp thumbnails when supporting animation.

When true and the media cannot be animated, such as in the case of a JPEG or PDF, the server SHOULD behave as though animated is false.

Added in v1.11

height integer

Required: The desired height of the thumbnail. The actual thumbnail may be larger than the size specified.

method string

The desired resizing method. See the Thumbnails section for more information.

One of: [crop, scale].

timeout_ms integer

The maximum number of milliseconds that the client is willing to wait to start receiving data, in the case that the content has not yet been uploaded. The default value is 20000 (20 seconds). The content repository SHOULD impose a maximum value for this parameter. The content repository MAY respond before the timeout.

Added in v1.7

width integer

Required: The desired width of the thumbnail. The actual thumbnail may be larger than the size specified.


Responses

Status Description
200 A thumbnail of the requested content.
307 A redirect to the thumbnail of the requested content.
308 A redirect to the thumbnail of the requested content.
400 The request does not make sense to the server, or the server cannot thumbnail the content. For example, the client requested non-integer dimensions or asked for negatively-sized images.
413 The local content is too large for the server to thumbnail.
429 This request was rate-limited.
502 The remote content is too large for the server to thumbnail.
504 The content is not yet available. A standard error response will be returned with the errcode M_NOT_YET_UPLOADED.

200 response

Headers
Name Type Description
Content-Disposition string

Required: The disposition of the returned content. MUST be inline, and SHOULD contain a file name (e.g. thumbnail.png).

Servers should note the Content-Type restrictions for serving inline content, as these limitations imply which formats should be used for thumbnail generation.

Added in v1.12

Content-Type string

Required: The content type of the thumbnail.

One of: [image/jpeg, image/png, image/apng, image/gif, image/webp].

Changed in v1.12: This header became required in order to support Content-Disposition.

Content-Type Description
image/apng

Required. The bytes for the animated thumbnail.

Added in v1.11

image/gif

Required. The bytes for the animated thumbnail.

Added in v1.11

image/jpeg

Required. The bytes for the thumbnail.

image/png

Required. The bytes for the thumbnail. The thumbnail MAY use an animated format if animated=true.

Changed in v1.11: The PNG may be of the APNG variety if animation is supported and requested.

image/webp

Required. The bytes for the animated thumbnail.

Added in v1.11

400 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_UNKNOWN",
  "error": "Cannot generate thumbnails for the requested content"
}

413 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_TOO_LARGE",
  "error": "Content is too large to thumbnail"
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

502 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_TOO_LARGE",
  "error": "Content is too large to thumbnail"
}

504 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_YET_UPLOADED",
  "error": "Content has not yet been uploaded"
}

POST /_matrix/media/v1/create


Added in v1.7

Creates a new mxc:// URI, independently of the content being uploaded. The content must be provided later via PUT /_matrix/media/v3/upload/{serverName}/{mediaId}.

The server may optionally enforce a maximum age for unused IDs, and delete media IDs when the client doesn’t start the upload in time, or when the upload was interrupted and not resumed in time. The server should include the maximum POSIX millisecond timestamp to complete the upload in the unused_expires_at field in the response JSON. The recommended default expiration is 24 hours which should be enough time to accommodate users on poor connection who find a better connection to complete the upload.

As well as limiting the rate of requests to create mxc:// URIs, the server should limit the number of concurrent pending media uploads a given user can have. A pending media upload is a created mxc:// URI where (a) the media has not yet been uploaded, and (b) has not yet expired (the unused_expires_at timestamp has not yet passed). In both cases, the server should respond with an HTTP 429 error with an errcode of M_LIMIT_EXCEEDED.

Rate-limited: Yes
Requires authentication: Yes

Request

No request parameters or request body.


Responses

Status Description
200 The mxc:// URI for the uploaded content.
403 The user does not have permission to upload the content.
429 This request was rate-limited.

200 response

Name Type Description
content_uri Matrix Content URI

Required: The mxc:// URI at which the content will be available, once it is uploaded.

unused_expires_at integer

The timestamp (in milliseconds since the unix epoch) when the generated media id will expire, if media is not uploaded.

{
  "content_uri": "mxc://example.com/AQwafuaFswefuhsfAFAgsw",
  "unused_expires_at": 1647257217083
}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_FORBIDDEN",
  "error": "Cannot upload this content"
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

GET /_matrix/media/v3/config


This API is deprecated and will be removed from a future release.

This endpoint allows clients to retrieve the configuration of the content repository, such as upload limitations. Clients SHOULD use this as a guide when using content repository endpoints. All values are intentionally left optional. Clients SHOULD follow the advice given in the field description when the field is not available.

NOTE: Both clients and server administrators should be aware that proxies between the client and the server may affect the apparent behaviour of content repository APIs, for example, proxies may enforce a lower upload size limit than is advertised by the server on this endpoint.

Rate-limited: Yes
Requires authentication: Yes

Request

No request parameters or request body.


Responses

Status Description
200 The public content repository configuration for the matrix server.
429 This request was rate-limited.

200 response

Name Type Description
m.upload.size integer

The maximum size an upload can be in bytes. Clients SHOULD use this as a guide when uploading content. If not listed or null, the size limit should be treated as unknown.

{
  "m.upload.size": 50000000
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

GET /_matrix/media/v3/download/{serverName}/{mediaId}


This API is deprecated and will be removed from a future release.

Replaced by GET /_matrix/client/v1/media/download/{serverName}/{mediaId} (requires authentication).

[Changed in v1.11] This endpoint MAY return 404 M_NOT_FOUND for media which exists, but is after the server froze unauthenticated media access. See Client Behaviour for more information.

Rate-limited: Yes
Requires authentication: No

Request

Request parameters

path parameters
Name Type Description
mediaId string

Required: The media ID from the mxc:// URI (the path component).

serverName Server Name

Required: The server name from the mxc:// URI (the authority component).

query parameters
Name Type Description
allow_redirect boolean

Indicates to the server that it may return a 307 or 308 redirect response that points at the relevant media content. When not explicitly set to true the server must return the media content itself.

Added in v1.7

allow_remote boolean

Indicates to the server that it should not attempt to fetch the media if it is deemed remote. This is to prevent routing loops where the server contacts itself.

Defaults to true if not provided.

timeout_ms integer

The maximum number of milliseconds that the client is willing to wait to start receiving data, in the case that the content has not yet been uploaded. The default value is 20000 (20 seconds). The content repository SHOULD impose a maximum value for this parameter. The content repository MAY respond before the timeout.

Added in v1.7


Responses

Status Description
200 The content that was previously uploaded.
307 A redirect to the requested content.
308 A redirect to the requested content.
429 This request was rate-limited.
502 The content is too large for the server to serve.
504 The content is not yet available. A standard error response will be returned with the errcode M_NOT_YET_UPLOADED.

200 response

Headers
Name Type Description
Content-Disposition string

Required: The disposition of the returned content. MUST be one of inline or attachment, and SHOULD contain a file name.

If the Content-Type is allowed in the restrictions for serving inline content, servers SHOULD use inline, otherwise they SHOULD use attachment.

If the upload was made with a filename, this header MUST contain the same filename. Otherwise, filename is excluded from the header. If the media being downloaded is remote, the remote server’s filename in the Content-Disposition header is used as the filename instead. When the header is not supplied, or does not supply a filename, the local download response does not include a filename.

Changed in v1.12: This header became required.

Content-Type string

Required: The content type of the file that was previously uploaded.

The server MUST return a Content-Type which is either exactly the same as the original upload, or reasonably close. The bounds of “reasonable” are:

  • Adding a charset to text/* content types.
  • Detecting HTML and using text/html instead of text/plain.
  • Using application/octet-stream when the server determines the content type is obviously wrong. For example, an encrypted file being claimed as image/png.
  • Returning application/octet-stream when the media has an unknown/unprovided Content-Type. For example, being uploaded before the server tracked content types or when the remote server is non-compliantly omitting the header entirely.

Actions not in the spirit of the above are not considered “reasonable”.

Changed in v1.12: This header became required in order to support Content-Disposition, and the behaviour to compute its value was clarified.

Content-Type Description
application/octet-stream

Required. The bytes for the uploaded file.

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

502 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_TOO_LARGE",
  "error": "Content is too large to serve"
}

504 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_YET_UPLOADED",
  "error": "Content has not yet been uploaded"
}

GET /_matrix/media/v3/download/{serverName}/{mediaId}/{fileName}


This API is deprecated and will be removed from a future release.

This will download content from the content repository (same as the previous endpoint) but replace the target file name with the one provided by the caller.

[Changed in v1.11] This endpoint MAY return 404 M_NOT_FOUND for media which exists, but is after the server froze unauthenticated media access. See Client Behaviour for more information.

Rate-limited: Yes
Requires authentication: No

Request

Request parameters

path parameters
Name Type Description
fileName string

Required: A filename to give in the Content-Disposition header.

mediaId string

Required: The media ID from the mxc:// URI (the path component).

serverName Server Name

Required: The server name from the mxc:// URI (the authority component).

query parameters
Name Type Description
allow_redirect boolean

Indicates to the server that it may return a 307 or 308 redirect response that points at the relevant media content. When not explicitly set to true the server must return the media content itself.

Added in v1.7

allow_remote boolean

Indicates to the server that it should not attempt to fetch the media if it is deemed remote. This is to prevent routing loops where the server contacts itself.

Defaults to true if not provided.

timeout_ms integer

The maximum number of milliseconds that the client is willing to wait to start receiving data, in the case that the content has not yet been uploaded. The default value is 20000 (20 seconds). The content repository SHOULD impose a maximum value for this parameter. The content repository MAY respond before the timeout.

Added in v1.7


Responses

Status Description
200 The content that was previously uploaded.
307 A redirect to the requested content.
308 A redirect to the requested content.
429 This request was rate-limited.
502 The content is too large for the server to serve.
504 The content is not yet available. A standard error response will be returned with the errcode M_NOT_YET_UPLOADED.

200 response

Headers
Name Type Description
Content-Disposition string

Required: The disposition of the returned content. MUST be one of inline or attachment, and MUST contain the file name requested in the path.

If the Content-Type is allowed in the restrictions for serving inline content, servers SHOULD use inline, otherwise they SHOULD use attachment.

Changed in v1.12: This header became required.

Content-Type string

Required: The content type of the file that was previously uploaded.

The server MUST return a Content-Type which is either exactly the same as the original upload, or reasonably close. The bounds of “reasonable” are:

  • Adding a charset to text/* content types.
  • Detecting HTML and using text/html instead of text/plain.
  • Using application/octet-stream when the server determines the content type is obviously wrong. For example, an encrypted file being claimed as image/png.
  • Returning application/octet-stream when the media has an unknown/unprovided Content-Type. For example, being uploaded before the server tracked content types or when the remote server is non-compliantly omitting the header entirely.

Actions not in the spirit of the above are not considered “reasonable”.

Changed in v1.12: This header became required in order to support Content-Disposition, and the behaviour to compute its value was clarified.

Content-Type Description
application/octet-stream

Required. The bytes for the uploaded file.

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

502 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_TOO_LARGE",
  "error": "Content is too large to serve"
}

504 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_YET_UPLOADED",
  "error": "Content has not yet been uploaded"
}

GET /_matrix/media/v3/preview_url


This API is deprecated and will be removed from a future release.

Get information about a URL for the client. Typically this is called when a client sees a URL in a message and wants to render a preview for the user.

Note: Clients should consider avoiding this endpoint for URLs posted in encrypted rooms. Encrypted rooms often contain more sensitive information the users do not want to share with the homeserver, and this can mean that the URLs being shared should also not be shared with the homeserver.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

query parameters
Name Type Description
ts integer

The preferred point in time to return a preview for. The server may return a newer version if it does not have the requested version available.

url URI

Required: The URL to get a preview of.


Responses

Status Description
200 The OpenGraph data for the URL, which may be empty. Some values are replaced with matrix equivalents if they are provided in the response. The differences from the OpenGraph protocol are described here.
429 This request was rate-limited.

200 response

Name Type Description
matrix:image:size integer

The byte-size of the image. Omitted if there is no image attached.

og:image URI

An mxc:// URI to the image. Omitted if there is no image.

{
  "matrix:image:size": 102400,
  "og:description": "This is a really cool blog post from matrix.org",
  "og:image": "mxc://example.com/ascERGshawAWawugaAcauga",
  "og:image:height": 48,
  "og:image:type": "image/png",
  "og:image:width": 48,
  "og:title": "Matrix Blog Post"
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

GET /_matrix/media/v3/thumbnail/{serverName}/{mediaId}


This API is deprecated and will be removed from a future release.

Replaced by GET /_matrix/client/v1/media/thumbnail/{serverName}/{mediaId} (requires authentication).

Download a thumbnail of content from the content repository. See the Thumbnails section for more information.

[Changed in v1.11] This endpoint MAY return 404 M_NOT_FOUND for media which exists, but is after the server froze unauthenticated media access. See Client Behaviour for more information.

Rate-limited: Yes
Requires authentication: No

Request

Request parameters

path parameters
Name Type Description
mediaId string

Required: The media ID from the mxc:// URI (the path component).

serverName Server Name

Required: The server name from the mxc:// URI (the authority component).

query parameters
Name Type Description
allow_redirect boolean

Indicates to the server that it may return a 307 or 308 redirect response that points at the relevant media content. When not explicitly set to true the server must return the media content itself.

Added in v1.7

allow_remote boolean

Indicates to the server that it should not attempt to fetch the media if it is deemed remote. This is to prevent routing loops where the server contacts itself.

Defaults to true if not provided.

animated boolean

Indicates preference for an animated thumbnail from the server, if possible. Animated thumbnails typically use the content types image/gif, image/png (with APNG format), image/apng, and image/webp instead of the common static image/png or image/jpeg content types.

When true, the server SHOULD return an animated thumbnail if possible and supported. When false, the server MUST NOT return an animated thumbnail. For example, returning a static image/png or image/jpeg thumbnail. When not provided, the server SHOULD NOT return an animated thumbnail.

Servers SHOULD prefer to return image/webp thumbnails when supporting animation.

When true and the media cannot be animated, such as in the case of a JPEG or PDF, the server SHOULD behave as though animated is false.

Added in v1.11

height integer

Required: The desired height of the thumbnail. The actual thumbnail may be larger than the size specified.

method string

The desired resizing method. See the Thumbnails section for more information.

One of: [crop, scale].

timeout_ms integer

The maximum number of milliseconds that the client is willing to wait to start receiving data, in the case that the content has not yet been uploaded. The default value is 20000 (20 seconds). The content repository SHOULD impose a maximum value for this parameter. The content repository MAY respond before the timeout.

Added in v1.7

width integer

Required: The desired width of the thumbnail. The actual thumbnail may be larger than the size specified.


Responses

Status Description
200 A thumbnail of the requested content.
307 A redirect to the thumbnail of the requested content.
308 A redirect to the thumbnail of the requested content.
400 The request does not make sense to the server, or the server cannot thumbnail the content. For example, the client requested non-integer dimensions or asked for negatively-sized images.
413 The local content is too large for the server to thumbnail.
429 This request was rate-limited.
502 The remote content is too large for the server to thumbnail.
504 The content is not yet available. A standard error response will be returned with the errcode M_NOT_YET_UPLOADED.

200 response

Headers
Name Type Description
Content-Disposition string

Required: The disposition of the returned content. MUST be inline, and SHOULD contain a file name (e.g. thumbnail.png).

Servers should note the Content-Type restrictions for serving inline content, as these limitations imply which formats should be used for thumbnail generation.

Added in v1.12

Content-Type string

Required: The content type of the thumbnail.

One of: [image/jpeg, image/png, image/apng, image/gif, image/webp].

Changed in v1.12: This header became required in order to support Content-Disposition.

Content-Type Description
image/apng

Required. The bytes for the animated thumbnail.

Added in v1.11

image/gif

Required. The bytes for the animated thumbnail.

Added in v1.11

image/jpeg

Required. The bytes for the thumbnail.

image/png

Required. The bytes for the thumbnail. The thumbnail MAY use an animated format if animated=true.

Changed in v1.11: The PNG may be of the APNG variety if animation is supported and requested.

image/webp

Required. The bytes for the animated thumbnail.

Added in v1.11

400 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_UNKNOWN",
  "error": "Cannot generate thumbnails for the requested content"
}

413 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_TOO_LARGE",
  "error": "Content is too large to thumbnail"
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

502 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_TOO_LARGE",
  "error": "Content is too large to thumbnail"
}

504 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_YET_UPLOADED",
  "error": "Content has not yet been uploaded"
}

POST /_matrix/media/v3/upload


Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

header parameters
Name Type Description
Content-Type string

Optional. The content type of the file being uploaded.

Clients SHOULD always supply this header.

Defaults to application/octet-stream if it is not set.

Changed in v1.12: This header became explicitly optional with a default value.

query parameters
Name Type Description
filename string

The name of the file being uploaded

Request body

Content-Type Description
application/octet-stream

The content to be uploaded.

Request body example

<bytes>

Responses

Status Description
200 The mxc:// URI for the uploaded content.
403

The user does not have permission to upload the content. Some reasons for this error include:

  • The server does not permit the file type.
  • The user has reached a quota for uploaded content.
413 The uploaded content is too large for the server.
429 This request was rate-limited.

200 response

Name Type Description
content_uri Matrix Content URI

Required: The mxc:// URI to the uploaded content.

{
  "content_uri": "mxc://example.com/AQwafuaFswefuhsfAFAgsw"
}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_FORBIDDEN",
  "error": "Cannot upload this content"
}

413 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_TOO_LARGE",
  "error": "Cannot upload files larger than 100mb"
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

PUT /_matrix/media/v3/upload/{serverName}/{mediaId}


Added in v1.7

This endpoint permits uploading content to an mxc:// URI that was created earlier via POST /_matrix/media/v1/create.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

header parameters
Name Type Description
Content-Type string

Optional. The content type of the file being uploaded.

Clients SHOULD always supply this header.

Defaults to application/octet-stream if it is not set.

Changed in v1.12: This header became explicitly optional with a default value.

path parameters
Name Type Description
mediaId string

Required: The media ID from the mxc:// URI returned by POST /_matrix/media/v1/create (the path component).

serverName Server Name

Required: The server name from the mxc:// URI returned by POST /_matrix/media/v1/create (the authority component).

query parameters
Name Type Description
filename string

The name of the file being uploaded

Request body

Content-Type Description
application/octet-stream

The content to be uploaded.

Request body example

<bytes>

Responses

Status Description
200 The upload was successful.
403

The user does not have permission to upload the content. Some reasons for this error include:

  • The server does not permit the file type.
  • The user has reached a quota for uploaded content.
  • The request comes from a different user than the one that called POST /_matrix/media/v1/create.

A standard error response will be returned with the errcode M_FORBIDDEN.

404

The user has provided an invalid MXC ID. Some reasons for this error include:

A standard error response will be returned with the errcode M_NOT_FOUND.

409 The endpoint was called with a media ID that already has content. A standard error response will be returned with the errcode M_CANNOT_OVERWRITE_MEDIA.
413 The uploaded content is too large for the server.
429 This request was rate-limited.

200 response

{}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_FORBIDDEN",
  "error": "Cannot upload this content"
}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND",
  "error": "Unknown media ID"
}

409 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_CANNOT_OVERWRITE_MEDIA",
  "error": "Media already uploaded"
}

413 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_TOO_LARGE",
  "error": "Cannot upload files larger than 100mb"
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}
Thumbnails

The homeserver SHOULD be able to supply thumbnails for uploaded images and videos. The exact file types which can be thumbnailed are not currently specified - see Issue #1938 for more information.

The thumbnail methods are “crop” and “scale”. “scale” tries to return an image where either the width or the height is smaller than the requested size. The client should then scale and letterbox the image if it needs to fit within a given rectangle. “crop” tries to return an image where the width and height are close to the requested size and the aspect matches the requested size. The client should scale the image if it needs to fit within a given rectangle.

The dimensions given to the thumbnail API are the minimum size the client would prefer. Servers must never return thumbnails smaller than the client’s requested dimensions, unless the content being thumbnailed is smaller than the dimensions. When the content is smaller than the requested dimensions, servers should return the original content rather than thumbnail it.

Servers SHOULD produce thumbnails with the following dimensions and methods:

  • 32x32, crop
  • 96x96, crop
  • 320x240, scale
  • 640x480, scale
  • 800x600, scale

In summary:

  • “scale” maintains the original aspect ratio of the image
  • “crop” provides an image in the aspect ratio of the sizes given in the request
  • The server will return an image larger than or equal to the dimensions requested where possible.

Servers MUST NOT upscale thumbnails under any circumstance. Servers MUST NOT return a smaller thumbnail than requested, unless the original content makes that impossible.

Security considerations

The HTTP GET endpoint does not require any authentication. Knowing the URL of the content is sufficient to retrieve the content, even if the entity isn’t in the room.

mxc:// URIs are vulnerable to directory traversal attacks such as mxc://127.0.0.1/../../../some_service/etc/passwd. This would cause the target homeserver to try to access and return this file. As such, homeservers MUST sanitise mxc:// URIs by allowing only alphanumeric (A-Za-z0-9), _ and - characters in the server-name and media-id values. This set of whitelisted characters allows URL-safe base64 encodings specified in RFC 4648. Applying this character whitelist is preferable to blacklisting . and / as there are techniques around blacklisted characters (percent-encoded characters, UTF-8 encoded traversals, etc).

Homeservers have additional content-specific concerns:

  • Clients may try to upload very large files. Homeservers should not store files that are too large and should not serve them to clients, returning a HTTP 413 error with the M_TOO_LARGE code.
  • Clients may try to upload very large images. Homeservers should not attempt to generate thumbnails for images that are too large, returning a HTTP 413 error with the M_TOO_LARGE code.
  • Remote homeservers may host very large files or images. Homeservers should not proxy or thumbnail large files or images from remote homeservers, returning a HTTP 502 error with the M_TOO_LARGE code.
  • Clients may try to upload a large number of files. Homeservers should limit the number and total size of media that can be uploaded by clients, returning a HTTP 403 error with the M_FORBIDDEN code.
  • Clients may try to access a large number of remote files through a homeserver. Homeservers should restrict the number and size of remote files that it caches.
  • Clients or remote homeservers may try to upload malicious files targeting vulnerabilities in either the homeserver thumbnailing or the client decoders.
Serving inline content

Clients with insecure configurations may be vulnerable to Cross-Site Scripting attacks when served media with a Content-Disposition of inline. Clients SHOULD NOT be hosted on the same domain as the media endpoints for the homeserver to mitigate most of this risk. Servers SHOULD restrict Content-Type headers to one of the following values when serving content with Content-Disposition: inline:

  • text/css
  • text/plain
  • text/csv
  • application/json
  • application/ld+json
  • image/jpeg
  • image/gif
  • image/png
  • image/apng
  • image/webp
  • image/avif
  • video/mp4
  • video/webm
  • video/ogg
  • video/quicktime
  • audio/mp4
  • audio/webm
  • audio/aac
  • audio/mpeg
  • audio/ogg
  • audio/wave
  • audio/wav
  • audio/x-wav
  • audio/x-pn-wav
  • audio/flac
  • audio/x-flac

These types are unlikely to cause Cross-Site Scripting issues when a Content-Type header is provided, as clients will only try to render the data using that content type. For example, if a HTML file is uploaded with a Content-Type of image/png, clients will just assume that the image is corrupted, and won’t render it as a HTML page. Therefore, there is no risk in trusting the user-defined content type, as long as the Content-Disposition is calculated based on that type.

Clients SHOULD NOT rely on servers returning inline rather than attachment on /download. Server implementations might decide out of an abundance of caution that all downloads are responded to with attachment, regardless of content type - clients should not be surprised by this behaviour.

Send-to-Device messaging

This module provides a means by which clients can exchange signalling messages without them being stored permanently as part of a shared communication history. A message is delivered exactly once to each client device.

The primary motivation for this API is exchanging data that is meaningless or undesirable to persist in the room DAG - for example, one-time authentication tokens or key data. It is not intended for conversational data, which should be sent using the normal /rooms/<room_id>/send API for consistency throughout Matrix.

Client behaviour

To send a message to other devices, a client should call /sendToDevice. Only one message can be sent to each device per transaction, and they must all have the same event type. The device ID in the request body can be set to * to request that the message be sent to all known devices.

If there are send-to-device messages waiting for a client, they will be returned by /sync, as detailed in Extensions to /sync. Clients should inspect the type of each returned event, and ignore any they do not understand.

Server behaviour

Servers should store pending messages for local users until they are successfully delivered to the destination device. When a client calls /sync with an access token which corresponds to a device with pending messages, the server should list the pending messages, in order of arrival, in the response body.

When the client calls /sync again with the next_batch token from the first response, the server should infer that any send-to-device messages in that response have been delivered successfully, and delete them from the store.

If there is a large queue of send-to-device messages, the server should limit the number sent in each /sync response. 100 messages is recommended as a reasonable limit.

If the client sends messages to users on remote domains, those messages should be sent on to the remote servers via federation.

Protocol definitions

PUT /_matrix/client/v3/sendToDevice/{eventType}/{txnId}


This endpoint is used to send send-to-device events to a set of client devices.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
eventType string

Required: The type of event to send.

txnId string

Required: The transaction ID for this event. Clients should generate an ID unique across requests with the same access token; it will be used by the server to ensure idempotency of requests.

Request body

body
Name Type Description
messages {User ID: {string: EventContent}}

Required: The messages to send. A map from user ID, to a map from device ID to message body. The device ID may also be *, meaning all known devices for the user.

Request body example

{
  "messages": {
    "@alice:example.com": {
      "TLLBEANAAG": {
        "example_content_key": "value"
      }
    }
  }
}

Responses

Status Description
200 The message was successfully sent.

200 response

{}
Extensions to /sync

This module adds the following properties to the /sync response:

Parameter Type Description
to_device ToDevice Optional. Information on the send-to-device messages for the client device.

ToDevice

Parameter Type Description
events [Event] List of send-to-device messages.

Event

Parameter Type Description
content EventContent The content of this event. The fields in this object will vary depending on the type of event.
sender string The Matrix user ID of the user who sent this event.
type string The type of event.

Example response:

{
  "next_batch": "s72595_4483_1934",
  "rooms": {"leave": {}, "join": {}, "invite": {}},
  "to_device": {
    "events": [
      {
        "sender": "@alice:example.com",
        "type": "m.new_device",
        "content": {
          "device_id": "XYZABCDE",
          "rooms": ["!726s6s6q:example.com"]
        }
      }
    ]
  }
}

Device Management

This module provides a means for a user to manage their devices.

Client behaviour

Clients that implement this module should offer the user a list of registered devices, as well as the means to update their display names. Clients should also allow users to delete disused devices.

POST /_matrix/client/v3/delete_devices


This API endpoint uses the User-Interactive Authentication API.

Deletes the given devices, and invalidates any access token associated with them.

Rate-limited: No
Requires authentication: Yes

Request

Request body

Name Type Description
auth Authentication Data

Additional authentication information for the user-interactive authentication API.

devices [string]

Required: The list of device IDs to delete.

Authentication Data
Name Type Description
session string

The value of the session key given by the homeserver.

type string

The authentication type that the client is attempting to complete. May be omitted if session is given, and the client is reissuing a request which it believes has been completed out-of-band (for example, via the fallback mechanism).

<Other properties>

Keys dependent on the login type

Request body example

{
  "auth": {
    "example_credential": "verypoorsharedsecret",
    "session": "xxxxx",
    "type": "example.type.foo"
  },
  "devices": [
    "QBUAZIFURK",
    "AUIECTSRND"
  ]
}

Responses

Status Description
200 The devices were successfully removed, or had been removed previously.
401 The homeserver requires additional authentication information.

200 response

{}

401 response

Authentication response
Name Type Description
completed [string]

A list of the stages the client has completed successfully

flows [Flow information]

Required: A list of the login flows supported by the server for this API.

params {string: object}

Contains any information that the client will need to know in order to use a given type of authentication. For each login type presented, that type may be present as a key in this dictionary. For example, the public part of an OAuth client ID could be given here.

session string

This is a session identifier that the client must pass back to the home server, if one is provided, in subsequent attempts to authenticate in the same API call.

Flow information
Name Type Description
stages [string]

Required: The login type of each of the stages required to complete this authentication flow

{
  "completed": [
    "example.type.foo"
  ],
  "flows": [
    {
      "stages": [
        "example.type.foo"
      ]
    }
  ],
  "params": {
    "example.type.baz": {
      "example_key": "foobar"
    }
  },
  "session": "xxxxxxyz"
}

GET /_matrix/client/v3/devices


Gets information about all devices for the current user.

Rate-limited: No
Requires authentication: Yes

Request

No request parameters or request body.


Responses

Status Description
200 Device information

200 response

Name Type Description
devices [Device]

A list of all registered devices for this user.

Device
Name Type Description
device_id string

Required: Identifier of this device.

display_name string

Display name set by the user for this device. Absent if no name has been set.

last_seen_ip string

The IP address where this device was last seen. (May be a few minutes out of date, for efficiency reasons).

last_seen_ts integer

The timestamp (in milliseconds since the unix epoch) when this devices was last seen. (May be a few minutes out of date, for efficiency reasons).

{
  "devices": [
    {
      "device_id": "QBUAZIFURK",
      "display_name": "android",
      "last_seen_ip": "1.2.3.4",
      "last_seen_ts": 1474491775024
    }
  ]
}

GET /_matrix/client/v3/devices/{deviceId}


Gets information on a single device, by device id.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
deviceId string

Required: The device to retrieve.


Responses

Status Description
200 Device information
404 The current user has no device with the given ID.

200 response

Device
Name Type Description
device_id string

Required: Identifier of this device.

display_name string

Display name set by the user for this device. Absent if no name has been set.

last_seen_ip string

The IP address where this device was last seen. (May be a few minutes out of date, for efficiency reasons).

last_seen_ts integer

The timestamp (in milliseconds since the unix epoch) when this devices was last seen. (May be a few minutes out of date, for efficiency reasons).

{
  "device_id": "QBUAZIFURK",
  "display_name": "android",
  "last_seen_ip": "1.2.3.4",
  "last_seen_ts": 1474491775024
}

PUT /_matrix/client/v3/devices/{deviceId}


Updates the metadata on the given device.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
deviceId string

Required: The device to update.

Request body

Name Type Description
display_name string

The new display name for this device. If not given, the display name is unchanged.

Request body example

{
  "display_name": "My other phone"
}

Responses

Status Description
200 The device was successfully updated.
404 The current user has no device with the given ID.

200 response

{}

DELETE /_matrix/client/v3/devices/{deviceId}


This API endpoint uses the User-Interactive Authentication API.

Deletes the given device, and invalidates any access token associated with it.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
deviceId string

Required: The device to delete.

Request body

Name Type Description
auth Authentication Data

Additional authentication information for the user-interactive authentication API.

Authentication Data
Name Type Description
session string

The value of the session key given by the homeserver.

type string

The authentication type that the client is attempting to complete. May be omitted if session is given, and the client is reissuing a request which it believes has been completed out-of-band (for example, via the fallback mechanism).

<Other properties>

Keys dependent on the login type

Request body example

{
  "auth": {
    "example_credential": "verypoorsharedsecret",
    "session": "xxxxx",
    "type": "example.type.foo"
  }
}

Responses

Status Description
200 The device was successfully removed, or had been removed previously.
401 The homeserver requires additional authentication information.

200 response

{}

401 response

Authentication response
Name Type Description
completed [string]

A list of the stages the client has completed successfully

flows [Flow information]

Required: A list of the login flows supported by the server for this API.

params {string: object}

Contains any information that the client will need to know in order to use a given type of authentication. For each login type presented, that type may be present as a key in this dictionary. For example, the public part of an OAuth client ID could be given here.

session string

This is a session identifier that the client must pass back to the home server, if one is provided, in subsequent attempts to authenticate in the same API call.

Flow information
Name Type Description
stages [string]

Required: The login type of each of the stages required to complete this authentication flow

{
  "completed": [
    "example.type.foo"
  ],
  "flows": [
    {
      "stages": [
        "example.type.foo"
      ]
    }
  ],
  "params": {
    "example.type.baz": {
      "example_key": "foobar"
    }
  },
  "session": "xxxxxxyz"
}

Security considerations

Deleting devices has security implications: it invalidates the access_token assigned to the device, so an attacker could use it to log out the real user (and do it repeatedly every time the real user tries to log in to block the attacker). Servers should require additional authentication beyond the access token when deleting devices (for example, requiring that the user resubmit their password).

The display names of devices are publicly visible. Clients should consider advising the user of this.

End-to-End Encryption

Matrix optionally supports end-to-end encryption, allowing rooms to be created whose conversation contents are not decryptable or interceptable on any of the participating homeservers.

Key Distribution

Encryption and Authentication in Matrix is based around public-key cryptography. The Matrix protocol provides a basic mechanism for exchange of public keys, though an out-of-band channel is required to exchange fingerprints between users to build a web of trust.

Overview
  1. Bob publishes the public keys and supported algorithms for his device. This may include long-term identity keys, and/or one-time keys.
      +----------+  +--------------+
      | Bob's HS |  | Bob's Device |
      +----------+  +--------------+
            |              |
            |<=============|
              /keys/upload
  1. Alice requests Bob’s public identity keys and supported algorithms.
      +----------------+  +------------+  +----------+
      | Alice's Device |  | Alice's HS |  | Bob's HS |
      +----------------+  +------------+  +----------+
             |                  |               |
             |=================>|==============>|
               /keys/query        <federation>
  1. Alice selects an algorithm and claims any one-time keys needed.
      +----------------+  +------------+  +----------+
      | Alice's Device |  | Alice's HS |  | Bob's HS |
      +----------------+  +------------+  +----------+
             |                  |               |
             |=================>|==============>|
               /keys/claim         <federation>
Key algorithms

Different key algorithms are used for different purposes. Each key algorithm is identified by a name and is represented in a specific way.

The name ed25519 corresponds to the Ed25519 signature algorithm. The key is a 32-byte Ed25519 public key, encoded using unpadded Base64. Example:

"SogYyrkTldLz0BXP+GYWs0qaYacUI0RleEqNT8J3riQ"

The name curve25519 corresponds to the Curve25519 ECDH algorithm. The key is a 32-byte Curve25519 public key, encoded using unpadded Base64. Example:

"JGLn/yafz74HB2AbPLYJWIVGnKAtqECOBf11yyXac2Y"

The name signed_curve25519 also corresponds to the Curve25519 ECDH algorithm, but the key is signed so that it can be authenticated. A key using this algorithm is represented by an object with the following properties:

KeyObject

Parameter Type Description
key string Required. The unpadded Base64-encoded 32-byte Curve25519 public key.
signatures Signatures Required. Signatures of the key object. The signature is calculated using the process described at Signing JSON.
fallback boolean Indicates whether this is a fallback key. Defaults to false.

Example:

{
  "key":"06UzBknVHFMwgi7AVloY7ylC+xhOhEX4PkNge14Grl8",
  "signatures": {
    "@user:example.com": {
      "ed25519:EGURVBUNJP": "YbJva03ihSj5mPk+CHMJKUKlCXCPFXjXOK6VqBnN9nA2evksQcTGn6hwQfrgRHIDDXO2le49x7jnWJHMJrJoBQ"
    }
  }
}

ed25519 and curve25519 keys are used for device keys. Additionally, ed25519 keys are used for cross-signing keys.

signed_curve25519 keys are used for one-time and fallback keys.

Device keys

Each device should have one Ed25519 signing key. This key should be generated on the device from a cryptographically secure source, and the private part of the key should never be exported from the device. This key is used as the fingerprint for a device by other clients, and signs the device’s other keys.

A device will generally need to generate a number of additional keys. Details of these will vary depending on the messaging algorithm in use.

For Olm version 1, each device also requires a single Curve25519 identity key.

One-time and fallback keys

In addition to the device keys, which are long-lived, some encryption algorithms require that devices may also have a number of one-time keys, which are only used once and discarded after use. For Olm version 1, devices use signed_curve25519 one-time keys, signed by the device’s Ed25519 key.

Devices will generate one-time keys and upload them to the server; these will later be claimed by other users. Servers must ensure that each one-time key is only claimed once: a homeserver should discard the one time key once it has been given to another user.

[Added in v1.2] Fallback keys are similar to one-time keys, but are not consumed once used. If a fallback key has been uploaded, it will be returned by the server when the device has run out of one-time keys and a user tries to claim a key. Fallback keys should be replaced with new fallback keys as soon as possible after they have been used.

Fallback keys are used to prevent one-time key exhaustion when devices are offline/unable to upload additional keys, though sessions started using fallback keys could be vulnerable to replay attacks.

Devices will be informed, via /sync, about the number of one-time keys remaining that can be claimed, as well as whether the fallback keys have been used. The device can thus ensure that, while it is online, there is a sufficient supply of one-time keys available, and that the fallback keys get replaced if they have been used.

Uploading keys

A device uploads the public parts of identity keys to their homeserver as a signed JSON object, using the /keys/upload API. The JSON object must include the public part of the device’s Ed25519 key, and must be signed by that key, as described in Signing JSON.

One-time and fallback keys are also uploaded to the homeserver using the /keys/upload API. New one-time and fallback keys are uploaded as needed. Fallback keys for key algorithms whose format is a signed JSON object should contain a property named fallback with a value of true.

Devices must store the private part of each key they upload. They can discard the private part of a one-time key when they receive a message using that key. However it’s possible that a one-time key given out by a homeserver will never be used, so the device that generates the key will never know that it can discard the key. Therefore a device could end up trying to store too many private keys. A device that is trying to store too many private keys may discard keys starting with the oldest.

Clients should not store the private half of fallback keys indefinitely to avoid situations where attackers can decrypt past messages sent using that fallback key.

Instead, clients should keep the private keys for at most 2 fallback keys: the current, unused, fallback key and the key immediately preceding it. Once the client is reasonably certain it has received all messages that used the old fallback key, such as after an hour since the first message, it should remove that fallback key.

Tracking the device list for a user

Before Alice can send an encrypted message to Bob, she needs a list of each of his devices and the associated identity keys, so that she can establish an encryption session with each device. This list can be obtained by calling /keys/query, passing Bob’s user ID in the device_keys parameter.

From time to time, Bob may add new devices, and Alice will need to know this so that she can include his new devices for later encrypted messages. A naive solution to this would be to call /keys/query before sending each message -however, the number of users and devices may be large and this would be inefficient.

It is therefore expected that each client will maintain a list of devices for a number of users (in practice, typically each user with whom we share an encrypted room). Furthermore, it is likely that this list will need to be persisted between invocations of the client application (to preserve device verification data and to alert Alice if Bob suddenly gets a new device).

Alice’s client can maintain a list of Bob’s devices via the following process:

  1. It first sets a flag to record that it is now tracking Bob’s device list, and a separate flag to indicate that its list of Bob’s devices is outdated. Both flags should be in storage which persists over client restarts.
  2. It then makes a request to /keys/query, passing Bob’s user ID in the device_keys parameter. When the request completes, it stores the resulting list of devices in persistent storage, and clears the ‘outdated’ flag.
  3. During its normal processing of responses to /sync, Alice’s client inspects the changed property of the device_lists field. If it is tracking the device lists of any of the listed users, then it marks the device lists for those users outdated, and initiates another request to /keys/query for them.
  4. Periodically, Alice’s client stores the next_batch field of the result from /sync in persistent storage. If Alice later restarts her client, it can obtain a list of the users who have updated their device list while it was offline by calling /keys/changes, passing the recorded next_batch field as the from parameter. If the client is tracking the device list of any of the users listed in the response, it marks them as outdated. It combines this list with those already flagged as outdated, and initiates a /keys/query request for all of them.

Bob may update one of his devices while Alice has a request to /keys/query in flight. Alice’s client may therefore see Bob’s user ID in the device_lists field of the /sync response while the first request is in flight, and initiate a second request to /keys/query. This may lead to either of two related problems.

The first problem is that, when the first request completes, the client will clear the ‘outdated’ flag for Bob’s devices. If the second request fails, or the client is shut down before it completes, this could lead to Alice using an outdated list of Bob’s devices.

The second possibility is that, under certain conditions, the second request may complete before the first one. When the first request completes, the client could overwrite the later results from the second request with those from the first request.

Clients MUST guard against these situations. For example, a client could ensure that only one request to /keys/query is in flight at a time for each user, by queuing additional requests until the first completes. Alternatively, the client could make a new request immediately, but ensure that the first request’s results are ignored (possibly by cancelling the request).

When Bob and Alice share a room, with Bob tracking Alice’s devices, she may leave the room and then add a new device. Bob will not be notified of this change, as he doesn’t share a room anymore with Alice. When they start sharing a room again, Bob has an out-of-date list of Alice’s devices. In order to address this issue, Bob’s homeserver will add Alice’s user ID to the changed property of the device_lists field, thus Bob will update his list of Alice’s devices as part of his normal processing. Note that Bob can also be notified when he stops sharing any room with Alice by inspecting the left property of the device_lists field, and as a result should remove her from its list of tracked users.

Sending encrypted attachments

When encryption is enabled in a room, files should be uploaded encrypted on the homeserver.

In order to achieve this, a client should generate a single-use 256-bit AES key, and encrypt the file using AES-CTR. The counter should be 64-bit long, starting at 0 and prefixed by a random 64-bit Initialization Vector (IV), which together form a 128-bit unique counter block.

An IV must never be used multiple times with the same key. This implies that if there are multiple files to encrypt in the same message, typically an image and its thumbnail, the files must not share both the same key and IV.

Then, the encrypted file can be uploaded to the homeserver. The key and the IV must be included in the room event along with the resulting mxc:// in order to allow recipients to decrypt the file. As the event containing those will be Megolm encrypted, the server will never have access to the decrypted file.

A hash of the ciphertext must also be included, in order to prevent the homeserver from changing the file content.

A client should send the data as an encrypted m.room.message event, using either m.file as the msgtype, or the appropriate msgtype for the file type. The key is sent using the JSON Web Key format, with a W3C extension.

Extensions to m.room.message msgtypes

This module adds file and thumbnail_file properties, of type EncryptedFile, to m.room.message msgtypes that reference files, such as m.file and m.image, replacing the url and thumbnail_url properties.

EncryptedFile

Parameter Type Description
url string Required. The URL to the file.
key JWK Required. A JSON Web Key object.
iv string Required. The 128-bit unique counter block used by AES-CTR, encoded as unpadded base64.
hashes {string: string} Required. A map from an algorithm name to a hash of the ciphertext, encoded as unpadded base64. Clients should support the SHA-256 hash, which uses the key sha256.
v string Required. Version of the encrypted attachment’s protocol. Must be v2.

JWK

Parameter Type Description
kty string Required. Key type. Must be oct.
key_ops [string] Required. Key operations. Must at least contain encrypt and decrypt.
alg string Required. Algorithm. Must be A256CTR.
k string Required. The key, encoded as urlsafe unpadded base64.
ext boolean Required. Extractable. Must be true. This is a W3C extension.

Example:

{
  "content": {
    "body": "something-important.jpg",
    "file": {
      "url": "mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe",
      "v": "v2",
      "key": {
        "alg": "A256CTR",
        "ext": true,
        "k": "aWF6-32KGYaC3A_FEUCk1Bt0JA37zP0wrStgmdCaW-0",
        "key_ops": ["encrypt","decrypt"],
        "kty": "oct"
      },
      "iv": "w+sE15fzSc0AAAAAAAAAAA",
      "hashes": {
        "sha256": "fdSLu/YkRx3Wyh3KQabP3rd6+SFiKg5lsJZQHtkSAYA"
      }
    },
    "info": {
      "mimetype": "image/jpeg",
      "h": 1536,
      "size": 422018,
      "thumbnail_file": {
        "hashes": {
          "sha256": "/NogKqW5bz/m8xHgFiH5haFGjCNVmUIPLzfvOhHdrxY"
        },
        "iv": "U+k7PfwLr6UAAAAAAAAAAA",
        "key": {
          "alg": "A256CTR",
          "ext": true,
          "k": "RMyd6zhlbifsACM1DXkCbioZ2u0SywGljTH8JmGcylg",
          "key_ops": ["encrypt", "decrypt"],
          "kty": "oct"
        },
        "url": "mxc://example.org/pmVJxyxGlmxHposwVSlOaEOv",
        "v": "v2"
      },
      "thumbnail_info": {
        "h": 768,
        "mimetype": "image/jpeg",
        "size": 211009,
        "w": 432
      },
      "w": 864
    },
    "msgtype": "m.image"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "type": "m.room.message",
  "unsigned": {
      "age": 1234
  }
}

Device verification

Before Alice sends Bob encrypted data, or trusts data received from him, she may want to verify that she is actually communicating with him, rather than a man-in-the-middle. This verification process requires an out-of-band channel: there is no way to do it within Matrix without trusting the administrators of the homeservers.

In Matrix, verification works by Alice meeting Bob in person, or contacting him via some other trusted medium, and using one of the verification methods defined below to interactively verify Bob’s devices. Alice and Bob may also read aloud their unpadded base64 encoded Ed25519 public key, as returned by /keys/query.

Device verification may reach one of several conclusions. For example:

  • Alice may “accept” the device. This means that she is satisfied that the device belongs to Bob. She can then encrypt sensitive material for that device, and knows that messages received were sent from that device.
  • Alice may “reject” the device. She will do this if she knows or suspects that Bob does not control that device (or equivalently, does not trust Bob). She will not send sensitive material to that device, and cannot trust messages apparently received from it.
  • Alice may choose to skip the device verification process. She is not able to verify that the device actually belongs to Bob, but has no reason to suspect otherwise. The encryption protocol continues to protect against passive eavesdroppers.

Once the signing key has been verified, it is then up to the encryption protocol to verify that a given message was sent from a device holding that Ed25519 private key, or to encrypt a message so that it may only be decrypted by such a device. For the Olm protocol, this is documented at https://matrix.org/docs/olm_signing.html.

Key verification framework

Verifying keys manually by reading out the Ed25519 key is not very user-friendly, and can lead to errors. In order to help mitigate errors, and to make the process easier for users, some verification methods are supported by the specification and use messages exchanged by the user’s devices to assist in the verification. The methods all use a common framework for negotiating the key verification.

Verification messages can be sent either in a room shared by the two parties, which should be a direct messaging room between the two parties, or by using to-device messages sent directly between the two devices involved. In both cases, the messages exchanged are similar, with minor differences as detailed below. Verifying between two different users should be performed using in-room messages, whereas verifying two devices belonging to the same user should be performed using to-device messages.

A key verification session is identified by an ID that is established by the first message sent in that session. For verifications using in-room messages, the ID is the event ID of the initial message, and for verifications using to-device messages, the first message contains a transaction_id field that is shared by the other messages of that session.

In general, verification operates as follows:

  • Alice requests a key verification with Bob by sending a key verification request event. If the verification is being requested in a room, this will be an event with type m.room.message and msgtype: m.key.verification.request; if the verification is being requested using to-device messaging, this will be an event with type m.key.verification.request. This event indicates the verification methods that Alice’s client supports. (Note that “Alice” and “Bob” may in fact be the same user, in the case where a user is verifying their own devices.)
  • Bob’s client prompts Bob to accept the key verification. When Bob accepts the verification, Bob’s client sends an m.key.verification.ready event. This event indicates the verification methods, corresponding to the verification methods supported by Alice’s client, that Bob’s client supports.
  • Alice’s or Bob’s devices allow their users to select one of the verification methods supported by both devices to use for verification. When Alice or Bob selects a verification method, their device begins the verification by sending an m.key.verification.start event, indicating the selected verification method. Note that if there is only one verification method in common between the devices then the receiver’s device (Bob) can auto-select it.
  • Alice and Bob complete the verification as defined by the selected verification method. This could involve their clients exchanging messages, Alice and Bob exchanging information out-of-band, and/or Alice and Bob interacting with their devices.
  • Alice’s and Bob’s clients send m.key.verification.done events to indicate that the verification was successful.

Verifications can be cancelled by either device at any time by sending an m.key.verification.cancel event with a code field that indicates the reason it was cancelled.

When using to-device messages, Alice may not know which of Bob’s devices to verify, or may not want to choose a specific device. In this case, Alice will send m.key.verification.request events to all of Bob’s devices. All of these events will use the same transaction ID. When Bob accepts or declines the verification on one of his devices (sending either an m.key.verification.ready or m.key.verification.cancel event), Alice will send an m.key.verification.cancel event to Bob’s other devices with a code of m.accepted in the case where Bob accepted the verification, or m.user in the case where Bob rejected the verification. This yields the following handshake when using to-device messages, assuming both Alice and Bob each have 2 devices, Bob’s first device accepts the key verification request, and Alice’s second device initiates the request. Note how Alice’s first device is not involved in the request or verification process. Also note that, although in this example, Bob’s device sends the m.key.verification.start, Alice’s device could also send that message. As well, the order of the m.key.verification.done messages could be reversed.

    +---------------+ +---------------+                    +-------------+ +-------------+
    | AliceDevice1  | | AliceDevice2  |                    | BobDevice1  | | BobDevice2  |
    +---------------+ +---------------+                    +-------------+ +-------------+
            |                 |                                   |               |
            |                 | m.key.verification.request        |               |
            |                 |---------------------------------->|               |
            |                 |                                   |               |
            |                 | m.key.verification.request        |               |
            |                 |-------------------------------------------------->|
            |                 |                                   |               |
            |                 |          m.key.verification.ready |               |
            |                 |<----------------------------------|               |
            |                 |                                   |               |
            |                 | m.key.verification.cancel         |               |
            |                 |-------------------------------------------------->|
            |                 |                                   |               |
            |                 |          m.key.verification.start |               |
            |                 |<----------------------------------|               |
            |                 |                                   |               |
            .
            .                       (verification messages)
            .
            |                 |                                   |               |
            |                 |           m.key.verification.done |               |
            |                 |<----------------------------------|               |
            |                 |                                   |               |
            |                 | m.key.verification.done           |               |
            |                 |---------------------------------->|               |
            |                 |                                   |               |

In contrast with the case of using to-devices messages, when using in-room messages, Alice only sends one request event (an event with type m.room.message with msgtype: m.key.verification.request, rather than an event with type m.key.verification.request), to the room. In addition, Alice does not send an m.key.verification.cancel event to tell Bob’s other devices that the request as already been accepted; instead, when Bob’s other devices see his m.key.verification.ready event, they will know that the request has already been accepted, and that they should ignore the request.

When using in-room messages and the room has encryption enabled, clients should ensure that encryption does not hinder the verification. For example, if the verification messages are encrypted, clients must ensure that all the recipient’s unverified devices receive the keys necessary to decrypt the messages, even if they would normally not be given the keys to decrypt messages in the room. Alternatively, verification messages may be sent unencrypted, though this is not encouraged.

Upon receipt of Alice’s m.key.verification.request message, if Bob’s device does not understand any of the methods, it should not cancel the request as one of his other devices may support the request. Instead, Bob’s device should tell Bob that no supported method was found, and allow him to manually reject the request.

The prompt for Bob to accept/reject Alice’s request (or the unsupported method prompt) should be automatically dismissed 10 minutes after the timestamp (in the case of to-device messages) or origin_ts (in the case of in-room messages) field or 2 minutes after Bob’s client receives the message, whichever comes first, if Bob does not interact with the prompt. The prompt should additionally be hidden if an appropriate m.key.verification.cancel message is received.

If Bob rejects the request, Bob’s client must send an m.key.verification.cancel event with code set to m.user. Upon receipt, Alice’s device should tell her that Bob does not want to verify her device and, if the request was sent as a to-device message, send m.key.verification.cancel messages to all of Bob’s devices to notify them that the request was rejected.

If Alice’s and Bob’s clients both send an m.key.verification.start message, and both specify the same verification method, then the m.key.verification.start message sent by the user whose ID is the lexicographically largest user ID should be ignored, and the situation should be treated the same as if only the user with the lexicographically smallest user ID had sent the m.key.verification.start message. In the case where the user IDs are the same (that is, when a user is verifying their own device), then the device IDs should be compared instead. If the two m.key.verification.start messages do not specify the same verification method, then the verification should be cancelled with a code of m.unexpected_message.

When verifying using to-device messages, an m.key.verification.start message can also be sent independently of any request, specifying the verification method to use. This behaviour is deprecated, and new clients should not begin verifications in this way. However, clients should handle such verifications started by other clients.

Individual verification methods may add additional steps, events, and properties to the verification messages. Event types for methods defined in this specification must be under the m.key.verification namespace and any other event types must be namespaced according to the Java package naming convention.

m.room.message with msgtype: m.key.verification.request


Requests a key verification in a room. When requesting a key verification using to-device messaging, an event with type m.key.verification.request should be used.

Event type: Message event

Content

Name Type Description
body string

A fallback message to alert users that their client does not support the key verification framework, and that they should use a different method to verify keys. For example, “Alice is requesting to verify keys with you. However, your client does not support this method, so you will need to use the legacy method of key verification.”

Clients that do support the key verification framework should hide the body and instead present the user with an interface to accept or reject the key verification.

format string

The format used in the formatted_body. Currently only org.matrix.custom.html is supported.

formatted_body string

The formatted version of the body. This is required if format is specified. As with the body, clients that do support the key verification framework should hide the formatted body and instead present the user with an interface to accept or reject the key verification.

from_device string

Required: The device ID which is initiating the request.

methods [string]

Required: The verification methods supported by the sender.

msgtype string

Required:

One of: [m.key.verification.request].

to string

Required: The user that the verification request is intended for. Users who are not named in this field and who did not send this event should ignore all other events that have an m.reference relationship with this event.

Examples

{
  "content": {
    "body": "Alice is requesting to verify your device, but your client does not support verification, so you may need to use a different verification method.",
    "from_device": "AliceDevice2",
    "methods": [
      "m.sas.v1"
    ],
    "msgtype": "m.key.verification.request",
    "to": "@bob:example.org"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@alice:example.org",
  "type": "m.room.message",
  "unsigned": {
    "age": 1234
  }
}

m.key.verification.request


Requests a key verification using to-device messaging. When requesting a key verification in a room, a m.room.message should be used, with m.key.verification.request as msgtype.

Event type: Message event

Content

Name Type Description
from_device string

Required: The device ID which is initiating the request.

methods [string]

Required: The verification methods supported by the sender.

timestamp integer

Required when sent as a to-device message. The POSIX timestamp in milliseconds for when the request was made. If the request is in the future by more than 5 minutes or more than 10 minutes in the past, the message should be ignored by the receiver.

transaction_id string

Required when sent as a to-device message. An opaque identifier for the verification request. Must be unique with respect to the devices involved.

Examples

{
  "content": {
    "from_device": "AliceDevice2",
    "methods": [
      "m.sas.v1"
    ],
    "timestamp": 1559598944869,
    "transaction_id": "S0meUniqueAndOpaqueString"
  },
  "type": "m.key.verification.request"
}

m.key.verification.ready


Accepts a key verification request. Sent in response to an m.key.verification.request event.

Event type: Message event

Content

Name Type Description
from_device string

Required: The device ID which is accepting the request.

m.relates_to VerificationRelatesTo

Required when sent as an in-room message. Indicates the m.key.verification.request that this message is related to. Note that for encrypted messages, this property should be in the unencrypted portion of the event.

methods [string]

Required: The verification methods supported by the sender, corresponding to the verification methods indicated in the m.key.verification.request message.

transaction_id string

Required when sent as a to-device message. The transaction ID of the verification request, as given in the m.key.verification.request message.

VerificationRelatesTo
Name Type Description
event_id string

The event ID of the m.key.verification.request that this message is related to.

rel_type string

The relationship type. Currently, this can only be an m.reference relationship type.

One of: [m.reference].

Examples

{
  "content": {
    "from_device": "BobDevice1",
    "methods": [
      "m.sas.v1"
    ],
    "transaction_id": "S0meUniqueAndOpaqueString"
  },
  "type": "m.key.verification.ready"
}

m.key.verification.start


Begins a key verification process. Typically sent as a to-device event. The method field determines the type of verification. The fields in the event will differ depending on the method. This definition includes fields that are in common among all variants.

Event type: Message event

Content

Name Type Description
from_device string

Required: The device ID which is initiating the process.

m.relates_to VerificationRelatesTo

Required when sent as an in-room message. Indicates the m.key.verification.request that this message is related to. Note that for encrypted messages, this property should be in the unencrypted portion of the event.

method string

Required: The verification method to use.

next_method string

Optional method to use to verify the other user’s key with. Applicable when the method chosen only verifies one user’s key. This field will never be present if the method verifies keys both ways.

transaction_id string

Required when sent as a to-device message. An opaque identifier for the verification process. Must be unique with respect to the devices involved. Must be the same as the transaction_id given in the m.key.verification.request if this process is originating from a request.

VerificationRelatesTo
Name Type Description
event_id string

The event ID of the m.key.verification.request that this message is related to.

rel_type string

The relationship type. Currently, this can only be an m.reference relationship type.

One of: [m.reference].

Examples

{
  "content": {
    "from_device": "BobDevice1",
    "method": "m.sas.v1",
    "transaction_id": "S0meUniqueAndOpaqueString"
  },
  "type": "m.key.verification.start"
}
{
  "content": {
    "from_device": "BobDevice1",
    "hashes": [
      "sha256"
    ],
    "key_agreement_protocols": [
      "curve25519"
    ],
    "message_authentication_codes": [
      "hkdf-hmac-sha256.v2",
      "hkdf-hmac-sha256"
    ],
    "method": "m.sas.v1",
    "short_authentication_string": [
      "decimal",
      "emoji"
    ],
    "transaction_id": "S0meUniqueAndOpaqueString"
  },
  "type": "m.key.verification.start"
}

m.key.verification.done


Indicates that a verification process/request has completed successfully.

Event type: Message event

Content

Name Type Description
m.relates_to VerificationRelatesTo

Required when sent as an in-room message. Indicates the m.key.verification.request that this message is related to. Note that for encrypted messages, this property should be in the unencrypted portion of the event.

transaction_id string

Required when sent as a to-device message. The opaque identifier for the verification process/request.

VerificationRelatesTo
Name Type Description
event_id string

The event ID of the m.key.verification.request that this message is related to.

rel_type string

The relationship type. Currently, this can only be an m.reference relationship type.

One of: [m.reference].

Examples

{
  "content": {
    "transaction_id": "S0meUniqueAndOpaqueString"
  },
  "type": "m.key.verification.done"
}

m.key.verification.cancel


Cancels a key verification process/request.

Event type: Message event

Content

Name Type Description
code string

Required: The error code for why the process/request was cancelled by the user. Error codes should use the Java package naming convention if not in the following list:

  • m.user: The user cancelled the verification.

  • m.timeout: The verification process timed out. Verification processes can define their own timeout parameters.

  • m.unknown_transaction: The device does not know about the given transaction ID.

  • m.unknown_method: The device does not know how to handle the requested method. This should be sent for m.key.verification.start messages and messages defined by individual verification processes.

  • m.unexpected_message: The device received an unexpected message. Typically raised when one of the parties is handling the verification out of order.

  • m.key_mismatch: The key was not verified.

  • m.user_mismatch: The expected user did not match the user verified.

  • m.invalid_message: The message received was invalid.

  • m.accepted: A m.key.verification.request was accepted by a different device. The device receiving this error can ignore the verification request.

Clients should be careful to avoid error loops. For example, if a device sends an incorrect message and the client returns m.invalid_message to which it gets an unexpected response with m.unexpected_message, the client should not respond again with m.unexpected_message to avoid the other device potentially sending another error response.

m.relates_to VerificationRelatesTo

Required when sent as an in-room message. Indicates the m.key.verification.request that this message is related to. Note that for encrypted messages, this property should be in the unencrypted portion of the event.

reason string

Required: A human readable description of the code. The client should only rely on this string if it does not understand the code.

transaction_id string

Required when sent as a to-device message. The opaque identifier for the verification process/request.

VerificationRelatesTo
Name Type Description
event_id string

The event ID of the m.key.verification.request that this message is related to.

rel_type string

The relationship type. Currently, this can only be an m.reference relationship type.

One of: [m.reference].

Examples

{
  "content": {
    "code": "m.user",
    "reason": "User rejected the key verification request",
    "transaction_id": "S0meUniqueAndOpaqueString"
  },
  "type": "m.key.verification.cancel"
}
Short Authentication String (SAS) verification

SAS verification is a user-friendly key verification process built off the common framework outlined above. SAS verification is intended to be a highly interactive process for users, and as such exposes verification methods which are easier for users to use.

The verification process is heavily inspired by Phil Zimmermann’s ZRTP key agreement handshake. A key part of key agreement in ZRTP is the hash commitment: the party that begins the Diffie-Hellman key sharing sends a hash of their part of the Diffie-Hellman exchange, and does not send their part of the Diffie-Hellman exchange until they have received the other party’s part. Thus an attacker essentially only has one attempt to attack the Diffie-Hellman exchange, and hence we can verify fewer bits while still achieving a high degree of security: if we verify n bits, then an attacker has a 1 in 2n chance of success. For example, if we verify 40 bits, then an attacker has a 1 in 1,099,511,627,776 chance (or less than 1 in 1012 chance) of success. A failed attack would result in a mismatched Short Authentication String, alerting users to the attack.

To advertise support for this method, clients use the name m.sas.v1 in the methods fields of the m.key.verification.request and m.key.verification.ready events.

The verification process takes place in two phases:

  1. Key agreement phase (based on ZRTP key agreement).
  2. Key verification phase (based on HMAC).

The process between Alice and Bob verifying each other would be:

  1. Alice and Bob establish a secure out-of-band connection, such as meeting in-person or a video call. “Secure” here means that either party cannot be impersonated, not explicit secrecy.

  2. Alice and Bob begin a key verification using the key verification framework as described above.

  3. Alice’s device sends Bob’s device an m.key.verification.start message. Alice’s device ensures it has a copy of Bob’s device key.

  4. Bob’s device receives the message and selects a key agreement protocol, hash algorithm, message authentication code, and SAS method supported by Alice’s device.

  5. Bob’s device ensures it has a copy of Alice’s device key.

  6. Bob’s device creates an ephemeral Curve25519 key pair (KBprivate, KBpublic), and calculates the hash (using the chosen algorithm) of the public key KBpublic.

  7. Bob’s device replies to Alice’s device with an m.key.verification.accept message.

  8. Alice’s device receives Bob’s message and stores the commitment hash for later use.

  9. Alice’s device creates an ephemeral Curve25519 key pair (KAprivate, KApublic) and replies to Bob’s device with an m.key.verification.key, sending only the public key KApublic.

  10. Bob’s device receives Alice’s message and replies with its own m.key.verification.key message containing its public key KBpublic.

  11. Alice’s device receives Bob’s message and verifies the commitment hash from earlier matches the hash of the key Bob’s device just sent and the content of Alice’s m.key.verification.start message.

  12. Both Alice’s and Bob’s devices perform an Elliptic-curve Diffie-Hellman using their private ephemeral key, and the other device’s ephemeral public key (ECDH(KAprivate, KBpublic) for Alice’s device and ECDH(KBprivate, KApublic) for Bob’s device), using the result as the shared secret.

  13. Both Alice and Bob’s devices display a SAS to their users, which is derived from the shared key using one of the methods in this section. If multiple SAS methods are available, clients should allow the users to select a method.

  14. Alice and Bob compare the strings shown by their devices, and tell their devices if they match or not.

  15. Assuming they match, Alice and Bob’s devices each calculate Message Authentication Codes (MACs) for:

    • Each of the keys that they wish the other user to verify (usually their device ed25519 key and their master cross-signing key).
    • The complete list of key IDs that they wish the other user to verify.

    The MAC calculation is defined below.

  16. Alice’s device sends Bob’s device an m.key.verification.mac message containing the MAC of Alice’s device keys and the MAC of her key IDs to be verified. Bob’s device does the same for Bob’s device keys and key IDs concurrently with Alice.

  17. When the other device receives the m.key.verification.mac message, the device calculates the MACs of its copies of the other device’s keys given in the message, as well as the MAC of the comma-separated, sorted, list of key IDs in the message. The device compares these with the MAC values given in the message, and if everything matches then the device keys are verified.

  18. Alice and Bob’s devices send m.key.verification.done messages to complete the verification.

The wire protocol looks like the following between Alice and Bob’s devices:

    +-------------+                    +-----------+
    | AliceDevice |                    | BobDevice |
    +-------------+                    +-----------+
          |                                 |
          | m.key.verification.start        |
          |-------------------------------->|
          |                                 |
          |       m.key.verification.accept |
          |<--------------------------------|
          |                                 |
          | m.key.verification.key          |
          |-------------------------------->|
          |                                 |
          |          m.key.verification.key |
          |<--------------------------------|
          |                                 |
          | m.key.verification.mac          |
          |-------------------------------->|
          |                                 |
          |          m.key.verification.mac |
          |<--------------------------------|
          |                                 |
Error and exception handling

At any point the interactive verification can go wrong. The following describes what to do when an error happens:

  • Alice or Bob can cancel the verification at any time. An m.key.verification.cancel message must be sent to signify the cancellation.
  • The verification can time out. Clients should time out a verification that does not complete within 10 minutes. Additionally, clients should expire a transaction_id which goes unused for 10 minutes after having last sent/received it. The client should inform the user that the verification timed out, and send an appropriate m.key.verification.cancel message to the other device.
  • When the same device attempts to initiate multiple verification attempts, the recipient should cancel all attempts with that device.
  • When a device receives an unknown transaction_id, it should send an appropriate m.key.verification.cancel message to the other device indicating as such. This does not apply for inbound m.key.verification.start or m.key.verification.cancel messages.
  • If the two devices do not share a common key share, hash, HMAC, or SAS method then the device should notify the other device with an appropriate m.key.verification.cancel message.
  • If the user claims the Short Authentication Strings do not match, the device should send an appropriate m.key.verification.cancel message to the other device.
  • If the device receives a message out of sequence or that it was not expecting, it should notify the other device with an appropriate m.key.verification.cancel message.
Verification messages specific to SAS

Building off the common framework, the following events are involved in SAS verification.

The m.key.verification.cancel event is unchanged, however the following error codes are used in addition to those already specified:

  • m.unknown_method: The devices are unable to agree on the key agreement, hash, MAC, or SAS method.
  • m.mismatched_commitment: The hash commitment did not match.
  • m.mismatched_sas: The SAS did not match.

m.key.verification.start with method: m.sas.v1


Begins a SAS key verification process using the m.sas.v1 method.

Event type: Message event

Content

Name Type Description
from_device string

Required: The device ID which is initiating the process.

hashes [string]

Required: The hash methods the sending device understands. Must include at least sha256.

key_agreement_protocols [string]

Required: The key agreement protocols the sending device understands. Should include at least curve25519-hkdf-sha256.

m.relates_to VerificationRelatesTo

Required when sent as an in-room message. Indicates the m.key.verification.request that this message is related to. Note that for encrypted messages, this property should be in the unencrypted portion of the event.

message_authentication_codes [string]

Required: The message authentication code methods that the sending device understands. Must include at least hkdf-hmac-sha256.v2. Should also include hkdf-hmac-sha256 for compatibility with older clients, though this identifier is deprecated and will be removed in a future version of the spec.

method string

Required: The verification method to use.

One of: [m.sas.v1].

short_authentication_string [string]

Required: The SAS methods the sending device (and the sending device’s user) understands. Must include at least decimal. Optionally can include emoji.

transaction_id string

Required when sent as a to-device message. An opaque identifier for the verification process. Must be unique with respect to the devices involved. Must be the same as the transaction_id given in the m.key.verification.request if this process is originating from a request.

VerificationRelatesTo
Name Type Description
event_id string

The event ID of the m.key.verification.request that this message is related to.

rel_type string

The relationship type. Currently, this can only be an m.reference relationship type.

One of: [m.reference].

Examples

{
  "content": {
    "from_device": "BobDevice1",
    "hashes": [
      "sha256"
    ],
    "key_agreement_protocols": [
      "curve25519"
    ],
    "message_authentication_codes": [
      "hkdf-hmac-sha256.v2",
      "hkdf-hmac-sha256"
    ],
    "method": "m.sas.v1",
    "short_authentication_string": [
      "decimal",
      "emoji"
    ],
    "transaction_id": "S0meUniqueAndOpaqueString"
  },
  "type": "m.key.verification.start"
}

m.key.verification.accept


Accepts a previously sent m.key.verification.start message.

Event type: Message event

Content

Name Type Description
commitment string

Required: The hash (encoded as unpadded base64) of the concatenation of the device’s ephemeral public key (encoded as unpadded base64) and the canonical JSON representation of the m.key.verification.start message.

hash string

Required: The hash method the device is choosing to use, out of the options in the m.key.verification.start message.

key_agreement_protocol string

Required: The key agreement protocol the device is choosing to use, out of the options in the m.key.verification.start message.

m.relates_to VerificationRelatesTo

Required when sent as an in-room message. Indicates the m.key.verification.request that this message is related to. Note that for encrypted messages, this property should be in the unencrypted portion of the event.

message_authentication_code string

Required: The message authentication code method the device is choosing to use, out of the options in the m.key.verification.start message.

short_authentication_string [string]

Required: The SAS methods both devices involved in the verification process understand. Must be a subset of the options in the m.key.verification.start message.

transaction_id string

Required when sent as a to-device message. An opaque identifier for the verification process. Must be the same as the one used for the m.key.verification.start message.

VerificationRelatesTo
Name Type Description
event_id string

The event ID of the m.key.verification.request that this message is related to.

rel_type string

The relationship type. Currently, this can only be an m.reference relationship type.

One of: [m.reference].

Examples

{
  "content": {
    "commitment": "fQpGIW1Snz+pwLZu6sTy2aHy/DYWWTspTJRPyNp0PKkymfIsNffysMl6ObMMFdIJhk6g6pwlIqZ54rxo8SLmAg",
    "hash": "sha256",
    "key_agreement_protocol": "curve25519",
    "message_authentication_code": "hkdf-hmac-sha256.v2",
    "method": "m.sas.v1",
    "short_authentication_string": [
      "decimal",
      "emoji"
    ],
    "transaction_id": "S0meUniqueAndOpaqueString"
  },
  "type": "m.key.verification.accept"
}

m.key.verification.key


Sends the ephemeral public key for a device to the partner device.

Event type: Message event

Content

Name Type Description
key string

Required: The device’s ephemeral public key, encoded as unpadded base64.

m.relates_to VerificationRelatesTo

Required when sent as an in-room message. Indicates the m.key.verification.request that this message is related to. Note that for encrypted messages, this property should be in the unencrypted portion of the event.

transaction_id string

Required when sent as a to-device message. An opaque identifier for the verification process. Must be the same as the one used for the m.key.verification.start message.

VerificationRelatesTo
Name Type Description
event_id string

The event ID of the m.key.verification.request that this message is related to.

rel_type string

The relationship type. Currently, this can only be an m.reference relationship type.

One of: [m.reference].

Examples

{
  "content": {
    "key": "fQpGIW1Snz+pwLZu6sTy2aHy/DYWWTspTJRPyNp0PKkymfIsNffysMl6ObMMFdIJhk6g6pwlIqZ54rxo8SLmAg",
    "transaction_id": "S0meUniqueAndOpaqueString"
  },
  "type": "m.key.verification.key"
}

m.key.verification.mac


Sends the MAC of a device’s key to the partner device. The MAC is calculated using the method given in message_authentication_code property of the m.key.verification.accept message.

Event type: Message event

Content

Name Type Description
keys string

Required: The MAC of the comma-separated, sorted, list of key IDs given in the mac property, encoded as unpadded base64.

m.relates_to VerificationRelatesTo

Required when sent as an in-room message. Indicates the m.key.verification.request that this message is related to. Note that for encrypted messages, this property should be in the unencrypted portion of the event.

mac {string: string}

Required: A map of the key ID to the MAC of the key, using the algorithm in the verification process. The MAC is encoded as unpadded base64.

transaction_id string

Required when sent as a to-device message. An opaque identifier for the verification process. Must be the same as the one used for the m.key.verification.start message.

VerificationRelatesTo
Name Type Description
event_id string

The event ID of the m.key.verification.request that this message is related to.

rel_type string

The relationship type. Currently, this can only be an m.reference relationship type.

One of: [m.reference].

Examples

{
  "content": {
    "keys": "2Wptgo4CwmLo/Y8B8qinxApKaCkBG2fjTWB7AbP5Uy+aIbygsSdLOFzvdDjww8zUVKCmI02eP9xtyJxc/cLiBA",
    "mac": {
      "ed25519:ABCDEF": "fQpGIW1Snz+pwLZu6sTy2aHy/DYWWTspTJRPyNp0PKkymfIsNffysMl6ObMMFdIJhk6g6pwlIqZ54rxo8SLmAg"
    },
    "transaction_id": "S0meUniqueAndOpaqueString"
  },
  "type": "m.key.verification.mac"
}
MAC calculation

During the verification process, Message Authentication Codes (MACs) are calculated for keys and lists of key IDs.

The method used to calculate these MACs depends upon the value of the message_authentication_code property in the m.key.verification.accept message. All current implementations should use the hkdf-hmac-sha256.v2 method which is defined as follows:

  1. An HMAC key is generated using HKDF, as defined in RFC 5869, using SHA-256 as the hash function. The shared secret is supplied as the input keying material. No salt is used, and in the info parameter is the concatenation of:

    • The string MATRIX_KEY_VERIFICATION_MAC.
    • The Matrix ID of the user whose key is being MAC-ed.
    • The Device ID of the device sending the MAC.
    • The Matrix ID of the other user.
    • The Device ID of the device receiving the MAC.
    • The transaction_id being used.
    • The Key ID of the key being MAC-ed, or the string KEY_IDS if the item being MAC-ed is the list of key IDs.
  2. A MAC is then generated using HMAC as defined in RFC 2104 with the key generated above and using SHA-256 as the hash function.

    If a key is being MACed, the MAC is performed on the public key as encoded according to the key algorithm. For example, for ed25519 keys, it is the unpadded base64-encoded key.

    If the key list is being MACed, the list is sorted lexicographically and comma-separated with no extra whitespace added, with each key written in the form {algorithm}:{keyId}. For example, the key list could look like: ed25519:Cross+Signing+Key,ed25519:DEVICEID. In this way, the recipient can reconstruct the list from the names in the mac property of the m.key.verification.mac message and ensure that no keys were added or removed.

  3. The MAC values are base64-encoded and sent in a m.key.verification.mac message.

The MAC method hkdf-hmac-sha256 used an incorrect base64 encoding, due to a bug in the original implementation in libolm. To remedy this, hkdf-hmac-sha256.v2 was introduced, which calculates the MAC in the same way, but uses a correct base64 encoding. hkdf-hmac-sha256 is deprecated and will be removed in a future version of the spec. Use of hkdf-hmac-sha256 should be avoided whenever possible: if both parties support hkdf-hmac-sha256.v2, then hkdf-hmac-sha256 MUST not be used.

SAS HKDF calculation

In all of the SAS methods, HKDF is as defined in RFC 5869 and uses the previously agreed-upon hash function for the hash function. The shared secret is supplied as the input keying material. No salt is used. When the key_agreement_protocol is curve25519-hkdf-sha256, the info parameter is the concatenation of:

  • The string MATRIX_KEY_VERIFICATION_SAS|.
  • The Matrix ID of the user who sent the m.key.verification.start message, followed by |.
  • The Device ID of the device which sent the m.key.verification.start message, followed by |.
  • The public key from the m.key.verification.key message sent by the device which sent the m.key.verification.start message, encoded as unpadded base64, followed by |.
  • The Matrix ID of the user who sent the m.key.verification.accept message, followed by |.
  • The Device ID of the device which sent the m.key.verification.accept message, followed by |.
  • The public key from the m.key.verification.key message sent by the device which sent the m.key.verification.accept message, encoded as unpadded base64, followed by |.
  • The transaction_id being used.

When the key_agreement_protocol is the deprecated method curve25519, the info parameter is the concatenation of:

  • The string MATRIX_KEY_VERIFICATION_SAS.
  • The Matrix ID of the user who sent the m.key.verification.start message.
  • The Device ID of the device which sent the m.key.verification.start message.
  • The Matrix ID of the user who sent the m.key.verification.accept message.
  • The Device ID of the device which sent the m.key.verification.accept message.
  • The transaction_id being used.

New implementations are discouraged from implementing the curve25519 method.

HKDF is used over the plain shared secret as it results in a harder attack as well as more uniform data to work with.

SAS method: decimal

Generate 5 bytes using HKDF then take sequences of 13 bits to convert to decimal numbers (resulting in 3 numbers between 0 and 8191 inclusive each). Add 1000 to each calculated number.

The bitwise operations to get the numbers given the 5 bytes B0, B1, B2, B3, B4 would be:

  • First: (B0 ≪ 5|B1 ≫ 3) + 1000
  • Second: ((B1&0x7) ≪ 10|B2 ≪ 2|B3 ≫ 6) + 1000
  • Third: ((B3&0x3F) ≪ 7|B4 ≫ 1) + 1000

The digits are displayed to the user either with an appropriate separator, such as dashes, or with the numbers on individual lines.

SAS method: emoji

Generate 6 bytes using HKDF then split the first 42 bits into 7 groups of 6 bits, similar to how one would base64 encode something. Convert each group of 6 bits to a number and use the following table to get the corresponding emoji:

Number Emoji Unicode Description
0 🐶 U+1F436 Dog
1 🐱 U+1F431 Cat
2 🦁 U+1F981 Lion
3 🐎 U+1F40E Horse
4 🦄 U+1F984 Unicorn
5 🐷 U+1F437 Pig
6 🐘 U+1F418 Elephant
7 🐰 U+1F430 Rabbit
8 🐼 U+1F43C Panda
9 🐓 U+1F413 Rooster
10 🐧 U+1F427 Penguin
11 🐢 U+1F422 Turtle
12 🐟 U+1F41F Fish
13 🐙 U+1F419 Octopus
14 🦋 U+1F98B Butterfly
15 🌷 U+1F337 Flower
16 🌳 U+1F333 Tree
17 🌵 U+1F335 Cactus
18 🍄 U+1F344 Mushroom
19 🌏 U+1F30F Globe
20 🌙 U+1F319 Moon
21 ☁️ U+2601U+FE0F Cloud
22 🔥 U+1F525 Fire
23 🍌 U+1F34C Banana
24 🍎 U+1F34E Apple
25 🍓 U+1F353 Strawberry
26 🌽 U+1F33D Corn
27 🍕 U+1F355 Pizza
28 🎂 U+1F382 Cake
29 ❤️ U+2764U+FE0F Heart
30 😀 U+1F600 Smiley
31 🤖 U+1F916 Robot
32 🎩 U+1F3A9 Hat
33 👓 U+1F453 Glasses
34 🔧 U+1F527 Spanner
35 🎅 U+1F385 Santa
36 👍 U+1F44D Thumbs Up
37 ☂️ U+2602U+FE0F Umbrella
38 U+231B Hourglass
39 U+23F0 Clock
40 🎁 U+1F381 Gift
41 💡 U+1F4A1 Light Bulb
42 📕 U+1F4D5 Book
43 ✏️ U+270FU+FE0F Pencil
44 📎 U+1F4CE Paperclip
45 ✂️ U+2702U+FE0F Scissors
46 🔒 U+1F512 Lock
47 🔑 U+1F511 Key
48 🔨 U+1F528 Hammer
49 ☎️ U+260EU+FE0F Telephone
50 🏁 U+1F3C1 Flag
51 🚂 U+1F682 Train
52 🚲 U+1F6B2 Bicycle
53 ✈️ U+2708U+FE0F Aeroplane
54 🚀 U+1F680 Rocket
55 🏆 U+1F3C6 Trophy
56 U+26BD Ball
57 🎸 U+1F3B8 Guitar
58 🎺 U+1F3BA Trumpet
59 🔔 U+1F514 Bell
60 U+2693 Anchor
61 🎧 U+1F3A7 Headphones
62 📁 U+1F4C1 Folder
63 📌 U+1F4CC Pin

The emoji above were chosen to:

  • Be recognisable without colour.
  • Be recognisable at a small size.
  • Be recognisable by most cultures.
  • Be distinguishable from each other.
  • Easily described by a few words.
  • Avoid symbols with negative connotations.
  • Be likely similar across multiple platforms.

Clients SHOULD show the emoji with the descriptions from the table, or appropriate translation of those descriptions. Client authors SHOULD collaborate to create a common set of translations for all languages.

Cross-signing

Rather than requiring Alice to verify each of Bob’s devices with each of her own devices and vice versa, the cross-signing feature allows users to sign their device keys such that Alice and Bob only need to verify once. With cross-signing, each user has a set of cross-signing keys that are used to sign their own device keys and other users’ keys, and can be used to trust device keys that were not verified directly.

Each user has three ed25519 key pairs for cross-signing:

  • a master key (MSK) that serves as the user’s identity in cross-signing and signs their other cross-signing keys;
  • a user-signing key (USK) – only visible to the user that it belongs to –that signs other users’ master keys; and
  • a self-signing key (SSK) that signs the user’s own device keys.

The master key may also be used to sign other items such as the backup key. The master key may also be signed by the user’s own device keys to aid in migrating from device verifications: if Alice’s device had previously verified Bob’s device and Bob’s device has signed his master key, then Alice’s device can trust Bob’s master key, and she can sign it with her user-signing key.

Users upload their cross-signing keys to the server using POST /_matrix/client/v3/keys/device_signing/upload. When Alice uploads new cross-signing keys, her user ID will appear in the changed property of the device_lists field of the /sync of response of all users who share an encrypted room with her. When Bob sees Alice’s user ID in his /sync, he will call POST /_matrix/client/v3/keys/query to retrieve Alice’s device and cross-signing keys.

If Alice has a device and wishes to send an encrypted message to Bob, she can trust Bob’s device if:

  • Alice’s device is using a master key that has signed her user-signing key,
  • Alice’s user-signing key has signed Bob’s master key,
  • Bob’s master key has signed Bob’s self-signing key, and
  • Bob’s self-signing key has signed Bob’s device key.

The following diagram illustrates how keys are signed:

    +------------------+                ..................   +----------------+
    | +--------------+ |   ..................            :   | +------------+ |
    | |              v v   v            :   :            v   v v            | |
    | |           +-----------+         :   :         +-----------+         | |
    | |           | Alice MSK |         :   :         |  Bob MSK  |         | |
    | |           +-----------+         :   :         +-----------+         | |
    | |             |       :           :   :           :       |           | |
    | |          +--+       :...        :   :        ...:       +--+        | |
    | |          v             v        :   :        v             v        | |
    | |    +-----------+ .............  :   :  ............. +-----------+  | |
    | |    | Alice SSK | : Alice USK :  :   :  :  Bob USK  : |  Bob SSK  |  | |
    | |    +-----------+ :...........:  :   :  :...........: +-----------+  | |
    | |      |  ...  |         :        :   :        :         |  ...  |    | |
    | |      V       V         :........:   :........:         V       V    | |
    | | +---------+   -+                                  +---------+   -+  | |
    | | | Devices | ...|                                  | Devices | ...|  | |
    | | +---------+   -+                                  +---------+   -+  | |
    | |      |  ...  |                                         |  ...  |    | |
    | +------+       |                                         |       +----+ |
    +----------------+                                         +--------------+

In the diagram, boxes represent keys and lines represent signatures with the arrows pointing from the signing key to the key being signed. Dotted boxes and lines represent keys and signatures that are only visible to the user who created them.

The following diagram illustrates Alice’s view, hiding the keys and signatures that she cannot see:

    +------------------+                +----------------+   +----------------+
    | +--------------+ |                |                |   | +------------+ |
    | |              v v                |                v   v v            | |
    | |           +-----------+         |             +-----------+         | |
    | |           | Alice MSK |         |             |  Bob MSK  |         | |
    | |           +-----------+         |             +-----------+         | |
    | |             |       |           |                       |           | |
    | |          +--+       +--+        |                       +--+        | |
    | |          v             v        |                          v        | |
    | |    +-----------+ +-----------+  |                    +-----------+  | |
    | |    | Alice SSK | | Alice USK |  |                    |  Bob SSK  |  | |
    | |    +-----------+ +-----------+  |                    +-----------+  | |
    | |      |  ...  |         |        |                      |  ...  |    | |
    | |      V       V         +--------+                      V       V    | |
    | | +---------+   -+                                  +---------+   -+  | |
    | | | Devices | ...|                                  | Devices | ...|  | |
    | | +---------+   -+                                  +---------+   -+  | |
    | |      |  ...  |                                         |  ...  |    | |
    | +------+       |                                         |       +----+ |
    +----------------+                                         +--------------+

Verification methods can be used to verify a user’s master key by using the master public key, encoded using unpadded base64, as the device ID, and treating it as a normal device. For example, if Alice and Bob verify each other using SAS, Alice’s m.key.verification.mac message to Bob may include "ed25519:alices+master+public+key": "alices+master+public+key" in the mac property. Servers therefore must ensure that device IDs will not collide with cross-signing public keys.

The cross-signing private keys can be stored on the server or shared with other devices using the Secrets module. When doing so, the master, user-signing, and self-signing keys are identified using the names m.cross_signing.master, m.cross_signing.user_signing, and m.cross_signing.self_signing, respectively, and the keys are base64-encoded before being encrypted.

Key and signature security

A user’s master key could allow an attacker to impersonate that user to other users, or other users to that user. Thus clients must ensure that the private part of the master key is treated securely. If clients do not have a secure means of storing the master key (such as a secret storage system provided by the operating system), then clients must not store the private part.

If a user’s client sees that any other user has changed their master key, that client must notify the user about the change before allowing communication between the users to continue.

Since device key IDs (ed25519:DEVICE_ID) and cross-signing key IDs (ed25519:PUBLIC_KEY) occupy the same namespace, clients must ensure that they use the correct keys when verifying.

While servers MUST not allow devices to have the same IDs as cross-signing keys, a malicious server could construct such a situation, so clients must not rely on the server being well-behaved and should take the following precautions against this.

  1. Clients MUST refer to keys by their public keys during the verification process, rather than only by the key ID.
  2. Clients MUST fix the keys that are being verified at the beginning of the verification process, and ensure that they do not change in the course of verification.
  3. Clients SHOULD also display a warning and MUST refuse to verify a user when they detect that the user has a device with the same ID as a cross-signing key.

A user’s user-signing and self-signing keys are intended to be easily replaceable if they are compromised by re-issuing a new key signed by the user’s master key and possibly by re-verifying devices or users. However, doing so relies on the user being able to notice when their keys have been compromised, and it involves extra work for the user, and so although clients do not have to treat the private parts as sensitively as the master key, clients should still make efforts to store the private part securely, or not store it at all. Clients will need to balance the security of the keys with the usability of signing users and devices when performing key verification.

To avoid leaking of social graphs, servers will only allow users to see:

  • signatures made by the user’s own master, self-signing or user-signing keys,
  • signatures made by the user’s own devices about their own master key,
  • signatures made by other users’ self-signing keys about their respective devices,
  • signatures made by other users’ master keys about their respective self-signing key, or
  • signatures made by other users’ devices about their respective master keys.

Users will not be able to see signatures made by other users’ user-signing keys.

POST /_matrix/client/v3/keys/device_signing/upload


Added in v1.1

Changed in v1.11: UIA is not always required for this endpoint.

Publishes cross-signing keys for the user.

This API endpoint uses the User-Interactive Authentication API.

User-Interactive Authentication MUST be performed, except in these cases:

  • there is no existing cross-signing master key uploaded to the homeserver, OR
  • there is an existing cross-signing master key and it exactly matches the cross-signing master key provided in the request body. If there are any additional keys provided in the request (self-signing key, user-signing key) they MUST also match the existing keys stored on the server. In other words, the request contains no new keys.

This allows clients to freely upload one set of keys, but not modify/overwrite keys if they already exist. Allowing clients to upload the same set of keys more than once makes this endpoint idempotent in the case where the response is lost over the network, which would otherwise cause a UIA challenge upon retry.

Rate-limited: No
Requires authentication: Yes

Request

Request body

Name Type Description
auth Authentication Data

Additional authentication information for the user-interactive authentication API.

master_key CrossSigningKey

Optional. The user's master key.

self_signing_key CrossSigningKey

Optional. The user's self-signing key. Must be signed by the accompanying master key, or by the user's most recently uploaded master key if no master key is included in the request.

user_signing_key CrossSigningKey

Optional. The user's user-signing key. Must be signed by the accompanying master key, or by the user's most recently uploaded master key if no master key is included in the request.

Authentication Data
Name Type Description
session string

The value of the session key given by the homeserver.

type string

The authentication type that the client is attempting to complete. May be omitted if session is given, and the client is reissuing a request which it believes has been completed out-of-band (for example, via the fallback mechanism).

<Other properties>

Keys dependent on the login type

CrossSigningKey
Name Type Description
keys {string: string}

Required: The public key. The object must have exactly one property, whose name is in the form <algorithm>:<unpadded_base64_public_key>, and whose value is the unpadded base64 public key.

signatures Signatures

Signatures of the key, calculated using the process described at Signing JSON. Optional for the master key. Other keys must be signed by the user's master key.

usage [string]

Required: What the key is used for.

user_id string

Required: The ID of the user the key belongs to.

Request body example

{
  "auth": {
    "example_credential": "verypoorsharedsecret",
    "session": "xxxxx",
    "type": "example.type.foo"
  },
  "master_key": {
    "keys": {
      "ed25519:base64+master+public+key": "base64+master+public+key"
    },
    "usage": [
      "master"
    ],
    "user_id": "@alice:example.com"
  },
  "self_signing_key": {
    "keys": {
      "ed25519:base64+self+signing+public+key": "base64+self+signing+master+public+key"
    },
    "signatures": {
      "@alice:example.com": {
        "ed25519:base64+master+public+key": "signature+of+self+signing+key"
      }
    },
    "usage": [
      "self_signing"
    ],
    "user_id": "@alice:example.com"
  },
  "user_signing_key": {
    "keys": {
      "ed25519:base64+user+signing+public+key": "base64+user+signing+master+public+key"
    },
    "signatures": {
      "@alice:example.com": {
        "ed25519:base64+master+public+key": "signature+of+user+signing+key"
      }
    },
    "usage": [
      "user_signing"
    ],
    "user_id": "@alice:example.com"
  }
}

Responses

Status Description
200 The provided keys were successfully uploaded.
400

The input was invalid in some way. This can include one of the following error codes:

  • M_INVALID_SIGNATURE: For example, the self-signing or user-signing key had an incorrect signature.
  • M_MISSING_PARAM: No master key is available.
403 The public key of one of the keys is the same as one of the user's device IDs, or the request is not authorized for any other reason.

200 response

{}

400 response

{
  "errcode": "M_INVALID_SIGNATURE",
  "error": "Invalid signature"
}

403 response

{
  "errcode": "M_FORBIDDEN",
  "error": "Key ID in use"
}

POST /_matrix/client/v3/keys/signatures/upload


Added in v1.1

Publishes cross-signing signatures for the user.

The signed JSON object must match the key previously uploaded or retrieved for the given key ID, with the exception of the signatures property, which contains the new signature(s) to add.

Rate-limited: No
Requires authentication: Yes

Request

Request body

Type Description
{User ID: {string: object}}

A map of user ID to a map of key ID to signed JSON object.

Request body example

{
  "@alice:example.com": {
    "HIJKLMN": {
      "algorithms": [
        "m.olm.v1.curve25519-aes-sha256",
        "m.megolm.v1.aes-sha"
      ],
      "device_id": "HIJKLMN",
      "keys": {
        "curve25519:HIJKLMN": "base64+curve25519+key",
        "ed25519:HIJKLMN": "base64+ed25519+key"
      },
      "signatures": {
        "@alice:example.com": {
          "ed25519:base64+self+signing+public+key": "base64+signature+of+HIJKLMN"
        }
      },
      "user_id": "@alice:example.com"
    },
    "base64+master+public+key": {
      "keys": {
        "ed25519:base64+master+public+key": "base64+master+public+key"
      },
      "signatures": {
        "@alice:example.com": {
          "ed25519:HIJKLMN": "base64+signature+of+master+key"
        }
      },
      "usage": [
        "master"
      ],
      "user_id": "@alice:example.com"
    }
  },
  "@bob:example.com": {
    "bobs+base64+master+public+key": {
      "keys": {
        "ed25519:bobs+base64+master+public+key": "bobs+base64+master+public+key"
      },
      "signatures": {
        "@alice:example.com": {
          "ed25519:base64+user+signing+public+key": "base64+signature+of+bobs+master+key"
        }
      },
      "usage": [
        "master"
      ],
      "user_id": "@bob:example.com"
    }
  }
}

Responses

Status Description
200 The provided signatures were processed.

200 response

Name Type Description
failures {User ID: {string: Error}}

A map from user ID to key ID to an error for any signatures that failed. If a signature was invalid, the errcode will be set to M_INVALID_SIGNATURE.

{
  "failures": {
    "@alice:example.com": {
      "HIJKLMN": {
        "errcode": "M_INVALID_SIGNATURE",
        "error": "Invalid signature"
      }
    }
  }
}
QR codes

[Added in v1.1]

Verifying by QR codes provides a quick way to verify when one of the parties has a device capable of scanning a QR code. The QR code encodes both parties’ master signing keys as well as a random shared secret that is used to allow bi-directional verification from a single scan.

To advertise the ability to show a QR code, clients use the names m.qr_code.show.v1 and m.reciprocate.v1 in the methods fields of the m.key.verification.request and m.key.verification.ready events. To advertise the ability to scan a QR code, clients use the names m.qr_code.scan.v1 and m.reciprocate.v1 in the methods fields of the m.key.verification.request and m.key.verification.ready events. Clients that support both showing and scanning QR codes would advertise m.qr_code.show.v1, m.qr_code.scan.v1, and m.reciprocate.v1 as methods.

The process between Alice and Bob verifying each other would be:

  1. Alice and Bob meet in person, and want to verify each other’s keys.

  2. Alice and Bob begin a key verification using the key verification framework as described above.

  3. Alice’s client displays a QR code that Bob is able to scan if Bob’s client indicated the ability to scan, an option to scan Bob’s QR code if her client is able to scan. Bob’s client prompts displays a QR code that Alice can scan if Alice’s client indicated the ability to scan, and an option to scan Alice’s QR code if his client is able to scan. The format for the QR code is described below. Other options, like starting SAS Emoji verification, can be presented alongside the QR code if the devices have appropriate support.

  4. Alice scans Bob’s QR code.

  5. Alice’s device ensures that the keys encoded in the QR code match the expected values for the keys. If not, Alice’s device displays an error message indicating that the code is incorrect, and sends a m.key.verification.cancel message to Bob’s device.

    Otherwise, at this point:

    • Alice’s device has now verified Bob’s key, and
    • Alice’s device knows that Bob has the correct key for her.

    Thus for Bob to verify Alice’s key, Alice needs to tell Bob that he has the right key.

  6. Alice’s device displays a message saying that the verification was successful because the QR code’s keys will have matched the keys expected for Bob. Bob’s device hasn’t had a chance to verify Alice’s keys yet so wouldn’t show the same message. Bob will know that he has the right key for Alice because Alice’s device will have shown this message, as otherwise the verification would be cancelled.

  7. Alice’s device sends an m.key.verification.start message with method set to m.reciprocate.v1 to Bob (see below). The message includes the shared secret from the QR code. This signals to Bob’s device that Alice has scanned Bob’s QR code.

    This message is merely a signal for Bob’s device to proceed to the next step, and is not used for verification purposes.

  8. Upon receipt of the m.key.verification.start message, Bob’s device ensures that the shared secret matches.

    If the shared secret does not match, it should display an error message indicating that an attack was attempted. (This does not affect Alice’s verification of Bob’s keys.)

    If the shared secret does match, it asks Bob to confirm that Alice has scanned the QR code.

  9. Bob sees Alice’s device confirm that the key matches, and presses the button on his device to indicate that Alice’s key is verified.

    Bob’s verification of Alice’s key hinges on Alice telling Bob the result of her scan. Since the QR code includes what Bob thinks Alice’s key is, Alice’s device can check whether Bob has the right key for her. Alice has no motivation to lie about the result, as getting Bob to trust an incorrect key would only affect communications between herself and Bob. Thus Alice telling Bob that the code was scanned successfully is sufficient for Bob to trust Alice’s key, under the assumption that this communication is done over a trusted medium (such as in-person).

  10. Both devices send an m.key.verification.done message.

QR code format

The QR codes to be displayed and scanned MUST be compatible with ISO/IEC 18004:2015 and contain a single segment that uses the byte mode encoding.

The error correction level can be chosen by the device displaying the QR code.

The binary segment MUST be of the following form:

  • the string MATRIX encoded as one ASCII byte per character (i.e. 0x4D, 0x41, 0x54, 0x52, 0x49, 0x58)
  • one byte indicating the QR code version (must be 0x02)
  • one byte indicating the QR code verification mode. Should be one of the following values:
    • 0x00 verifying another user with cross-signing
    • 0x01 self-verifying in which the current device does trust the master key
    • 0x02 self-verifying in which the current device does not yet trust the master key
  • the event ID or transaction_id of the associated verification request event, encoded as:
    • two bytes in network byte order (big-endian) indicating the length in bytes of the ID as a UTF-8 string
    • the ID encoded as a UTF-8 string
  • the first key, as 32 bytes. The key to use depends on the mode field:
    • if 0x00 or 0x01, then the current user’s own master cross-signing public key
    • if 0x02, then the current device’s Ed25519 signing key
  • the second key, as 32 bytes. The key to use depends on the mode field:
    • if 0x00, then what the device thinks the other user’s master cross-signing public key is
    • if 0x01, then what the device thinks the other device’s Ed25519 signing public key is
    • if 0x02, then what the device thinks the user’s master cross-signing public key is
  • a random shared secret, as a sequence of bytes. It is suggested to use a secret that is about 8 bytes long. Note: as we do not share the length of the secret, and it is not a fixed size, clients will just use the remainder of binary segment as the shared secret.

For example, if Alice displays a QR code encoding the following binary data:

      "MATRIX"    |ver|mode| len   | event ID
 4D 41 54 52 49 58  02  00   00 2D   21 41 42 43 44 ...
| user's cross-signing key    | other user's cross-signing key | shared secret
  00 01 02 03 04 05 06 07 ...   10 11 12 13 14 15 16 17 ...      20 21 22 23 24 25 26 27

this indicates that Alice is verifying another user (say Bob), in response to the request from event “$ABCD…”, her cross-signing key is 0001020304050607... (which is “AAECAwQFBg…” in base64), she thinks that Bob’s cross-signing key is 1011121314151617... (which is “EBESExQVFh…” in base64), and the shared secret is 2021222324252627 (which is “ICEiIyQlJic” in base64).

Verification messages specific to QR codes

m.key.verification.start with method: m.reciprocate.v1


Begins a key verification process using the m.reciprocate.v1 method, after scanning a QR code.

Event type: Message event

Content

Name Type Description
from_device string

Required: The device ID which is initiating the process.

m.relates_to VerificationRelatesTo

Required when sent as an in-room message. Indicates the m.key.verification.request that this message is related to. Note that for encrypted messages, this property should be in the unencrypted portion of the event.

method string

Required: The verification method to use.

One of: [m.reciprocate.v1].

secret string

Required: The shared secret from the QR code, encoded using unpadded base64.

transaction_id string

Required when sent as a to-device message. An opaque identifier for the verification process. Must be unique with respect to the devices involved. Must be the same as the transaction_id given in the m.key.verification.request if this process is originating from a request.

VerificationRelatesTo
Name Type Description
event_id string

The event ID of the m.key.verification.request that this message is related to.

rel_type string

The relationship type. Currently, this can only be an m.reference relationship type.

One of: [m.reference].

Examples

Sharing keys between devices

If Bob has an encrypted conversation with Alice on his computer, and then logs in through his phone for the first time, he may want to have access to the previously exchanged messages. To address this issue, several methods are provided to allow users to transfer keys from one device to another.

Key requests

When a device is missing keys to decrypt messages, it can request the keys by sending m.room_key_request to-device messages to other devices with action set to request.

If a device wishes to share the keys with that device, it can forward the keys to the first device by sending an encrypted m.forwarded_room_key to-device message. The first device should then send an m.room_key_request to-device message with action set to request_cancellation to the other devices that it had originally sent the key request to; a device that receives a request_cancellation should disregard any previously-received request message with the same request_id and requesting_device_id.

If a device does not wish to share keys with that device, it can indicate this by sending an m.room_key.withheld to-device message, as described in Reporting that decryption keys are withheld.

Key sharing can be a big attack vector, thus it must be done very carefully. Clients should only send keys requested by the verified devices of the same user, and should only request and accept forwarded keys from verified devices of the same user.

Server-side key backups

Devices may upload encrypted copies of keys to the server. When a device tries to read a message that it does not have keys for, it may request the key from the server and decrypt it. Backups are per-user, and users may replace backups with new backups.

In contrast with key requests, server-side key backups do not require another device to be online from which to request keys. However, as the session keys are stored on the server encrypted, the client requires a decryption key to decrypt the session keys.

To create a backup, a client will call POST /_matrix/client/v3/room_keys/version and define how the keys are to be encrypted through the backup’s auth_data; other clients can discover the backup by calling GET /_matrix/client/v3/room_keys/version. Keys are encrypted according to the backup’s auth_data and added to the backup by calling PUT /_matrix/client/v3/room_keys/keys or one of its variants, and can be retrieved by calling GET /_matrix/client/v3/room_keys/keys or one of its variants. Keys can only be written to the most recently created version of the backup. Backups can also be deleted using DELETE /_matrix/client/v3/room_keys/version/{version}, or individual keys can be deleted using DELETE /_matrix/client/v3/room_keys/keys or one of its variants.

Clients must only store keys in backups after they have ensured that the auth_data is trusted. This can be done either by:

  • checking that it is signed by the user’s master cross-signing key or by a verified device belonging to the same user, or
  • deriving the public key from a private key that it obtained from a trusted source. Trusted sources for the private key include the user entering the key, retrieving the key stored in secret storage, or obtaining the key via secret sharing from a verified device belonging to the same user.

When a client uploads a key for a session that the server already has a key for, the server will choose to either keep the existing key or replace it with the new key based on the key metadata as follows:

  • if the keys have different values for is_verified, then it will keep the key that has is_verified set to true;
  • if they have the same values for is_verified, then it will keep the key with a lower first_message_index;
  • and finally, if is_verified and first_message_index are equal, then it will keep the key with a lower forwarded_count.
Decryption key

Normally, the decryption key (i.e. the secret part of the encryption key) is stored on the server or shared with other devices using the Secrets module. When doing so, it is identified using the name m.megolm_backup.v1, and the key is base64-encoded before being encrypted.

If the backup decryption key is given directly to the user, the key should be presented as a string using the common cryptographic key representation.

The backup decryption key was previously referred to as a “recovery key”. However, this conflicted with common practice in client user interfaces, which often use the term “recovery key” to refer to the secret storage key. The term “recovery key” is no longer used in this specification.

Backup algorithm: m.megolm_backup.v1.curve25519-aes-sha2

When a backup is created with the algorithm set to m.megolm_backup.v1.curve25519-aes-sha2, the auth_data should have the following format:

AuthData


The format of the auth_data when a key backup is created with the algorithm set to m.megolm_backup.v1.curve25519-aes-sha2.

AuthData
Name Type Description
public_key string

Required: The curve25519 public key used to encrypt the backups, encoded in unpadded base64.

signatures object

Signatures of the auth_data, as Signed JSON

Examples

{
  "public_key": "abcdefg",
  "signatures": {
    "something": {
      "ed25519:something": "hijklmnop"
    }
  }
}

The session_data field in the backups is constructed as follows:

  1. Encode the session key to be backed up as a JSON object using the BackedUpSessionData format defined below.

  2. Generate an ephemeral curve25519 key, and perform an ECDH with the ephemeral key and the backup’s public key to generate a shared secret. The public half of the ephemeral key, encoded using unpadded base64, becomes the ephemeral property of the session_data.

  3. Using the shared secret, generate 80 bytes by performing an HKDF using SHA-256 as the hash, with a salt of 32 bytes of 0, and with the empty string as the info. The first 32 bytes are used as the AES key, the next 32 bytes are used as the MAC key, and the last 16 bytes are used as the AES initialization vector.

  4. Stringify the JSON object, and encrypt it using AES-CBC-256 with PKCS#7 padding. This encrypted data, encoded using unpadded base64, becomes the ciphertext property of the session_data.

  5. Pass an empty string through HMAC-SHA-256 using the MAC key generated above. The first 8 bytes of the resulting MAC are base64-encoded, and become the mac property of the session_data.

Step 5 was intended to pass the raw encrypted data, but due to a bug in libolm, all implementations have since passed an empty string instead.

Future versions of the spec will fix this problem. See MSC4048 for a potential new key backup algorithm version that would fix this issue.

BackedUpSessionData


The format of a backed-up session key, prior to encryption, when using the m.megolm_backup.v1.curve25519-aes-sha2 algorithm.

BackedUpSessionData
Name Type Description
algorithm string

Required: The end-to-end message encryption algorithm that the key is for. Must be m.megolm.v1.aes-sha2.

forwarding_curve25519_key_chain [string]

Required: Chain of Curve25519 keys through which this session was forwarded, via m.forwarded_room_key events.

sender_claimed_keys {string: string}

Required: A map from algorithm name (ed25519) to the Ed25519 signing key of the sending device.

sender_key string

Required: Unpadded base64-encoded device Curve25519 key.

session_key string

Required: Unpadded base64-encoded session key in session-export format.

Examples

{
  "algorithm": "m.megolm.v1.aes-sha2",
  "forwarding_curve25519_key_chain": [
    "hPQNcabIABgGnx3/ACv/jmMmiQHoeFfuLB17tzWp6Hw"
  ],
  "sender_claimed_keys": {
    "ed25519": "aj40p+aw64yPIdsxoog8jhPu9i7l7NcFRecuOQblE3Y"
  },
  "sender_key": "RF3s+E7RkTQTGF2d8Deol0FkQvgII2aJDf3/Jp5mxVU",
  "session_key": "AgAAAADxKHa9uFxcXzwYoNueL5Xqi69IkD4sni8Llf..."
}

GET /_matrix/client/v3/room_keys/keys


Added in v1.1

Retrieve the keys from the backup.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

query parameters
Name Type Description
version string

Required: The backup from which to retrieve the keys.


Responses

Status Description
200 The key data. If no keys are found, then an object with an empty rooms property will be returned ({"rooms": {}}).
404 The backup was not found.
429 This request was rate-limited.

200 response

Name Type Description
rooms {Room ID: RoomKeyBackup}

Required: A map of room IDs to room key backup data.

RoomKeyBackup
Name Type Description
sessions {string: KeyBackupData}

Required: A map of session IDs to key data.

KeyBackupData
Name Type Description
first_message_index integer

Required: The index of the first message in the session that the key can decrypt.

forwarded_count integer

Required: The number of times this key has been forwarded via key-sharing between devices.

is_verified boolean

Required: Whether the device backing up the key verified the device that the key is from.

session_data object

Required: Algorithm-dependent data. See the documentation for the backup algorithms in Server-side key backups for more information on the expected format of the data.

{
  "rooms": {
    "!room:example.org": {
      "sessions": {
        "sessionid1": {
          "first_message_index": 1,
          "forwarded_count": 0,
          "is_verified": true,
          "session_data": {
            "ciphertext": "base64+ciphertext+of+JSON+data",
            "ephemeral": "base64+ephemeral+key",
            "mac": "base64+mac+of+ciphertext"
          }
        }
      }
    }
  }
}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND",
  "error": "Unknown backup version."
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

PUT /_matrix/client/v3/room_keys/keys


Added in v1.1

Store several keys in the backup.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

query parameters
Name Type Description
version string

Required: The backup in which to store the keys. Must be the current backup.

Request body

Name Type Description
rooms {Room ID: RoomKeyBackup}

Required: A map of room IDs to room key backup data.

RoomKeyBackup
Name Type Description
sessions {string: KeyBackupData}

Required: A map of session IDs to key data.

KeyBackupData
Name Type Description
first_message_index integer

Required: The index of the first message in the session that the key can decrypt.

forwarded_count integer

Required: The number of times this key has been forwarded via key-sharing between devices.

is_verified boolean

Required: Whether the device backing up the key verified the device that the key is from.

session_data object

Required: Algorithm-dependent data. See the documentation for the backup algorithms in Server-side key backups for more information on the expected format of the data.

Request body example

{
  "rooms": {
    "!room:example.org": {
      "sessions": {
        "sessionid1": {
          "first_message_index": 1,
          "forwarded_count": 0,
          "is_verified": true,
          "session_data": {
            "ciphertext": "base64+ciphertext+of+JSON+data",
            "ephemeral": "base64+ephemeral+key",
            "mac": "base64+mac+of+ciphertext"
          }
        }
      }
    }
  }
}

Responses

Status Description
200 The update succeeded
403 The version specified does not match the current backup version. The current version will be included in the current_version field.
404 The backup was not found.
429 This request was rate-limited.

200 response

RoomKeysUpdateResponse
Name Type Description
count integer

Required: The number of keys stored in the backup

etag string

Required: The new etag value representing stored keys in the backup. See GET /room_keys/version/{version} for more details.

{
  "count": 10,
  "etag": "abcdefg"
}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "current_version": "42",
  "errcode": "M_WRONG_ROOM_KEYS_VERSION",
  "error": "Wrong backup version."
}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND",
  "error": "Unknown backup version"
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

DELETE /_matrix/client/v3/room_keys/keys


Added in v1.1

Delete the keys from the backup.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

query parameters
Name Type Description
version string

Required: The backup from which to delete the key


Responses

Status Description
200 The update succeeded
404 The backup was not found.
429 This request was rate-limited.

200 response

RoomKeysUpdateResponse
Name Type Description
count integer

Required: The number of keys stored in the backup

etag string

Required: The new etag value representing stored keys in the backup. See GET /room_keys/version/{version} for more details.

{
  "count": 10,
  "etag": "abcdefg"
}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND",
  "error": "Unknown backup version"
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

GET /_matrix/client/v3/room_keys/keys/{roomId}


Added in v1.1

Retrieve the keys from the backup for a given room.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string

Required: The ID of the room that the requested key is for.

query parameters
Name Type Description
version string

Required: The backup from which to retrieve the key.


Responses

Status Description
200 The key data. If no keys are found, then an object with an empty sessions property will be returned ({"sessions": {}}).
404 The backup was not found.
429 This request was rate-limited.

200 response

RoomKeyBackup
Name Type Description
sessions {string: KeyBackupData}

Required: A map of session IDs to key data.

KeyBackupData
Name Type Description
first_message_index integer

Required: The index of the first message in the session that the key can decrypt.

forwarded_count integer

Required: The number of times this key has been forwarded via key-sharing between devices.

is_verified boolean

Required: Whether the device backing up the key verified the device that the key is from.

session_data object

Required: Algorithm-dependent data. See the documentation for the backup algorithms in Server-side key backups for more information on the expected format of the data.

{
  "sessions": {
    "sessionid1": {
      "first_message_index": 1,
      "forwarded_count": 0,
      "is_verified": true,
      "session_data": {
        "ciphertext": "base64+ciphertext+of+JSON+data",
        "ephemeral": "base64+ephemeral+key",
        "mac": "base64+mac+of+ciphertext"
      }
    }
  }
}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND",
  "error": "Unknown backup version"
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

PUT /_matrix/client/v3/room_keys/keys/{roomId}


Added in v1.1

Store several keys in the backup for a given room.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string

Required: The ID of the room that the keys are for.

query parameters
Name Type Description
version string

Required: The backup in which to store the keys. Must be the current backup.

Request body

RoomKeyBackup
Name Type Description
sessions {string: KeyBackupData}

Required: A map of session IDs to key data.

KeyBackupData
Name Type Description
first_message_index integer

Required: The index of the first message in the session that the key can decrypt.

forwarded_count integer

Required: The number of times this key has been forwarded via key-sharing between devices.

is_verified boolean

Required: Whether the device backing up the key verified the device that the key is from.

session_data object

Required: Algorithm-dependent data. See the documentation for the backup algorithms in Server-side key backups for more information on the expected format of the data.

Request body example

{
  "sessions": {
    "sessionid1": {
      "first_message_index": 1,
      "forwarded_count": 0,
      "is_verified": true,
      "session_data": {
        "ciphertext": "base64+ciphertext+of+JSON+data",
        "ephemeral": "base64+ephemeral+key",
        "mac": "base64+mac+of+ciphertext"
      }
    }
  }
}

Responses

Status Description
200 The update succeeded
403 The version specified does not match the current backup version. The current version will be included in the current_version field.
404 The backup was not found.
429 This request was rate-limited.

200 response

RoomKeysUpdateResponse
Name Type Description
count integer

Required: The number of keys stored in the backup

etag string

Required: The new etag value representing stored keys in the backup. See GET /room_keys/version/{version} for more details.

{
  "count": 10,
  "etag": "abcdefg"
}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "current_version": "42",
  "errcode": "M_WRONG_ROOM_KEYS_VERSION",
  "error": "Wrong backup version."
}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND",
  "error": "Unknown backup version"
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

DELETE /_matrix/client/v3/room_keys/keys/{roomId}


Added in v1.1

Delete the keys from the backup for a given room.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string

Required: The ID of the room that the specified key is for.

query parameters
Name Type Description
version string

Required: The backup from which to delete the key.


Responses

Status Description
200 The update succeeded
404 The backup was not found.
429 This request was rate-limited.

200 response

RoomKeysUpdateResponse
Name Type Description
count integer

Required: The number of keys stored in the backup

etag string

Required: The new etag value representing stored keys in the backup. See GET /room_keys/version/{version} for more details.

{
  "count": 10,
  "etag": "abcdefg"
}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND",
  "error": "Unknown backup version"
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

GET /_matrix/client/v3/room_keys/keys/{roomId}/{sessionId}


Added in v1.1

Retrieve a key from the backup.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string

Required: The ID of the room that the requested key is for.

sessionId string

Required: The ID of the megolm session whose key is requested.

query parameters
Name Type Description
version string

Required: The backup from which to retrieve the key.


Responses

Status Description
200 The key data
404 The key or backup was not found.
429 This request was rate-limited.

200 response

KeyBackupData
Name Type Description
first_message_index integer

Required: The index of the first message in the session that the key can decrypt.

forwarded_count integer

Required: The number of times this key has been forwarded via key-sharing between devices.

is_verified boolean

Required: Whether the device backing up the key verified the device that the key is from.

session_data object

Required: Algorithm-dependent data. See the documentation for the backup algorithms in Server-side key backups for more information on the expected format of the data.

{
  "first_message_index": 1,
  "session_data": {
    "ciphertext": "base64+ciphertext+of+JSON+data",
    "ephemeral": "base64+ephemeral+key",
    "mac": "base64+mac+of+ciphertext"
  }
}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND",
  "error": "Key not found."
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

PUT /_matrix/client/v3/room_keys/keys/{roomId}/{sessionId}


Added in v1.1

Store a key in the backup.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string

Required: The ID of the room that the key is for.

sessionId string

Required: The ID of the megolm session that the key is for.

query parameters
Name Type Description
version string

Required: The backup in which to store the key. Must be the current backup.

Request body

KeyBackupData
Name Type Description
first_message_index integer

Required: The index of the first message in the session that the key can decrypt.

forwarded_count integer

Required: The number of times this key has been forwarded via key-sharing between devices.

is_verified boolean

Required: Whether the device backing up the key verified the device that the key is from.

session_data object

Required: Algorithm-dependent data. See the documentation for the backup algorithms in Server-side key backups for more information on the expected format of the data.

Request body example

{
  "first_message_index": 1,
  "session_data": {
    "ciphertext": "base64+ciphertext+of+JSON+data",
    "ephemeral": "base64+ephemeral+key",
    "mac": "base64+mac+of+ciphertext"
  }
}

Responses

Status Description
200 The update succeeded.
403 The version specified does not match the current backup version. The current version will be included in the current_version field.
429 This request was rate-limited.

200 response

RoomKeysUpdateResponse
Name Type Description
count integer

Required: The number of keys stored in the backup

etag string

Required: The new etag value representing stored keys in the backup. See GET /room_keys/version/{version} for more details.

{
  "count": 10,
  "etag": "abcdefg"
}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "current_version": "42",
  "errcode": "M_WRONG_ROOM_KEYS_VERSION",
  "error": "Wrong backup version."
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

DELETE /_matrix/client/v3/room_keys/keys/{roomId}/{sessionId}


Added in v1.1

Delete a key from the backup.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string

Required: The ID of the room that the specified key is for.

sessionId string

Required: The ID of the megolm session whose key is to be deleted.

query parameters
Name Type Description
version string

Required: The backup from which to delete the key


Responses

Status Description
200 The update succeeded
404 The backup was not found.
429 This request was rate-limited.

200 response

RoomKeysUpdateResponse
Name Type Description
count integer

Required: The number of keys stored in the backup

etag string

Required: The new etag value representing stored keys in the backup. See GET /room_keys/version/{version} for more details.

{
  "count": 10,
  "etag": "abcdefg"
}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND",
  "error": "Unknown backup version"
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

GET /_matrix/client/v3/room_keys/version


Added in v1.1

Get information about the latest backup version.

Rate-limited: Yes
Requires authentication: Yes

Request

No request parameters or request body.


Responses

Status Description
200 The information about the backup.
404 No backup exists.
429 This request was rate-limited.

200 response

Name Type Description
algorithm string

Required: The algorithm used for storing backups.

One of: [m.megolm_backup.v1.curve25519-aes-sha2].

auth_data object

Required: Algorithm-dependent data. See the documentation for the backup algorithms in Server-side key backups for more information on the expected format of the data.

count integer

Required: The number of keys stored in the backup.

etag string

Required: An opaque string representing stored keys in the backup. Clients can compare it with the etag value they received in the request of their last key storage request. If not equal, another client has modified the backup.

version string

Required: The backup version.

{
  "algorithm": "m.megolm_backup.v1.curve25519-aes-sha2",
  "auth_data": {
    "public_key": "abcdefg",
    "signatures": {
      "@alice:example.org": {
        "ed25519:deviceid": "signature"
      }
    }
  },
  "count": 42,
  "etag": "anopaquestring",
  "version": "1"
}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND",
  "error": "No current backup version"
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

POST /_matrix/client/v3/room_keys/version


Added in v1.1

Creates a new backup.

Rate-limited: Yes
Requires authentication: Yes

Request

Request body

Name Type Description
algorithm string

Required: The algorithm used for storing backups.

One of: [m.megolm_backup.v1.curve25519-aes-sha2].

auth_data object

Required: Algorithm-dependent data. See the documentation for the backup algorithms in Server-side key backups for more information on the expected format of the data.

Request body example

{
  "algorithm": "m.megolm_backup.v1.curve25519-aes-sha2",
  "auth_data": {
    "public_key": "abcdefg",
    "signatures": {
      "@alice:example.org": {
        "ed25519:deviceid": "signature"
      }
    }
  }
}

Responses

Status Description
200 The version id of the new backup.
429 This request was rate-limited.

200 response

Name Type Description
version string

Required: The backup version. This is an opaque string.

{
  "version": "1"
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

GET /_matrix/client/v3/room_keys/version/{version}


Added in v1.1

Get information about an existing backup.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
version string

Required: The backup version to get, as returned in the version parameter of the response in POST /_matrix/client/v3/room_keys/version or this endpoint.


Responses

Status Description
200 The information about the requested backup.
404 The backup specified does not exist.
429 This request was rate-limited.

200 response

Name Type Description
algorithm string

Required: The algorithm used for storing backups.

One of: [m.megolm_backup.v1.curve25519-aes-sha2].

auth_data object

Required: Algorithm-dependent data. See the documentation for the backup algorithms in Server-side key backups for more information on the expected format of the data.

count integer

Required: The number of keys stored in the backup.

etag string

Required: An opaque string representing stored keys in the backup. Clients can compare it with the etag value they received in the request of their last key storage request. If not equal, another client has modified the backup.

version string

Required: The backup version.

{
  "algorithm": "m.megolm_backup.v1.curve25519-aes-sha2",
  "auth_data": {
    "public_key": "abcdefg",
    "signatures": {
      "@alice:example.org": {
        "ed25519:deviceid": "signature"
      }
    }
  },
  "count": 42,
  "etag": "anopaquestring",
  "version": "1"
}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND",
  "error": "Unknown backup version"
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

PUT /_matrix/client/v3/room_keys/version/{version}


Added in v1.1

Update information about an existing backup. Only auth_data can be modified.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
version string

Required: The backup version to update, as returned in the version parameter in the response of POST /_matrix/client/v3/room_keys/version or GET /_matrix/client/v3/room_keys/version/{version}.

Request body

Name Type Description
algorithm string

Required: The algorithm used for storing backups. Must be the same as the algorithm currently used by the backup.

One of: [m.megolm_backup.v1.curve25519-aes-sha2].

auth_data object

Required: Algorithm-dependent data. See the documentation for the backup algorithms in Server-side key backups for more information on the expected format of the data.

version string

The backup version. If present, must be the same as the version in the path parameter.

Request body example

{
  "algorithm": "m.megolm_backup.v1.curve25519-aes-sha2",
  "auth_data": {
    "public_key": "abcdefg",
    "signatures": {
      "@alice:example.org": {
        "ed25519:deviceid": "signature"
      }
    }
  },
  "version": "1"
}

Responses

Status Description
200 The update succeeded.
400 A parameter was incorrect. For example, the algorithm does not match the current backup algorithm, or the version in the body does not match the version in the path.
404 The backup specified does not exist.
429 This request was rate-limited.

200 response

{}

400 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_INVALID_PARAM",
  "error": "Algorithm does not match"
}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND",
  "error": "Unknown backup version"
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

DELETE /_matrix/client/v3/room_keys/version/{version}


Added in v1.1

Delete an existing key backup. Both the information about the backup, as well as all key data related to the backup will be deleted.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
version string

Required: The backup version to delete, as returned in the version parameter in the response of POST /_matrix/client/v3/room_keys/version or GET /_matrix/client/v3/room_keys/version/{version}.


Responses

Status Description
200 The delete succeeded, or the specified backup was previously deleted.
404 The backup specified does not exist. If the backup was previously deleted, the call should succeed rather than returning an error.
429 This request was rate-limited.

200 response

{}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND",
  "error": "Unknown backup version"
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}
Key exports

Keys can be manually exported from one device to an encrypted file, copied to another device, and imported. The file is encrypted using a user-supplied passphrase, and is created as follows:

  1. Encode the sessions as a JSON object, formatted as described in Key export format.

  2. Generate a 512-bit key from the user-entered passphrase by computing PBKDF2(HMAC-SHA-512, passphrase, S, N, 512), where S is a 128-bit cryptographically-random salt and N is the number of rounds. N should be at least 100,000. The keys K and K’ are set to the first and last 256 bits of this generated key, respectively. K is used as an AES-256 key, and K’ is used as an HMAC-SHA-256 key.

  3. Serialize the JSON object as a UTF-8 string, and encrypt it using AES-CTR-256 with the key K generated above, and with a 128-bit cryptographically-random initialization vector, IV, that has bit 63 set to zero. (Setting bit 63 to zero in IV is needed to work around differences in implementations of AES-CTR.)

  4. Concatenate the following data:

Size (bytes) Description
1 Export format version, which must be 0x01.
16 The salt S.
16 The initialization vector IV.
4 The number of rounds N, as a big-endian unsigned 32-bit integer.
variable The encrypted JSON object.
32 The HMAC-SHA-256 of all the above string concatenated together, using K’ as the key.
  1. Base64-encode the string above. Newlines may be added to avoid overly long lines.

  2. Prepend the resulting string with -----BEGIN MEGOLM SESSION DATA-----, with a trailing newline, and append -----END MEGOLM SESSION DATA-----, with a leading and trailing newline.

Key export format

The exported sessions are formatted as a JSON array of ExportedSessionData objects described as follows:

ExportedSessionData


The format used to encode a Megolm session key for export.

This is similar to the format before encryption used for the session keys in Server-side key backups but adds the room_id and session_id fields.

ExportedSessionData
Name Type Description
algorithm string

Required: The end-to-end message encryption algorithm that the key is for. Must be m.megolm.v1.aes-sha2.

forwarding_curve25519_key_chain [string]

Required: Chain of Curve25519 keys through which this session was forwarded, via m.forwarded_room_key events.

room_id string

Required: The room where the session is used.

sender_claimed_keys {string: string}

Required: A map from algorithm name (ed25519) to the Ed25519 signing key of the sending device.

sender_key string

Required: Unpadded base64-encoded device Curve25519 key.

session_id string

Required: The Megolm session ID.

session_key string

Required: Unpadded base64-encoded session key in session-export format.

Examples

{
  "algorithm": "m.megolm.v1.aes-sha2",
  "forwarding_curve25519_key_chain": [
    "hPQNcabIABgGnx3/ACv/jmMmiQHoeFfuLB17tzWp6Hw"
  ],
  "sender_claimed_keys": {
    "ed25519": "aj40p+aw64yPIdsxoog8jhPu9i7l7NcFRecuOQblE3Y"
  },
  "sender_key": "RF3s+E7RkTQTGF2d8Deol0FkQvgII2aJDf3/Jp5mxVU",
  "session_key": "AgAAAADxKHa9uFxcXzwYoNueL5Xqi69IkD4sni8Llf..."
}

Messaging Algorithms

Messaging Algorithm Names

Messaging algorithm names use the extensible naming scheme used throughout this specification. Algorithm names that start with m. are reserved for algorithms defined by this specification. Implementations wanting to experiment with new algorithms must be uniquely globally namespaced following Java’s package naming conventions.

Algorithm names should be short and meaningful, and should list the primitives used by the algorithm so that it is easier to see if the algorithm is using a broken primitive.

A name of m.olm.v1 is too short: it gives no information about the primitives in use, and is difficult to extend for different primitives. However a name of m.olm.v1.ecdh-curve25519-hdkfsha256.hmacsha256.hkdfsha256-aes256-cbc-hmac64sha256 is too long despite giving a more precise description of the algorithm: it adds to the data transfer overhead and sacrifices clarity for human readers without adding any useful extra information.

m.olm.v1.curve25519-aes-sha2

The name m.olm.v1.curve25519-aes-sha2 corresponds to version 1 of the Olm ratchet, as defined by the Olm specification. This uses:

  • Curve25519 for the initial key agreement.
  • HKDF-SHA-256 for ratchet key derivation.
  • Curve25519 for the root key ratchet.
  • HMAC-SHA-256 for the chain key ratchet.
  • HKDF-SHA-256, AES-256 in CBC mode, and 8 byte truncated HMAC-SHA-256 for authenticated encryption.

Devices that support Olm must include “m.olm.v1.curve25519-aes-sha2” in their list of supported messaging algorithms, must list a Curve25519 device key, and must publish Curve25519 one-time keys.

An event encrypted using Olm has the following format:

{
  "type": "m.room.encrypted",
  "content": {
    "algorithm": "m.olm.v1.curve25519-aes-sha2",
    "sender_key": "<sender_curve25519_key>",
    "ciphertext": {
      "<device_curve25519_key>": {
        "type": 0,
        "body": "<encrypted_payload_base_64>"
      }
    }
  }
}

ciphertext is a mapping from device Curve25519 key to an encrypted payload for that device. body is a Base64-encoded Olm message body. type is an integer indicating the type of the message body: 0 for the initial pre-key message, 1 for ordinary messages.

Olm sessions will generate messages with a type of 0 until they receive a message. Once a session has decrypted a message it will produce messages with a type of 1.

When a client receives a message with a type of 0 it must first check if it already has a matching session. If it does then it will use that session to try to decrypt the message. If there is no existing session then the client must create a new session and use the new session to decrypt the message. A client must not persist a session or remove one-time keys used by a session until it has successfully decrypted a message using that session.

Messages with type 1 can only be decrypted with an existing session. If there is no matching session, the client must treat this as an invalid message.

The plaintext payload is of the form:

{
  "type": "<type of the plaintext event>",
  "content": "<content for the plaintext event>",
  "sender": "<sender_user_id>",
  "recipient": "<recipient_user_id>",
  "recipient_keys": {
    "ed25519": "<our_ed25519_key>"
  },
  "keys": {
    "ed25519": "<sender_ed25519_key>"
  }
}

The type and content of the plaintext message event are given in the payload.

Other properties are included in order to prevent an attacker from publishing someone else’s curve25519 keys as their own and subsequently claiming to have sent messages which they didn’t. sender must correspond to the user who sent the event, recipient to the local user, and recipient_keys to the local ed25519 key.

Clients must confirm that the sender_key property in the cleartext m.room.encrypted event body, and the keys.ed25519 property in the decrypted plaintext, match the keys returned by /keys/query for the given user. Clients must also verify the signature of the keys from the /keys/query response. Without this check, a client cannot be sure that the sender device owns the private part of the ed25519 key it claims to have in the Olm payload. This is crucial when the ed25519 key corresponds to a verified device.

If a client has multiple sessions established with another device, it should use the session from which it last received and successfully decrypted a message. For these purposes, a session that has not received any messages should use its creation time as the time that it last received a message. A client may expire old sessions by defining a maximum number of olm sessions that it will maintain for each device, and expiring sessions on a Least Recently Used basis. The maximum number of olm sessions maintained per device should be at least 4.

Recovering from undecryptable messages

Occasionally messages may be undecryptable by clients due to a variety of reasons. When this happens to an Olm-encrypted message, the client should assume that the Olm session has become corrupted and create a new one to replace it.

Megolm-encrypted messages generally do not have the same problem. Usually the key for an undecryptable Megolm-encrypted message will come later, allowing the client to decrypt it successfully. Olm does not have a way to recover from the failure, making this session replacement process required.

To establish a new session, the client sends an m.dummy to-device event to the other party to notify them of the new session details.

Clients should rate-limit the number of sessions it creates per device that it receives a message from. Clients should not create a new session with another device if it has already created one for that given device in the past 1 hour.

Clients should attempt to mitigate loss of the undecryptable messages. For example, Megolm sessions that were sent using the old session would have been lost. The client can attempt to retrieve the lost sessions through m.room_key_request messages.

Clients should send key requests for unknown sessions to all devices for the user which used the session rather than just the device_id or sender_key denoted on the event.

This is due to a deprecation of the fields. See m.megolm.v1.aes-sha2 for more information.

m.megolm.v1.aes-sha2

[Changed in v1.3]

The name m.megolm.v1.aes-sha2 corresponds to version 1 of the Megolm ratchet, as defined by the Megolm specification. This uses:

  • HMAC-SHA-256 for the hash ratchet.
  • HKDF-SHA-256, AES-256 in CBC mode, and 8 byte truncated HMAC-SHA-256 for authenticated encryption.
  • Ed25519 for message authenticity.

Devices that support Megolm must support Olm, and include “m.megolm.v1.aes-sha2” in their list of supported messaging algorithms.

An event encrypted using Megolm has the following format:

{
  "type": "m.room.encrypted",
  "content": {
    "algorithm": "m.megolm.v1.aes-sha2",
    "sender_key": "<sender_curve25519_key>",
    "device_id": "<sender_device_id>",
    "session_id": "<outbound_group_session_id>",
    "ciphertext": "<encrypted_payload_base_64>"
  }
}

The encrypted payload can contain any message event. The plaintext is of the form:

{
  "type": "<event_type>",
  "content": "<event_content>",
  "room_id": "<the room_id>"
}

We include the room ID in the payload, because otherwise the homeserver would be able to change the room a message was sent in.

Clients must guard against replay attacks by keeping track of the ratchet indices of Megolm sessions. They should reject messages with a ratchet index that they have already decrypted. Care should be taken in order to avoid false positives, as a client may decrypt the same event twice as part of its normal processing.

Similar to Olm events, clients should confirm that the user who sent the message corresponds to the user the message was expected to come from. For room events, this means ensuring the event’s sender, room_id, and the recorded session_id match a trusted session (eg: the session_id is already known and validated to the client).

As of v1.3, the sender_key and device_id keys are deprecated. They SHOULD continue to be sent, however they MUST NOT be used to verify the message’s source.

Clients MUST NOT store or lookup sessions using the sender_key or device_id.

In a future version of the specification the keys can be removed completely, including for sending new messages.

Removing the fields (eventually) improves privacy and security by masking the device which sent the encrypted message as well as reducing the client’s dependence on untrusted data: a malicious server (or similar attacker) could change these values, and other devices/users can simply lie about them too.

We can remove the fields, particularly the sender_key, because the session_id is already globally unique, therefore making storage and lookup possible without the need for added context from the sender_key or device_id.

Removing the dependence on the fields gives a privacy gain while also increasing the security of messages transmitted over Matrix.

In order to enable end-to-end encryption in a room, clients can send an m.room.encryption state event specifying m.megolm.v1.aes-sha2 as its algorithm property.

When creating a Megolm session in a room, clients must share the corresponding session key using Olm with the intended recipients, so that they can decrypt future messages encrypted using this session. An m.room_key event is used to do this. Clients must also handle m.room_key events sent by other devices in order to decrypt their messages.

When a client receives a Megolm session, the client MUST ensure that the session was received via an Olm channel, in order to ensure the authenticity of the messages.

When a client is updating a Megolm session in its store, the client MUST ensure:

  • that the updated session data comes from a trusted source, such as via a m.forwarded_room_key event from a verified device belonging to the same user, or from a m.room_key event.
  • that the new session key has a lower message index than the existing session key.

Protocol definitions

Events

m.room.encryption


Defines how messages sent in this room should be encrypted.

Event type: State event
State key A zero-length string.

Content

Name Type Description
algorithm string

Required: The encryption algorithm to be used to encrypt messages sent in this room.

One of: [m.megolm.v1.aes-sha2].

rotation_period_ms integer

How long the session should be used before changing it. 604800000 (a week) is the recommended default.

rotation_period_msgs integer

How many messages should be sent before changing the session. 100 is the recommended default.

Examples

{
  "content": {
    "algorithm": "m.megolm.v1.aes-sha2",
    "rotation_period_ms": 604800000,
    "rotation_period_msgs": 100
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "state_key": "",
  "type": "m.room.encryption",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

m.room.encrypted


This event type is used when sending encrypted events. It can be used either within a room (in which case it will have all of the normal properties in Room events), or as a to-device event.

Event type: Message event

Content

Name Type Description
algorithm string

Required: The encryption algorithm used to encrypt this event. The value of this field determines which other properties will be present.

One of: [m.olm.v1.curve25519-aes-sha2, m.megolm.v1.aes-sha2].

ciphertext string|{string: CiphertextInfo}

Required: The encrypted content of the event. Either the encrypted payload itself, in the case of a Megolm event, or a map from the recipient Curve25519 identity key to ciphertext information, in the case of an Olm event. For more details, see Messaging Algorithms.

device_id string

The ID of the sending device.

Deprecated: This field provides no additional security or privacy benefit for Megolm messages and must not be read from if the encrypted event is using Megolm. It should still be included on outgoing messages, however must not be used to find the corresponding session. See m.megolm.v1.aes-sha2 for more information.

Changed in v1.3: Previously this field was required for Megolm messages, however given it offers no additional security or privacy benefit it has been deprecated for Megolm messages. See m.megolm.v1.aes-sha2 for more information.

sender_key string

The Curve25519 key of the sender. Required (not deprecated) if not using Megolm.

Deprecated: This field provides no additional security or privacy benefit for Megolm messages and must not be read from if the encrypted event is using Megolm. It should still be included on outgoing messages, however must not be used to find the corresponding session. See m.megolm.v1.aes-sha2 for more information.

Changed in v1.3: Previously this field was required, however given it offers no additional security or privacy benefit it has been deprecated for Megolm messages. See m.megolm.v1.aes-sha2 for more information.

session_id string

The ID of the session used to encrypt the message. Required with Megolm.

CiphertextInfo
Name Type Description
body string

The encrypted payload.

type integer

The Olm message type.

Examples

{
  "content": {
    "algorithm": "m.megolm.v1.aes-sha2",
    "ciphertext": "AwgAEnACgAkLmt6qF84IK++J7UDH2Za1YVchHyprqTqsg...",
    "device_id": "RJYKSTBOIE",
    "sender_key": "IlRMeOPX2e0MurIyfWEucYBRVOEEUMrOHqn/8mLqMjA",
    "session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "type": "m.room.encrypted",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}
{
  "content": {
    "algorithm": "m.olm.v1.curve25519-aes-sha2",
    "ciphertext": {
      "7qZcfnBmbEGzxxaWfBjElJuvn7BZx+lSz/SvFrDF/z8": {
        "body": "AwogGJJzMhf/S3GQFXAOrCZ3iKyGU5ZScVtjI0KypTYrW...",
        "type": 0
      }
    },
    "sender_key": "Szl29ksW/L8yZGWAX+8dY1XyFi+i5wm+DRhTGkbMiwU"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "type": "m.room.encrypted",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

m.room_key


This event type is used to exchange keys for end-to-end encryption. It is encrypted as an m.room.encrypted event using Olm, then sent as a to-device event.

Event type: Message event

Content

Name Type Description
algorithm string

Required: The encryption algorithm the key in this event is to be used with.

One of: [m.megolm.v1.aes-sha2].

room_id string

Required: The room where the key is used.

session_id string

Required: The ID of the session that the key is for.

session_key string

Required: The key to be exchanged.

Examples

{
  "content": {
    "algorithm": "m.megolm.v1.aes-sha2",
    "room_id": "!Cuyf34gef24t:localhost",
    "session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ",
    "session_key": "AgAAAADxKHa9uFxcXzwYoNueL5Xqi69IkD4sni8LlfJL7qNBEY..."
  },
  "type": "m.room_key"
}

m.room_key_request


This event type is used to request keys for end-to-end encryption. It is sent as an unencrypted to-device event.

Event type: Message event

Content

Name Type Description
action string

Required:

One of: [request, request_cancellation].

body RequestedKeyInfo

Information about the requested key. Required when action is request.

request_id string

Required: A random string uniquely identifying the request for a key. If the key is requested multiple times, it should be reused. It should also reused in order to cancel a request.

requesting_device_id string

Required: ID of the device requesting the key.

RequestedKeyInfo
Name Type Description
algorithm string

Required: The encryption algorithm the requested key in this event is to be used with.

room_id string

Required: The room where the key is used.

sender_key string

The Curve25519 key of the device which initiated the session originally.

Deprecated: This field provides no additional security or privacy benefit and must not be read from. It should still be included on outgoing messages (if the event for which keys are being requested for also has a sender_key), however must not be used to find the corresponding session. See m.megolm.v1.aes-sha2 for more information.

Changed in v1.3: Previously this field was required, however given it offers no additional security or privacy benefit it has been deprecated. See m.megolm.v1.aes-sha2 for more information.

session_id string

Required: The ID of the session that the key is for.

Examples

{
  "content": {
    "action": "request_cancellation",
    "request_id": "1495474790150.19",
    "requesting_device_id": "RJYKSTBOIE"
  },
  "type": "m.room_key_request"
}
{
  "content": {
    "action": "request",
    "body": {
      "algorithm": "m.megolm.v1.aes-sha2",
      "room_id": "!Cuyf34gef24t:localhost",
      "sender_key": "RF3s+E7RkTQTGF2d8Deol0FkQvgII2aJDf3/Jp5mxVU",
      "session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ"
    },
    "request_id": "1495474790150.19",
    "requesting_device_id": "RJYKSTBOIE"
  },
  "type": "m.room_key_request"
}

m.forwarded_room_key


This event type is used to forward keys for end-to-end encryption. It is encrypted as an m.room.encrypted event using Olm, then sent as a to-device event.

Event type: Message event

Content

Name Type Description
algorithm string

Required: The encryption algorithm the key in this event is to be used with.

forwarding_curve25519_key_chain [string]

Required: Chain of Curve25519 keys. It starts out empty, but each time the key is forwarded to another device, the previous sender in the chain is added to the end of the list. For example, if the key is forwarded from A to B to C, this field is empty between A and B, and contains A’s Curve25519 key between B and C.

room_id string

Required: The room where the key is used.

sender_claimed_ed25519_key string

Required: The Ed25519 key of the device which initiated the session originally. It is ‘claimed’ because the receiving device has no way to tell that the original room_key actually came from a device which owns the private part of this key unless they have done device verification.

sender_key string

Required: The Curve25519 key of the device which initiated the session originally.

session_id string

Required: The ID of the session that the key is for.

session_key string

Required: The key to be exchanged.

withheld object

Indicates that the key cannot be used to decrypt all the messages from the session because a portion of the session was withheld as described in Reporting that decryption keys are withheld. This object must include the code and reason properties from the m.room_key.withheld message that was received by the sender of this message.

Examples

{
  "content": {
    "algorithm": "m.megolm.v1.aes-sha2",
    "forwarding_curve25519_key_chain": [
      "hPQNcabIABgGnx3/ACv/jmMmiQHoeFfuLB17tzWp6Hw"
    ],
    "room_id": "!Cuyf34gef24t:localhost",
    "sender_claimed_ed25519_key": "aj40p+aw64yPIdsxoog8jhPu9i7l7NcFRecuOQblE3Y",
    "sender_key": "RF3s+E7RkTQTGF2d8Deol0FkQvgII2aJDf3/Jp5mxVU",
    "session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ",
    "session_key": "AgAAAADxKHa9uFxcXzwYoNueL5Xqi69IkD4sni8Llf..."
  },
  "type": "m.forwarded_room_key"
}

m.dummy


This event type is used to indicate new Olm sessions for end-to-end encryption. Typically it is encrypted as an m.room.encrypted event, then sent as a to-device event.

The event does not have any content associated with it. The sending client is expected to send a key share request shortly after this message, causing the receiving client to process this m.dummy event as the most recent event and using the keyshare request to set up the session. The keyshare request and m.dummy combination should result in the original sending client receiving keys over the newly established session.

Event type: Message event

Content

Examples

{
  "content": {},
  "type": "m.dummy"
}
Key management API

GET /_matrix/client/v3/keys/changes


Gets a list of users who have updated their device identity keys since a previous sync token.

The server should include in the results any users who:

  • currently share a room with the calling user (ie, both users have membership state join); and
  • added new device identity keys or removed an existing device with identity keys, between from and to.
Rate-limited: No
Requires authentication: Yes

Request

Request parameters

query parameters
Name Type Description
from string

Required: The desired start point of the list. Should be the next_batch field from a response to an earlier call to /sync. Users who have not uploaded new device identity keys since this point, nor deleted existing devices with identity keys since then, will be excluded from the results.

to string

Required: The desired end point of the list. Should be the next_batch field from a recent call to /sync - typically the most recent such call. This may be used by the server as a hint to check its caches are up to date.


Responses

Status Description
200 The list of users who updated their devices.

200 response

Name Type Description
changed [string]

The Matrix User IDs of all users who updated their device identity keys.

left [string]

The Matrix User IDs of all users who may have left all the end-to-end encrypted rooms they previously shared with the user.

{
  "changed": [
    "@alice:example.com",
    "@bob:example.org"
  ],
  "left": [
    "@clara:example.com",
    "@doug:example.org"
  ]
}

POST /_matrix/client/v3/keys/claim


Claims one-time keys for use in pre-key messages.

The request contains the user ID, device ID and algorithm name of the keys that are required. If a key matching these requirements can be found, the response contains it. The returned key is a one-time key if one is available, and otherwise a fallback key.

One-time keys are given out in the order that they were uploaded via /keys/upload. (All keys uploaded within a given call to /keys/upload are considered equivalent in this regard; no ordering is specified within them.)

Servers must ensure that each one-time key is returned at most once, so when a key has been returned, no other request will ever return the same key.

Rate-limited: No
Requires authentication: Yes

Request

Request body

Name Type Description
one_time_keys {User ID: {string: string}}

Required: The keys to be claimed. A map from user ID, to a map from device ID to algorithm name.

timeout integer

The time (in milliseconds) to wait when downloading keys from remote servers. 10 seconds is the recommended default.

Request body example

{
  "one_time_keys": {
    "@alice:example.com": {
      "JLAFKJWSCS": "signed_curve25519"
    }
  },
  "timeout": 10000
}

Responses

Status Description
200 The claimed keys.

200 response

Name Type Description
failures {string: object}

If any remote homeservers could not be reached, they are recorded here. The names of the properties are the names of the unreachable servers.

If the homeserver could be reached, but the user or device was unknown, no failure is recorded. Instead, the corresponding user or device is missing from the one_time_keys result.

one_time_keys {User ID: {string: {string: string|KeyObject}}}

Required: One-time keys for the queried devices. A map from user ID, to a map from devices to a map from <algorithm>:<key_id> to the key object.

See the key algorithms section for information on the Key Object format.

If necessary, the claimed key might be a fallback key. Fallback keys are re-used by the server until replaced by the device.

KeyObject
Name Type Description
key string

Required: The key, encoded using unpadded base64.

signatures {User ID: object}

Required: Signature for the device. Mapped from user ID to signature object, containing mapping from key signing identifier to the signature (see also: Signing JSON)

{
  "one_time_keys": {
    "@alice:example.com": {
      "JLAFKJWSCS": {
        "signed_curve25519:AAAAHg": {
          "key": "zKbLg+NrIjpnagy+pIY6uPL4ZwEG2v+8F9lmgsnlZzs",
          "signatures": {
            "@alice:example.com": {
              "ed25519:JLAFKJWSCS": "FLWxXqGbwrb8SM3Y795eB6OA8bwBcoMZFXBqnTn58AYWZSqiD45tlBVcDa2L7RwdKXebW/VzDlnfVJ+9jok1Bw"
            }
          }
        }
      }
    }
  }
}

POST /_matrix/client/v3/keys/query


Returns the current devices and identity keys for the given users.

Rate-limited: No
Requires authentication: Yes

Request

Request body

Name Type Description
device_keys {User ID: [string]}

Required: The keys to be downloaded. A map from user ID, to a list of device IDs, or to an empty list to indicate all devices for the corresponding user.

timeout integer

The time (in milliseconds) to wait when downloading keys from remote servers. 10 seconds is the recommended default.

Request body example

{
  "device_keys": {
    "@alice:example.com": []
  },
  "timeout": 10000
}

Responses

Status Description
200 The device information

200 response

Name Type Description
device_keys {User ID: {string: DeviceInformation}}

Information on the queried devices. A map from user ID, to a map from device ID to device information. For each device, the information returned will be the same as uploaded via /keys/upload, with the addition of an unsigned property.

failures {string: object}

If any remote homeservers could not be reached, they are recorded here. The names of the properties are the names of the unreachable servers.

If the homeserver could be reached, but the user or device was unknown, no failure is recorded. Instead, the corresponding user or device is missing from the device_keys result.

master_keys {User ID: CrossSigningKey}

Information on the master cross-signing keys of the queried users. A map from user ID, to master key information. For each key, the information returned will be the same as uploaded via /keys/device_signing/upload, along with the signatures uploaded via /keys/signatures/upload that the requesting user is allowed to see.

Added in v1.1

self_signing_keys {User ID: CrossSigningKey}

Information on the self-signing keys of the queried users. A map from user ID, to self-signing key information. For each key, the information returned will be the same as uploaded via /keys/device_signing/upload.

Added in v1.1

user_signing_keys {User ID: CrossSigningKey}

Information on the user-signing key of the user making the request, if they queried their own device information. A map from user ID, to user-signing key information. The information returned will be the same as uploaded via /keys/device_signing/upload.

DeviceInformation
Name Type Description
algorithms [string]

Required: The encryption algorithms supported by this device.

device_id string

Required: The ID of the device these keys belong to. Must match the device ID used when logging in.

keys {string: string}

Required: Public identity keys. The names of the properties should be in the format <algorithm>:<device_id>. The keys themselves should be encoded as specified by the key algorithm.

signatures {User ID: {string: string}}

Required: Signatures for the device key object. A map from user ID, to a map from <algorithm>:<device_id> to the signature.

The signature is calculated using the process described at Signing JSON.

unsigned UnsignedDeviceInfo

Additional data added to the device key information by intermediate servers, and not covered by the signatures.

user_id string

Required: The ID of the user the device belongs to. Must match the user ID used when logging in.

UnsignedDeviceInfo
Name Type Description
device_display_name string

The display name which the user set on the device.

CrossSigningKey
Name Type Description
keys {string: string}

Required: The public key. The object must have exactly one property, whose name is in the form <algorithm>:<unpadded_base64_public_key>, and whose value is the unpadded base64 public key.

signatures Signatures

Signatures of the key, calculated using the process described at Signing JSON. Optional for the master key. Other keys must be signed by the user's master key.

usage [string]

Required: What the key is used for.

user_id string

Required: The ID of the user the key belongs to.

{
  "device_keys": {
    "@alice:example.com": {
      "JLAFKJWSCS": {
        "algorithms": [
          "m.olm.v1.curve25519-aes-sha2",
          "m.megolm.v1.aes-sha2"
        ],
        "device_id": "JLAFKJWSCS",
        "keys": {
          "curve25519:JLAFKJWSCS": "3C5BFWi2Y8MaVvjM8M22DBmh24PmgR0nPvJOIArzgyI",
          "ed25519:JLAFKJWSCS": "lEuiRJBit0IG6nUf5pUzWTUEsRVVe/HJkoKuEww9ULI"
        },
        "signatures": {
          "@alice:example.com": {
            "ed25519:JLAFKJWSCS": "dSO80A01XiigH3uBiDVx/EjzaoycHcjq9lfQX0uWsqxl2giMIiSPR8a4d291W1ihKJL/a+myXS367WT6NAIcBA"
          }
        },
        "unsigned": {
          "device_display_name": "Alice's mobile phone"
        },
        "user_id": "@alice:example.com"
      }
    }
  },
  "master_keys": {
    "@alice:example.com": {
      "keys": {
        "ed25519:base64+master+public+key": "base64+master+public+key"
      },
      "usage": [
        "master"
      ],
      "user_id": "@alice:example.com"
    }
  },
  "self_signing_keys": {
    "@alice:example.com": {
      "keys": {
        "ed25519:base64+self+signing+public+key": "base64+self+signing+master+public+key"
      },
      "signatures": {
        "@alice:example.com": {
          "ed25519:base64+master+public+key": "signature+of+self+signing+key"
        }
      },
      "usage": [
        "self_signing"
      ],
      "user_id": "@alice:example.com"
    }
  },
  "user_signing_keys": {
    "@alice:example.com": {
      "keys": {
        "ed25519:base64+user+signing+public+key": "base64+user+signing+master+public+key"
      },
      "signatures": {
        "@alice:example.com": {
          "ed25519:base64+master+public+key": "signature+of+user+signing+key"
        }
      },
      "usage": [
        "user_signing"
      ],
      "user_id": "@alice:example.com"
    }
  }
}

POST /_matrix/client/v3/keys/upload


Publishes end-to-end encryption keys for the device.

Rate-limited: No
Requires authentication: Yes

Request

Request body

Name Type Description
device_keys DeviceKeys

Identity keys for the device. May be absent if no new identity keys are required.

fallback_keys {string: string|KeyObject}

The public key which should be used if the device’s one-time keys are exhausted. The fallback key is not deleted once used, but should be replaced when additional one-time keys are being uploaded. The server will notify the client of the fallback key being used through /sync.

There can only be at most one key per algorithm uploaded, and the server will only persist one key per algorithm.

When uploading a signed key, an additional fallback: true key should be included to denote that the key is a fallback key.

May be absent if a new fallback key is not required.

Added in v1.2

one_time_keys {string: string|KeyObject}

One-time public keys for “pre-key” messages. The names of the properties should be in the format <algorithm>:<key_id>. The format of the key is determined by the key algorithm.

May be absent if no new one-time keys are required.

DeviceKeys
Name Type Description
algorithms [string]

Required: The encryption algorithms supported by this device.

device_id string

Required: The ID of the device these keys belong to. Must match the device ID used when logging in.

keys {string: string}

Required: Public identity keys. The names of the properties should be in the format <algorithm>:<device_id>. The keys themselves should be encoded as specified by the key algorithm.

signatures {User ID: {string: string}}

Required: Signatures for the device key object. A map from user ID, to a map from <algorithm>:<device_id> to the signature.

The signature is calculated using the process described at Signing JSON.

user_id string

Required: The ID of the user the device belongs to. Must match the user ID used when logging in.

KeyObject
Name Type Description
key string

Required: The key, encoded using unpadded base64.

signatures {User ID: object}

Required: Signature for the device. Mapped from user ID to signature object, containing mapping from key signing identifier to the signature (see also: Signing JSON)

Request body example

{
  "device_keys": {
    "algorithms": [
      "m.olm.v1.curve25519-aes-sha2",
      "m.megolm.v1.aes-sha2"
    ],
    "device_id": "JLAFKJWSCS",
    "keys": {
      "curve25519:JLAFKJWSCS": "3C5BFWi2Y8MaVvjM8M22DBmh24PmgR0nPvJOIArzgyI",
      "ed25519:JLAFKJWSCS": "lEuiRJBit0IG6nUf5pUzWTUEsRVVe/HJkoKuEww9ULI"
    },
    "signatures": {
      "@alice:example.com": {
        "ed25519:JLAFKJWSCS": "dSO80A01XiigH3uBiDVx/EjzaoycHcjq9lfQX0uWsqxl2giMIiSPR8a4d291W1ihKJL/a+myXS367WT6NAIcBA"
      }
    },
    "user_id": "@alice:example.com"
  },
  "fallback_keys": {
    "signed_curve25519:AAAAGj": {
      "fallback": true,
      "key": "zKbLg+NrIjpnagy+pIY6uPL4ZwEG2v+8F9lmgsnlZzs",
      "signatures": {
        "@alice:example.com": {
          "ed25519:JLAFKJWSCS": "FLWxXqGbwrb8SM3Y795eB6OA8bwBcoMZFXBqnTn58AYWZSqiD45tlBVcDa2L7RwdKXebW/VzDlnfVJ+9jok1Bw"
        }
      }
    }
  },
  "one_time_keys": {
    "signed_curve25519:AAAAHQ": {
      "key": "j3fR3HemM16M7CWhoI4Sk5ZsdmdfQHsKL1xuSft6MSw",
      "signatures": {
        "@alice:example.com": {
          "ed25519:JLAFKJWSCS": "IQeCEPb9HFk217cU9kw9EOiusC6kMIkoIRnbnfOh5Oc63S1ghgyjShBGpu34blQomoalCyXWyhaaT3MrLZYQAA"
        }
      }
    },
    "signed_curve25519:AAAAHg": {
      "key": "zKbLg+NrIjpnagy+pIY6uPL4ZwEG2v+8F9lmgsnlZzs",
      "signatures": {
        "@alice:example.com": {
          "ed25519:JLAFKJWSCS": "FLWxXqGbwrb8SM3Y795eB6OA8bwBcoMZFXBqnTn58AYWZSqiD45tlBVcDa2L7RwdKXebW/VzDlnfVJ+9jok1Bw"
        }
      }
    }
  }
}

Responses

Status Description
200 The provided keys were successfully uploaded.

200 response

Name Type Description
one_time_key_counts {string: integer}

Required: For each key algorithm, the number of unclaimed one-time keys of that type currently held on the server for this device. If an algorithm is not listed, the count for that algorithm is to be assumed zero.

{
  "one_time_key_counts": {
    "signed_curve25519": 20
  }
}
Extensions to /sync

This module adds an optional device_lists property to the /sync response, as specified below. The server need only populate this property for an incremental /sync (i.e., one where the since parameter was specified). The client is expected to use /keys/query or /keys/changes for the equivalent functionality after an initial sync, as documented in Tracking the device list for a user.

It also adds a device_one_time_keys_count property. Note the spelling difference with the one_time_key_counts property in the /keys/upload response.

[Added in v1.2] Finally, a device_unused_fallback_key_types property is added to list the key algorithms where the device has a fallback key that has not been used in a /keys/claim response. When a previously uploaded fallback key’s algorithm is missing from this list, the device should upload a replacement key alongside any necessary one-time keys to avoid the fallback key’s further usage. This property is required for inclusion, though previous versions of the specification did not have it. In addition to /versions, this can be a way to identify the server’s support for fallback keys.

Parameter Type Description
device_lists DeviceLists Optional. Information on e2e device updates. Note: only present on an incremental sync.
device_one_time_keys_count {string: integer} Optional. For each key algorithm, the number of unclaimed one-time keys currently held on the server for this device. If an algorithm is unlisted, the count for that algorithm is assumed to be zero. If this entire parameter is missing, the count for all algorithms is assumed to be zero.
device_unused_fallback_key_types [string] Required. The unused fallback key algorithms.

DeviceLists

Parameter Type Description
changed [string] List of users who have updated their device identity or cross-signing keys, or who now share an encrypted room with the client since the previous sync response.
left [string] List of users with whom we do not share any encrypted rooms anymore since the previous sync response.

For optimal performance, Alice should be added to changed in Bob’s sync only when she updates her devices or cross-signing keys, or when Alice and Bob now share a room but didn’t share any room previously. However, for the sake of simpler logic, a server may add Alice to changed when Alice and Bob share a new room, even if they previously already shared a room.

Example response:

{
  "next_batch": "s72595_4483_1934",
  "rooms": {"leave": {}, "join": {}, "invite": {}},
  "device_lists": {
    "changed": [
       "@alice:example.com",
    ],
    "left": [
       "@bob:example.com",
    ],
  },
  "device_one_time_keys_count": {
    "signed_curve25519": 20
  },
  "device_unused_fallback_key_types": ["signed_curve25519"]
}

Reporting that decryption keys are withheld

When sending an encrypted event to a room, a client can optionally signal to other devices in that room that it is not sending them the keys needed to decrypt the event. In this way, the receiving client can indicate to the user why it cannot decrypt the event, rather than just showing a generic error message.

In the same way, when one device requests keys from another using Key requests, the device from which the key is being requested may want to tell the requester that it is purposely not sharing the key.

If Alice withholds a megolm session from Bob for some messages in a room, and then later on decides to allow Bob to decrypt later messages, she can send Bob the megolm session, ratcheted up to the point at which she allows Bob to decrypt the messages. If Bob logs into a new device and uses key sharing to obtain the decryption keys, the new device will be sent the megolm sessions that have been ratcheted up. Bob’s old device can include the reason that the session was initially not shared by including a withheld property in the m.forwarded_room_key message that is an object with the code and reason properties from the m.room_key.withheld message.

m.room_key.withheld


This event type is used to indicate that the sender is not sharing room keys with the recipient. It is sent as a to-device event.

Possible values for code include:

  • m.blacklisted: the user/device was blacklisted.
  • m.unverified: the user/device was not verified, and the sender is only sharing keys with verified users/devices.
  • m.unauthorised: the user/device is not allowed to have the key. For example, this could be sent in response to a key request if the user/device was not in the room when the original message was sent.
  • m.unavailable: sent in reply to a key request if the device that the key is requested from does not have the requested key.
  • m.no_olm: an olm session could not be established.

In most cases, this event refers to a specific room key. The one exception to this is when the sender is unable to establish an olm session with the recipient. When this happens, multiple sessions will be affected. In order to avoid filling the recipient's device mailbox, the sender should only send one m.room_key.withheld message with no room_id nor session_id set. If the sender retries and fails to create an olm session again in the future, it should not send another m.room_key.withheld message with a code of m.no_olm, unless another olm session was previously established successfully. In response to receiving an m.room_key.withheld message with a code of m.no_olm, the recipient may start an olm session with the sender and send an m.dummy message to notify the sender of the new olm session. The recipient may assume that this m.room_key.withheld message applies to all encrypted room messages sent before it receives the message.

Event type: Message event

Content

Name Type Description
algorithm string

Required: The encryption algorithm for the key that this event is about.

One of: [m.megolm.v1.aes-sha2].

code string

Required: A machine-readable code for why the key was not sent. Codes beginning with m. are reserved for codes defined in the Matrix specification. Custom codes must use the Java package naming convention.

One of: [m.blacklisted, m.unverified, m.unauthorised, m.unavailable, m.no_olm].

reason string

A human-readable reason for why the key was not sent. The receiving client should only use this string if it does not understand the code.

room_id string

Required if code is not m.no_olm. The room for the key that this event is about.

sender_key string

Required: The unpadded base64-encoded device curve25519 key of the event's sender.

session_id string

Required of code is not m.no_olm. The session ID of the key that this event is aboutis for.

Examples

{
  "content": {
    "algorithm": "m.megolm.v1.aes-sha2",
    "code": "m.unverified",
    "reason": "Device not verified",
    "room_id": "!Cuyf34gef24t:localhost",
    "sender_key": "RF3s+E7RkTQTGF2d8Deol0FkQvgII2aJDf3/Jp5mxVU",
    "session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ"
  },
  "type": "m.room_key.withheld"
}

Secrets

[Added in v1.1]

Clients may have secret information that they wish to be made available to other authorised clients, but that the server should not be able to see, so the information must be encrypted as it passes through the server. This can be done either asynchronously, by storing encrypted data on the server for later retrieval, or synchronously, by sending messages to each other.

Each secret has an identifier that is used by clients to refer to the secret when storing, fetching, requesting, or sharing the secret. Secrets are plain strings; structured data can be stored by encoding it as a string.

The mechanism described in this section is known as “secure secret storage and sharing”, “SSSS”, or “4S”.

Storage

When secrets are stored on the server, they are stored in the user’s account-data, using an event type equal to the secret’s identifier. The keys that secrets are encrypted with are described by data that is also stored in the user’s account-data. Users can have multiple keys, allowing them to control what sets of secrets clients can access, depending on what keys are given to them.

Key storage

Each key has an ID, and the description of the key is stored in the user’s account data using the event type m.secret_storage.key.[key ID]. The contents of the account data for the key will include an algorithm property, which indicates the encryption algorithm used, as well as a name property, which is a human-readable name. Key descriptions may also have a passphrase property for generating the key from a user-entered passphrase, as described in deriving keys from passphrases.

KeyDescription

Parameter Type Description
name string Optional. The name of the key. If not given, the client may use a generic name such as “Unnamed key”, or “Default key” if the key is marked as the default key (see below).
algorithm string Required. The encryption algorithm to be used for this key. Currently, only m.secret_storage.v1.aes-hmac-sha2 is supported.
passphrase string See deriving keys from passphrases section for a description of this property.

Other properties depend on the encryption algorithm, and are described below.

A key can be marked as the “default” key by setting the user’s account data with event type m.secret_storage.default_key to an object that has the ID of the key as its key property. The default key will be used to encrypt all secrets that the user would expect to be available on all their clients. Unless the user specifies otherwise, clients will try to use the default key to decrypt secrets.

Clients that want to present a simplified interface to users by not supporting multiple keys should use the default key if one is specified. If not default key is specified, the client may behave as if there is no key is present at all. When such a client creates a key, it should mark that key as being the default key.

DefaultKey

Parameter Type Description
key string Required. The ID of the default key.
m.secret_storage.v1.aes-hmac-sha2

For the purposes of allowing clients to check whether a user has correctly entered the key, keys for use with the m.secret_storage.v1.aes-hmac-sha2 algorithm are stored with some additional data.

When storing a key, clients SHOULD:

  1. Given the secret storage key, generate 64 bytes by performing an HKDF with SHA-256 as the hash, a salt of 32 bytes of 0, and the empty string as the info. The first 32 bytes are used as the AES key, and the next 32 bytes are used as the MAC key.

  2. Generate 16 random bytes, set bit 63 to 0 (in order to work around differences in AES-CTR implementations), and use this as the AES initialization vector (IV).

  3. Encrypt a message consisting of 32 bytes of 0, using AES-CTR-256 using the AES key and IV generated above.

  4. Pass the raw encrypted data through HMAC-SHA-256 using the MAC key generated above.

  5. Encode the IV from step 2, and the MAC from step 4, using unpadded base64, and store the results in the iv and mac properties respectively in the m.secret_storage.key.[key ID] account-data. (The ciphertext from step 3 is discarded after passing through the MAC calculation.)

This process can be repeated by a client checking if the key is correct: the MAC should match if the key is correct. Note, however, that these properties are optional. If they are not present, clients must assume that the key is valid.

Note also, that although clients SHOULD use unpadded base64 as specified above, some existing implementations use standard RFC4648-compliant base64 with padding, so clients must accept either encoding.

The structure of a m.secret_storage.key.[key ID] account data object for use with this algorithm is therefore as follows:

AesHmacSha2KeyDescription

Parameter Type Description
name string Optional. The name of the key.
algorithm string Required. The encryption algorithm to be used for this key: m.secret_storage.v1.aes-hmac-sha2.
passphrase object See deriving keys from passphrases section for a description of this property.
iv string Optional. The 16-byte initialization vector for the validation check, encoded as base64.
mac string Optional. The MAC of the result of encrypting 32 bytes of 0, encoded as base64.

For example, it could look like:

{
  "name": "m.default",
  "algorithm": "m.secret_storage.v1.aes-hmac-sha2",
  "iv": "random+data",
  "mac": "mac+of+encrypted+zeros"
}
Secret storage

Encrypted data is stored in the user’s account data using the event type defined by the feature that uses the data. The account data will have an encrypted property that is a map from key ID to an object. The algorithm from the m.secret_storage.key.[key ID] data for the given key defines how the other properties are interpreted, though it’s expected that most encryption schemes would have ciphertext and mac properties, where the ciphertext property is the unpadded base64-encoded ciphertext, and the mac is used to ensure the integrity of the data.

Secret

Parameter Type Description
encrypted {string: object} Required. Map from key ID the encrypted data. The exact format for the encrypted data is dependent on the key algorithm. See the definition of AesHmacSha2EncryptedData in the m.secret_storage.v1.aes-hmac-sha2 section.

Example:

Some secret is encrypted using keys with ID key_id_1 and key_id_2:

org.example.some.secret:

{
  "encrypted": {
    "key_id_1": {
      "ciphertext": "base64+encoded+encrypted+data",
      "mac": "base64+encoded+mac",
      // ... other properties according to algorithm property in
      // m.secret_storage.key.key_id_1
    },
    "key_id_2": {
      // ...
    }
  }
}

and the key descriptions for the keys would be:

m.secret_storage.key.key_id_1:

{
  "name": "Some key",
  "algorithm": "m.secret_storage.v1.aes-hmac-sha2",
  // ... other properties according to algorithm
}

m.secret_storage.key.key_id_2:

{
  "name": "Some other key",
  "algorithm": "m.secret_storage.v1.aes-hmac-sha2",
  // ... other properties according to algorithm
}

If key_id_1 is the default key, then we also have:

m.secret_storage.default_key:

{
  "key": "key_id_1"
}
m.secret_storage.v1.aes-hmac-sha2

Secrets encrypted using the m.secret_storage.v1.aes-hmac-sha2 algorithm are encrypted using AES-CTR-256, and authenticated using HMAC-SHA-256. The secret is encrypted as follows:

  1. Given the secret storage key, generate 64 bytes by performing an HKDF with SHA-256 as the hash, a salt of 32 bytes of 0, and with the secret name as the info. The first 32 bytes are used as the AES key, and the next 32 bytes are used as the MAC key.

  2. Generate 16 random bytes, set bit 63 to 0 (in order to work around differences in AES-CTR implementations), and use this as the AES initialization vector (IV).

  3. Encrypt the data using AES-CTR-256 using the AES key and IV generated above.

  4. Pass the raw encrypted data through HMAC-SHA-256 using the MAC key generated above.

  5. Encode the IV from step 2, the ciphertext from step 3, and MAC from step 4, using unpadded base64, and store them as the iv, ciphertext, and mac properties respectively in the account data object.

    Note: some existing implementations encode these properties using standard RFC4648-compliant base64 with padding, so clients must accept either encoding.

The structure of the encrypted property of an account data object encrypted with this algorithm is therefore as follows:

AesHmacSha2EncryptedData

Parameter Type Description
iv string Required. The 16-byte initialization vector, encoded as base64.
ciphertext string Required. The AES-CTR-encrypted data, encoded as base64.
mac string Required. The MAC, encoded as base64.

For example, data encrypted using this algorithm could look like this:

{
  "encrypted": {
      "key_id": {
        "iv": "16+bytes+base64",
        "ciphertext": "base64+encoded+encrypted+data",
        "mac": "base64+encoded+mac"
      }
  }
}
Key representation

When a user is given a raw key for m.secret_storage.v1.aes-hmac-sha2, the key should be presented as a string using the common cryptographic key representation.

Deriving keys from passphrases

A user may wish to use a chosen passphrase rather than a randomly generated key. In this case, information on how to generate the key from a passphrase will be stored in the passphrase property of the m.secret_storage.key.[key ID] account-data. The passphrase property has an algorithm property that indicates how to generate the key from the passphrase. Other properties of the passphrase property are defined by the algorithm specified.

Currently, the only algorithm defined is m.pbkdf2. For the m.pbkdf2 algorithm, the passphrase property has the following properties:

Parameter Type Description
algorithm string Required. Must be m.pbkdf2
salt string Required. The salt used in PBKDF2.
iterations integer Required. The number of iterations to use in PBKDF2.
bits integer Optional. The number of bits to generate for the key. Defaults to 256.

The key is generated using PBKDF2 with SHA-512 as the hash, using the salt given in the salt parameter, and the number of iterations given in the iterations parameter.

Example:

{
    "passphrase": {
        "algorithm": "m.pbkdf2",
        "salt": "MmMsAlty",
        "iterations": 100000,
        "bits": 256
    },
    ...
}

Sharing

To request a secret from other devices, a client sends an m.secret.request device event with action set to request and name set to the identifier of the secret. A device that wishes to share the secret will reply with an m.secret.send event, encrypted using olm. When the original client obtains the secret, it sends an m.secret.request event with action set to request_cancellation to all devices other than the one that it received the secret from. Clients should ignore m.secret.send events received from devices that it did not send an m.secret.request event to.

Clients must ensure that they only share secrets with other devices that are allowed to see them. For example, clients should only share secrets with the user’s own devices that are verified and may prompt the user to confirm sharing the secret.

Event definitions

m.secret.request


Sent by a client to request a secret from another device or to cancel a previous request. It is sent as an unencrypted to-device event.

Event type: Message event

Content

Name Type Description
action string

Required:

One of: [request, request_cancellation].

name string

Required if action is request. The name of the secret that is being requested.

request_id string

Required: A random string uniquely identifying (with respect to the requester and the target) the target for a secret. If the secret is requested from multiple devices at the same time, the same ID MAY be used for every target. The same ID is also used in order to cancel a previous request.

requesting_device_id string

Required: The ID of the device requesting the secret.

Examples

{
  "content": {
    "action": "request",
    "name": "org.example.some.secret",
    "request_id": "randomly_generated_id_9573",
    "requesting_device_id": "ABCDEFG"
  },
  "type": "m.secret.request"
}

m.secret.send


Sent by a client to share a secret with another device, in response to an m.secret.request event. It must be encrypted as an m.room.encrypted event using Olm, then sent as a to-device event.

The request_id must match the ID previously given in an m.secret.request event. The recipient must ensure that this event comes from a device that the m.secret.request event was originally sent to, and that the device is a verified device owned by the recipient. This should be done by checking the sender key of the Olm session that the event was sent over.

Event type: Message event

Content

Name Type Description
request_id string

Required: The ID of the request that this is a response to.

secret string

Required: The contents of the secret

Examples

{
  "content": {
    "request_id": "randomly_generated_id_9573",
    "secret": "ThisIsASecretDon'tTellAnyone"
  },
  "type": "m.secret.send"
}

Room History Visibility

This module adds support for controlling the visibility of previous events in a room.

In all cases except world_readable, a user needs to join a room to view events in that room. Once they have joined a room, they will gain access to a subset of events in the room. How this subset is chosen is controlled by the m.room.history_visibility event outlined below. After a user has left a room, they may see any events which they were allowed to see before they left the room, but no events received after they left.

The four options for the m.room.history_visibility event are:

  • world_readable - All events while this is the m.room.history_visibility value may be shared by any participating homeserver with anyone, regardless of whether they have ever joined the room.
  • shared - Previous events are always accessible to newly joined members. All events in the room are accessible, even those sent when the member was not a part of the room.
  • invited - Events are accessible to newly joined members from the point they were invited onwards. Events stop being accessible when the member’s state changes to something other than invite or join.
  • joined - Events are accessible to newly joined members from the point they joined the room onwards. Events stop being accessible when the member’s state changes to something other than join.

These options are applied at the point an event is sent. Checks are performed with the state of the m.room.history_visibility event when the event in question is added to the DAG. This means clients cannot retrospectively choose to show or hide history to new users if the setting at that time was more restrictive.

Events

m.room.history_visibility


This event controls whether a user can see the events that happened in a room from before they joined.

Event type: State event
State key A zero-length string.

Content

Name Type Description
history_visibility string

Required: Who can see the room history.

One of: [invited, joined, shared, world_readable].

Examples

{
  "content": {
    "history_visibility": "shared"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "state_key": "",
  "type": "m.room.history_visibility",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

Client behaviour

Clients that implement this module MUST present to the user the possible options for setting history visibility when creating a room.

Clients may want to display a notice that their events may be read by non-joined people if the value is set to world_readable.

Server behaviour

By default if no history_visibility is set, or if the value is not understood, the visibility is assumed to be shared. The rules governing whether a user is allowed to see an event depend on the state of the room at that event.

  1. If the history_visibility was set to world_readable, allow.
  2. If the user’s membership was join, allow.
  3. If history_visibility was set to shared, and the user joined the room at any point after the event was sent, allow.
  4. If the user’s membership was invite, and the history_visibility was set to invited, allow.
  5. Otherwise, deny.

For m.room.history_visibility events themselves, the user should be allowed to see the event if the history_visibility before or after the event would allow them to see it. (For example, a user should be able to see m.room.history_visibility events which change the history_visibility from world_readable to joined or from joined to world_readable, even if that user was not a member of the room.)

Likewise, for the user’s own m.room.member events, the user should be allowed to see the event if their membership before or after the event would allow them to see it. (For example, a user can always see m.room.member events which set their membership to join, or which change their membership from join to any other value, even if history_visibility is joined.)

Security considerations

The default value for history_visibility is shared for backwards-compatibility reasons. Clients need to be aware that by not setting this event they are exposing all of their room history to anyone in the room.

Push Notifications

                                   +--------------------+  +-------------------+
                  Matrix HTTP      |                    |  |                   |
             Notification Protocol |   App Developer    |  |   Device Vendor   |
                                   |                    |  |                   |
           +-------------------+   | +----------------+ |  | +---------------+ |
           |                   |   | |                | |  | |               | |
           | Matrix homeserver +----->  Push Gateway  +------> Push Provider | |
           |                   |   | |                | |  | |               | |
           +-^-----------------+   | +----------------+ |  | +----+----------+ |
             |                     |                    |  |      |            |
    Matrix   |                     |                    |  |      |            |
 Client/Server API  +              |                    |  |      |            |
             |      |              +--------------------+  +-------------------+
             |   +--+-+                                           |
             |   |    <-------------------------------------------+
             +---+    |
                 |    |          Provider Push Protocol
                 +----+
          Mobile Device or Client

This module adds support for push notifications. Homeservers send notifications of events to user-configured HTTP endpoints. Users may also configure a number of rules that determine which events generate notifications. These are all stored and managed by the user’s homeserver. This allows user-specific push settings to be reused between client applications.

The above diagram shows the flow of push notifications being sent to a handset where push notifications are submitted via the handset vendor, such as Apple’s APNS or Google’s GCM. This happens as follows:

  1. The client app signs in to a homeserver.
  2. The client app registers with its vendor’s Push Provider and obtains a routing token of some kind.
  3. The mobile app uses the Client/Server API to add a ‘pusher’, providing the URL of a specific Push Gateway which is configured for that application. It also provides the routing token it has acquired from the Push Provider.
  4. The homeserver starts sending HTTP requests to the Push Gateway using the supplied URL. The Push Gateway relays this notification to the Push Provider, passing the routing token along with any necessary private credentials the provider requires to send push notifications.
  5. The Push Provider sends the notification to the device.

Definitions for terms used in this section are below:

Push Provider

A push provider is a service managed by the device vendor which can send notifications directly to the device. Google Cloud Messaging (GCM) and Apple Push Notification Service (APNS) are two examples of push providers.

Push Gateway

A push gateway is a server that receives HTTP event notifications from homeservers and passes them on to a different protocol such as APNS for iOS devices or GCM for Android devices. Clients inform the homeserver which Push Gateway to send notifications to when it sets up a Pusher.

Pusher

A pusher is a worker on the homeserver that manages the sending of HTTP notifications for a user. A user can have multiple pushers: one per device.

Push Rule

A push rule is a single rule that states under what conditions an event should be passed onto a push gateway and how the notification should be presented. These rules are stored on the user’s homeserver. They are manually configured by the user, who can create and view them via the Client/Server API.

Push Ruleset

A push ruleset scopes a set of rules according to some criteria. For example, some rules may only be applied for messages from a particular sender, a particular room, or by default. The push ruleset contains the entire set of scopes and rules.

Push Rules

A push rule is a single rule that states under what conditions an event should be passed onto a push gateway and how the notification should be presented. There are different “kinds” of push rules and each rule has an associated priority. Every push rule MUST have a kind and rule_id. The rule_id is a unique string within the kind of rule and its’ scope: rule_ids do not need to be unique between rules of the same kind on different devices. Rules may have extra keys depending on the value of kind.

The different kinds of rule, in the order that they are checked, are:

  1. Override rules (override). The highest priority rules are user-configured overrides.

  2. Content-specific rules (content). These configure behaviour for messages that match certain patterns. Content rules take one parameter: pattern, that gives the glob-style pattern to match against. The match is performed case-insensitively, and must match any substring of the content.body property which starts and ends at a word boundary. A word boundary is defined as the start or end of the value, or any character not in the sets [A-Z], [a-z], [0-9] or _.The exact meaning of “case insensitive” is defined by the implementation of the homeserver.

  3. Room-specific rules (room). These rules change the behaviour of all messages for a given room. The rule_id of a room rule is always the ID of the room that it affects.

  4. Sender-specific rules (sender). These rules configure notification behaviour for messages from a specific Matrix user ID. The rule_id of Sender rules is always the Matrix user ID of the user whose messages they’d apply to.

  5. Underride rules (underride). These are identical to override rules, but have a lower priority than content, room and sender rules.

Rules with the same kind can specify an ordering priority. This determines which rule is selected in the event of multiple matches. For example, a rule matching “tea” and a separate rule matching “time” would both match the sentence “It’s time for tea”. The ordering of the rules would then resolve the tiebreak to determine which rule is executed. Only actions for highest priority rule will be sent to the Push Gateway.

Each rule can be enabled or disabled. Disabled rules never match. If no rules match an event, the homeserver MUST NOT notify the Push Gateway for that event. Homeservers MUST NOT notify the Push Gateway for events that the user has sent themselves.

Actions

All rules have an associated list of actions. An action affects if and how a notification is delivered for a matching event. The following actions are defined:

notify

This causes each matching event to generate a notification.

set_tweak

Sets an entry in the tweaks dictionary key that is sent in the notification request to the Push Gateway. This takes the form of a dictionary with a set_tweak key whose value is the name of the tweak to set. It may also have a value key which is the value to which it should be set.

The following tweaks are defined:

sound

A string representing the sound to be played when this notification arrives. A value of default means to play a default sound. A device may choose to alert the user by some other means if appropriate, eg. vibration.

highlight

A boolean representing whether or not this message should be highlighted in the UI. This will normally take the form of presenting the message in a different colour and/or style. The UI might also be adjusted to draw particular attention to the room in which the event occurred. If a highlight tweak is given with no value, its value is defined to be true. If no highlight tweak is given at all then the value of highlight is defined to be false.

Tweaks are passed transparently through the homeserver so client applications and Push Gateways may agree on additional tweaks. For example, a tweak may be added to specify how to flash the notification light on a mobile device.

Actions that have no parameters are represented as a string. Otherwise, they are represented as a dictionary with a key equal to their name and other keys as their parameters, e.g. { "set_tweak": "sound", "value": "default" }.

Historical Actions

Older versions of the Matrix specification included the dont_notify and coalesce actions. Clients and homeservers MUST ignore these actions, for instance, by stripping them from actions arrays they encounter. This means, for example, that a rule with ["dont_notify"] actions MUST be equivalent to a rule with an empty actions array.

Conditions

override and underride rules MAY have a list of ‘conditions’. All conditions must hold true for an event in order for the rule to match. A rule with no conditions always matches.

Unrecognised conditions MUST NOT match any events, effectively making the push rule disabled.

room, sender and content rules do not have conditions in the same way, but instead have predefined conditions. In the cases of room and sender rules, the rule_id of the rule determines its behaviour.

The following conditions are defined:

event_match

This is a glob pattern match on a property of the event. Parameters:

The match is performed case-insensitively, and must match the entire value of the event property given by key (though see below regarding content.body). The exact meaning of “case insensitive” is defined by the implementation of the homeserver.

If the property specified by key is completely absent from the event, or does not have a string value, then the condition will not match, even if pattern is *.

For example, if key is content.topic, and pattern is lunc?*, then the following event will match:

{
  "content": {
    "topic": "Lunch plans",
  },
  "event_id": "$143273582443PhrSn:example.org",
  "room_id": "!636q39766251:example.com",
  "sender": "@example:example.org",
  "state_key": "",
  "type": "m.room.topic"
}

Other topic values which will match are:

  • "LUNCH" (case-insensitive; * may match zero characters)

The following topic values will NOT match:

  • " lunch" (note leading space)
  • "lunc" (? must match a character)
  • null (not a string)

As a special case, if key is content.body, then pattern must instead match any substring of the value of the property which starts and ends at a word boundary. A word boundary is defined as the start or end of the value, or any character not in the sets [A-Z], [a-z], [0-9] or _.

For example, if key is content.body and pattern is ex*ple, the following event will match:

{
  "content": {
    "body": "An example event."
  },
  "event_id": "$143273976499sgjks:example.org",
  "room_id": "!636q39766251:example.com",
  "sender": "@example:example.org",
  "type": "m.room.message"
}

Other body values which will match are:

  • "exple" (the pattern can match at the start and end of the body.)
  • "An exciting triple-whammy" (the pattern can span multiple words, and - acts as a word separator.)

Note that there is no implicit condition for state_key. In other words, push rules which should match only state events must include an explicit condition for state_key.

For an example of this, see the default rule .m.rule.tombstone below.

event_property_is

This is an exact value match on a property of the event. Parameters:

The match is performed exactly and only supports non-compound canonical JSON values: strings, integers in the range of [-(2**53)+1, (2**53)-1], booleans, and null.

If the property specified by key is completely absent from the event, or does not have a string, integer, boolean, or null value, then the condition will not match.

For example, if key is content.m\.federate, and value is true, then the following event will match:

{
  "content": {
    "creator": "@example:example.org",
    "m.federate": true,
    "predecessor": {
      "event_id": "$something:example.org",
      "room_id": "!oldroom:example.org"
    },
    "room_version": "1"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "room_id": "!636q39766251:example.com",
  "sender": "@example:example.org",
  "state_key": "",
  "type": "m.room.create"
}

The following m.federate values will NOT match:

  • "true" (note the string value)
  • 1 (do not cast types)

event_property_contains

This matches if an array property of an event exactly contains a value. Parameters:

The array values are matched exactly and only supports non-compound canonical JSON values: strings, integers in the range of [-(2**53)+1, (2**53)-1], booleans, and null. Array values not of those types are ignored.

If the property specified by key is completely absent from the event, or is not an array, then the condition will not match.

For example, if key is content.alt_aliases, and value is "#myroom:example.com", then the following event will match:

{
  "content": {
    "alias": "#somewhere:localhost",
    "alt_aliases": [
      "#somewhere:example.org",
      "#myroom:example.com"
    ]
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "state_key": "",
  "type": "m.room.canonical_alias",
  "unsigned": {
    "age": 1234
  }
}

The following alt_aliases values will NOT match:

  • ":example.com" (partial values do not match)

contains_display_name

This matches messages where content.body contains the owner’s display name in that room. This is a separate condition because display names may change and as such it would be hard to maintain a rule that matched the user’s display name. This condition has no parameters.

room_member_count

This matches the current number of members in the room. Parameters:

  • is: A decimal integer optionally prefixed by one of, ==, <, >, >= or <=. A prefix of < matches rooms where the member count is strictly less than the given number and so forth. If no prefix is present, this parameter defaults to ==.

sender_notification_permission

This takes into account the current power levels in the room, ensuring the sender of the event has high enough power to trigger the notification.

Parameters:

  • key: A string that determines the power level the sender must have to trigger notifications of a given type, such as room. Refer to the m.room.power_levels event schema for information about what the defaults are and how to interpret the event. The key is used to look up the power level required to send a notification type from the notifications object in the power level event content.

Predefined Rules

Homeservers can specify “server-default rules”. They operate at a lower priority than “user-defined rules”, except for the .m.rule.master rule which has always a higher priority than any other rule. The rule_id for all server-default rules MUST start with a dot (".") to identify them as “server-default”. The following server-default rules are specified:

Default Override Rules

.m.rule.master

Matches all events. This can be enabled to turn off all push notifications. Unlike other server-default rules, this one has always a higher priority than other rules, even user defined ones. By default this rule is disabled.

Definition:

{
    "rule_id": ".m.rule.master",
    "default": true,
    "enabled": false,
    "conditions": [],
    "actions": []
}

.m.rule.suppress_notices

Matches messages with a msgtype of notice.

Definition:

{
    "rule_id": ".m.rule.suppress_notices",
    "default": true,
    "enabled": true,
    "conditions": [
        {
            "kind": "event_match",
            "key": "content.msgtype",
            "pattern": "m.notice"
        }
    ],
    "actions": []
}

.m.rule.invite_for_me

Matches any invites to a new room for this user.

Definition:

{
    "rule_id": ".m.rule.invite_for_me",
    "default": true,
    "enabled": true,
    "conditions": [
        {
            "key": "type",
            "kind": "event_match",
            "pattern": "m.room.member"
        },
        {
            "key": "content.membership",
            "kind": "event_match",
            "pattern": "invite"
        },
        {
            "key": "state_key",
            "kind": "event_match",
            "pattern": "[the user's Matrix ID]"
        }
    ],
    "actions": [
       "notify",
        {
            "set_tweak": "sound",
            "value": "default"
        }
    ]
}

.m.rule.member_event

Matches any m.room.member_event.

Definition:

{
    "rule_id": ".m.rule.member_event",
    "default": true,
    "enabled": true,
    "conditions": [
        {
            "key": "type",
            "kind": "event_match",
            "pattern": "m.room.member"
        }
    ],
    "actions": []
}

.m.rule.is_user_mention

[Added in v1.7]

Matches any message which contains the user’s Matrix ID in the list of user_ids under the m.mentions property.

Definition:

{
    "rule_id": ".m.rule.is_user_mention",
    "default": true,
    "enabled": true,
    "conditions": [
        {
            "kind": "event_property_contains",
            "key": "content.m\\.mentions.user_ids",
            "value": "[the user's Matrix ID]"
        }
    ],
    "actions": [
        "notify",
        {
            "set_tweak": "sound",
            "value": "default"
        },
        {
            "set_tweak": "highlight"
        }
    ]
}

.m.rule.contains_display_name

[Changed in v1.7]

As of v1.7, this rule is deprecated and should only be enabled if the event does not have an m.mentions property.

Matches any message whose content contains the user’s current display name in the room in which it was sent.

Definition:

{
    "rule_id": ".m.rule.contains_display_name",
    "default": true,
    "enabled": true,
    "conditions": [
        {
            "kind": "contains_display_name"
        }
    ],
    "actions": [
        "notify",
        {
            "set_tweak": "sound",
            "value": "default"
        },
        {
            "set_tweak": "highlight"
        }
    ]
}

.m.rule.is_room_mention

[Added in v1.7]

Matches any message from a sender with the proper power level with the room property of the m.mentions property set to true.

Definition:

{
    "rule_id": ".m.rule.is_room_mention",
    "default": true,
    "enabled": true,
    "conditions": [
        {
            "kind": "event_property_is",
            "key": "content.m\\.mentions.room",
            "value": true
        },
        {
            "kind": "sender_notification_permission",
            "key": "room"
        }
    ],
    "actions": [
        "notify",
        {
            "set_tweak": "highlight"
        }
    ]
}

.m.rule.roomnotif

[Changed in v1.7]

As of v1.7, this rule is deprecated and should only be enabled if the event does not have an m.mentions property.

Matches any message from a sender with the proper power level whose content contains the text @room, signifying the whole room should be notified of the event.

Definition:

{
    "rule_id": ".m.rule.roomnotif",
    "default": true,
    "enabled": true,
    "conditions": [
        {
            "kind": "event_match",
            "key": "content.body",
            "pattern": "@room"
        },
        {
            "kind": "sender_notification_permission",
            "key": "room"
        }
    ],
    "actions": [
        "notify",
        {
            "set_tweak": "highlight"
        }
    ]
}

.m.rule.tombstone

Matches any state event whose type is m.room.tombstone. This is intended to notify users of a room when it is upgraded, similar to what an @room notification would accomplish.

Definition:

{
    "rule_id": ".m.rule.tombstone",
    "default": true,
    "enabled": true,
    "conditions": [
        {
            "kind": "event_match",
            "key": "type",
            "pattern": "m.room.tombstone"
        },
        {
            "kind": "event_match",
            "key": "state_key",
            "pattern": ""
        }
    ],
    "actions": [
        "notify",
        {
            "set_tweak": "highlight"
        }
    ]
}

.m.rule.reaction

[Added in v1.7]

Matches any event whose type is m.reaction. This suppresses notifications for m.reaction events.

Definition:

{
    "rule_id": ".m.rule.reaction",
    "default": true,
    "enabled": true,
    "conditions": [
        {
            "kind": "event_match",
            "key": "type",
            "pattern": "m.reaction"
        }
    ],
    "actions": []
}

.m.rule.room.server_acl

[Added in v1.4]

Suppresses notifications for m.room.server_acl events.

Definition:

{
    "rule_id": ".m.rule.room.server_acl",
    "default": true,
    "enabled": true,
    "conditions": [
        {
            "kind": "event_match",
            "key": "type",
            "pattern": "m.room.server_acl"
        },
        {
            "kind": "event_match",
            "key": "state_key",
            "pattern": ""
        }
    ],
    "actions": []
}

.m.rule.suppress_edits

[Added in v1.9]

Suppresses notifications related to event replacements.

Definition:

{
    "rule_id": ".m.rule.suppress_edits",
    "default": true,
    "enabled": true,
    "conditions": [
        {
            "kind": "event_property_is",
            "key": "content.m\\.relates_to.rel_type",
            "value": "m.replace"
        }
    ],
    "actions": []
}
Default Content Rules

.m.rule.contains_user_name

[Changed in v1.7]

As of v1.7, this rule is deprecated and should only be enabled if the event does not have an m.mentions property.

Matches any message whose content contains the local part of the user’s Matrix ID, separated by word boundaries.

Definition (as a content rule):

{
    "rule_id": ".m.rule.contains_user_name",
    "default": true,
    "enabled": true,
    "pattern": "[the local part of the user's Matrix ID]",
    "actions": [
        "notify",
        {
            "set_tweak": "sound",
            "value": "default"
        },
        {
            "set_tweak": "highlight"
        }
    ]
}
Default Underride Rules

.m.rule.call

Matches any incoming VOIP call.

Definition:

{
    "rule_id": ".m.rule.call",
    "default": true,
    "enabled": true,
    "conditions": [
        {
            "key": "type",
            "kind": "event_match",
            "pattern": "m.call.invite"
        }
    ],
    "actions": [
        "notify",
        {
            "set_tweak": "sound",
            "value": "ring"
        }
    ]
}

.m.rule.encrypted_room_one_to_one

Matches any encrypted event sent in a room with exactly two members. Unlike other push rules, this rule cannot be matched against the content of the event by nature of it being encrypted. This causes the rule to be an “all or nothing” match where it either matches all events that are encrypted (in 1:1 rooms) or none.

Definition:

{
    "rule_id": ".m.rule.encrypted_room_one_to_one",
    "default": true,
    "enabled": true,
    "conditions": [
        {
            "kind": "room_member_count",
            "is": "2"
        },
        {
            "kind": "event_match",
            "key": "type",
            "pattern": "m.room.encrypted"
        }
    ],
    "actions": [
        "notify",
        {
            "set_tweak": "sound",
            "value": "default"
        }
    ]
}

.m.rule.room_one_to_one

Matches any message sent in a room with exactly two members.

Definition:

{
    "rule_id": ".m.rule.room_one_to_one",
    "default": true,
    "enabled": true,
    "conditions": [
        {
            "kind": "room_member_count",
            "is": "2"
        },
        {
            "kind": "event_match",
            "key": "type",
            "pattern": "m.room.message"
        }
    ],
    "actions": [
        "notify",
        {
            "set_tweak": "sound",
            "value": "default"
        }
    ]
}

.m.rule.message

Matches all chat messages.

Definition:

{
     "rule_id": ".m.rule.message",
     "default": true,
     "enabled": true,
     "conditions": [
         {
             "kind": "event_match",
             "key": "type",
             "pattern": "m.room.message"
         }
     ],
     "actions": [
         "notify"
     ]
}

.m.rule.encrypted

Matches all encrypted events. Unlike other push rules, this rule cannot be matched against the content of the event by nature of it being encrypted. This causes the rule to be an “all or nothing” match where it either matches all events that are encrypted (in group rooms) or none.

Definition:

{
     "rule_id": ".m.rule.encrypted",
     "default": true,
     "enabled": true,
     "conditions": [
         {
             "kind": "event_match",
             "key": "type",
             "pattern": "m.room.encrypted"
         }
     ],
     "actions": [
         "notify"
     ]
}

Push Rules: API

Clients can retrieve, add, modify and remove push rules globally or per-device using the APIs below.

GET /_matrix/client/v3/pushrules/


Retrieve all push rulesets for this user. Currently the only push ruleset defined is global.

Rate-limited: No
Requires authentication: Yes

Request

No request parameters or request body.


Responses

Status Description
200 All the push rulesets for this user.

200 response

Name Type Description
global Ruleset

Required: The global ruleset.

Ruleset
Name Type Description
content [PushRule]
override [PushRule]
room [PushRule]
sender [PushRule]
underride [PushRule]
PushRule
Name Type Description
actions [string|object]

Required: The actions to perform when this rule is matched.

conditions [PushCondition]

The conditions that must hold true for an event in order for a rule to be applied to an event. A rule with no conditions always matches. Only applicable to underride and override rules.

default boolean

Required: Whether this is a default rule, or has been set explicitly.

enabled boolean

Required: Whether the push rule is enabled or not.

pattern string

The glob-style pattern to match against. Only applicable to content rules.

rule_id string

Required: The ID of this rule.

PushCondition
Name Type Description
is string

Required for room_member_count conditions. A decimal integer optionally prefixed by one of, ==, <, >, >= or <=. A prefix of < matches rooms where the member count is strictly less than the given number and so forth. If no prefix is present, this parameter defaults to ==.

key string

Required for event_match, event_property_is and event_property_contains conditions. The dot-separated field of the event to match.

Required for sender_notification_permission conditions. The field in the power level event the user needs a minimum power level for. Fields must be specified under the notifications property in the power level event’s content.

kind string

Required: The kind of condition to apply. See conditions for more information on the allowed kinds and how they work.

pattern string

Required for event_match conditions. The glob-style pattern to match against.

value string|integer|boolean|null

Required for event_property_is and event_property_contains conditions. A non-compound canonical JSON value to match against.

{
  "global": {
    "content": [
      {
        "actions": [
          "notify",
          {
            "set_tweak": "sound",
            "value": "default"
          },
          {
            "set_tweak": "highlight"
          }
        ],
        "default": true,
        "enabled": true,
        "pattern": "alice",
        "rule_id": ".m.rule.contains_user_name"
      }
    ],
    "override": [
      {
        "actions": [],
        "conditions": [],
        "default": true,
        "enabled": false,
        "rule_id": ".m.rule.master"
      },
      {
        "actions": [],
        "conditions": [
          {
            "key": "content.msgtype",
            "kind": "event_match",
            "pattern": "m.notice"
          }
        ],
        "default": true,
        "enabled": true,
        "rule_id": ".m.rule.suppress_notices"
      }
    ],
    "room": [],
    "sender": [],
    "underride": [
      {
        "actions": [
          "notify",
          {
            "set_tweak": "sound",
            "value": "ring"
          },
          {
            "set_tweak": "highlight",
            "value": false
          }
        ],
        "conditions": [
          {
            "key": "type",
            "kind": "event_match",
            "pattern": "m.call.invite"
          }
        ],
        "default": true,
        "enabled": true,
        "rule_id": ".m.rule.call"
      },
      {
        "actions": [
          "notify",
          {
            "set_tweak": "sound",
            "value": "default"
          },
          {
            "set_tweak": "highlight"
          }
        ],
        "conditions": [
          {
            "kind": "contains_display_name"
          }
        ],
        "default": true,
        "enabled": true,
        "rule_id": ".m.rule.contains_display_name"
      },
      {
        "actions": [
          "notify",
          {
            "set_tweak": "sound",
            "value": "default"
          },
          {
            "set_tweak": "highlight",
            "value": false
          }
        ],
        "conditions": [
          {
            "is": "2",
            "kind": "room_member_count"
          },
          {
            "key": "type",
            "kind": "event_match",
            "pattern": "m.room.message"
          }
        ],
        "default": true,
        "enabled": true,
        "rule_id": ".m.rule.room_one_to_one"
      },
      {
        "actions": [
          "notify",
          {
            "set_tweak": "sound",
            "value": "default"
          },
          {
            "set_tweak": "highlight",
            "value": false
          }
        ],
        "conditions": [
          {
            "key": "type",
            "kind": "event_match",
            "pattern": "m.room.member"
          },
          {
            "key": "content.membership",
            "kind": "event_match",
            "pattern": "invite"
          },
          {
            "key": "state_key",
            "kind": "event_match",
            "pattern": "@alice:example.com"
          }
        ],
        "default": true,
        "enabled": true,
        "rule_id": ".m.rule.invite_for_me"
      },
      {
        "actions": [
          "notify",
          {
            "set_tweak": "highlight",
            "value": false
          }
        ],
        "conditions": [
          {
            "key": "type",
            "kind": "event_match",
            "pattern": "m.room.member"
          }
        ],
        "default": true,
        "enabled": true,
        "rule_id": ".m.rule.member_event"
      },
      {
        "actions": [
          "notify",
          {
            "set_tweak": "highlight",
            "value": false
          }
        ],
        "conditions": [
          {
            "key": "type",
            "kind": "event_match",
            "pattern": "m.room.message"
          }
        ],
        "default": true,
        "enabled": true,
        "rule_id": ".m.rule.message"
      }
    ]
  }
}

GET /_matrix/client/v3/pushrules/global/


Retrieve all push rules for this user.

Rate-limited: No
Requires authentication: Yes

Request

No request parameters or request body.


Responses

Status Description
200 All the push rules for this user.

200 response

Name Type Description
content [PushRule]
override [PushRule]
room [PushRule]
sender [PushRule]
underride [PushRule]
PushRule
Name Type Description
actions [string|object]

Required: The actions to perform when this rule is matched.

conditions [PushCondition]

The conditions that must hold true for an event in order for a rule to be applied to an event. A rule with no conditions always matches. Only applicable to underride and override rules.

default boolean

Required: Whether this is a default rule, or has been set explicitly.

enabled boolean

Required: Whether the push rule is enabled or not.

pattern string

The glob-style pattern to match against. Only applicable to content rules.

rule_id string

Required: The ID of this rule.

PushCondition
Name Type Description
is string

Required for room_member_count conditions. A decimal integer optionally prefixed by one of, ==, <, >, >= or <=. A prefix of < matches rooms where the member count is strictly less than the given number and so forth. If no prefix is present, this parameter defaults to ==.

key string

Required for event_match, event_property_is and event_property_contains conditions. The dot-separated field of the event to match.

Required for sender_notification_permission conditions. The field in the power level event the user needs a minimum power level for. Fields must be specified under the notifications property in the power level event’s content.

kind string

Required: The kind of condition to apply. See conditions for more information on the allowed kinds and how they work.

pattern string

Required for event_match conditions. The glob-style pattern to match against.

value string|integer|boolean|null

Required for event_property_is and event_property_contains conditions. A non-compound canonical JSON value to match against.

{
  "content": [
    {
      "actions": [
        "notify",
        {
          "set_tweak": "sound",
          "value": "default"
        },
        {
          "set_tweak": "highlight"
        }
      ],
      "default": true,
      "enabled": true,
      "pattern": "alice",
      "rule_id": ".m.rule.contains_user_name"
    }
  ],
  "override": [
    {
      "actions": [],
      "conditions": [],
      "default": true,
      "enabled": false,
      "rule_id": ".m.rule.master"
    },
    {
      "actions": [],
      "conditions": [
        {
          "key": "content.msgtype",
          "kind": "event_match",
          "pattern": "m.notice"
        }
      ],
      "default": true,
      "enabled": true,
      "rule_id": ".m.rule.suppress_notices"
    }
  ],
  "room": [],
  "sender": [],
  "underride": [
    {
      "actions": [
        "notify",
        {
          "set_tweak": "sound",
          "value": "ring"
        },
        {
          "set_tweak": "highlight",
          "value": false
        }
      ],
      "conditions": [
        {
          "key": "type",
          "kind": "event_match",
          "pattern": "m.call.invite"
        }
      ],
      "default": true,
      "enabled": true,
      "rule_id": ".m.rule.call"
    },
    {
      "actions": [
        "notify",
        {
          "set_tweak": "sound",
          "value": "default"
        },
        {
          "set_tweak": "highlight"
        }
      ],
      "conditions": [
        {
          "kind": "contains_display_name"
        }
      ],
      "default": true,
      "enabled": true,
      "rule_id": ".m.rule.contains_display_name"
    },
    {
      "actions": [
        "notify",
        {
          "set_tweak": "sound",
          "value": "default"
        },
        {
          "set_tweak": "highlight",
          "value": false
        }
      ],
      "conditions": [
        {
          "is": "2",
          "kind": "room_member_count"
        },
        {
          "key": "type",
          "kind": "event_match",
          "pattern": "m.room.message"
        }
      ],
      "default": true,
      "enabled": true,
      "rule_id": ".m.rule.room_one_to_one"
    },
    {
      "actions": [
        "notify",
        {
          "set_tweak": "sound",
          "value": "default"
        },
        {
          "set_tweak": "highlight",
          "value": false
        }
      ],
      "conditions": [
        {
          "key": "type",
          "kind": "event_match",
          "pattern": "m.room.member"
        },
        {
          "key": "content.membership",
          "kind": "event_match",
          "pattern": "invite"
        },
        {
          "key": "state_key",
          "kind": "event_match",
          "pattern": "@alice:example.com"
        }
      ],
      "default": true,
      "enabled": true,
      "rule_id": ".m.rule.invite_for_me"
    },
    {
      "actions": [
        "notify",
        {
          "set_tweak": "highlight",
          "value": false
        }
      ],
      "conditions": [
        {
          "key": "type",
          "kind": "event_match",
          "pattern": "m.room.member"
        }
      ],
      "default": true,
      "enabled": true,
      "rule_id": ".m.rule.member_event"
    },
    {
      "actions": [
        "notify",
        {
          "set_tweak": "highlight",
          "value": false
        }
      ],
      "conditions": [
        {
          "key": "type",
          "kind": "event_match",
          "pattern": "m.room.message"
        }
      ],
      "default": true,
      "enabled": true,
      "rule_id": ".m.rule.message"
    }
  ]
}

GET /_matrix/client/v3/pushrules/global/{kind}/{ruleId}


Retrieve a single specified push rule.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
kind string

Required: The kind of rule

One of: [override, underride, sender, room, content].

ruleId string

Required: The identifier for the rule.


Responses

Status Description
200 The specific push rule. This will also include keys specific to the rule itself such as the rule’s actions and conditions if set.
404 The push rule does not exist.

200 response

PushRule
Name Type Description
actions [string|object]

Required: The actions to perform when this rule is matched.

conditions [PushCondition]

The conditions that must hold true for an event in order for a rule to be applied to an event. A rule with no conditions always matches. Only applicable to underride and override rules.

default boolean

Required: Whether this is a default rule, or has been set explicitly.

enabled boolean

Required: Whether the push rule is enabled or not.

pattern string

The glob-style pattern to match against. Only applicable to content rules.

rule_id string

Required: The ID of this rule.

PushCondition
Name Type Description
is string

Required for room_member_count conditions. A decimal integer optionally prefixed by one of, ==, <, >, >= or <=. A prefix of < matches rooms where the member count is strictly less than the given number and so forth. If no prefix is present, this parameter defaults to ==.

key string

Required for event_match, event_property_is and event_property_contains conditions. The dot-separated field of the event to match.

Required for sender_notification_permission conditions. The field in the power level event the user needs a minimum power level for. Fields must be specified under the notifications property in the power level event’s content.

kind string

Required: The kind of condition to apply. See conditions for more information on the allowed kinds and how they work.

pattern string

Required for event_match conditions. The glob-style pattern to match against.

value string|integer|boolean|null

Required for event_property_is and event_property_contains conditions. A non-compound canonical JSON value to match against.

{
  "actions": [],
  "default": false,
  "enabled": true,
  "pattern": "cake*lie",
  "rule_id": "nocake"
}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND",
  "error": "The push rule was not found."
}

PUT /_matrix/client/v3/pushrules/global/{kind}/{ruleId}


This endpoint allows the creation and modification of user defined push rules.

If a rule with the same rule_id already exists among rules of the same kind, it is updated with the new parameters, otherwise a new rule is created.

If both after and before are provided, the new or updated rule must be the next most important rule with respect to the rule identified by before.

If neither after nor before are provided and the rule is created, it should be added as the most important user defined rule among rules of the same kind.

When creating push rules, they MUST be enabled by default.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
kind string

Required: The kind of rule

One of: [override, underride, sender, room, content].

ruleId string

Required: The identifier for the rule. If the string starts with a dot ("."), the request MUST be rejected as this is reserved for server-default rules. Slashes ("/") and backslashes ("\") are also not allowed.

query parameters
Name Type Description
after string

This makes the new rule the next-less important rule relative to the given user defined rule. It is not possible to add a rule relative to a predefined server rule.

before string

Use ‘before’ with a rule_id as its value to make the new rule the next-most important rule with respect to the given user defined rule. It is not possible to add a rule relative to a predefined server rule.

Request body

Name Type Description
actions [string|object]

Required: The action(s) to perform when the conditions for this rule are met.

conditions [PushCondition]

The conditions that must hold true for an event in order for a rule to be applied to an event. A rule with no conditions always matches. Only applicable to underride and override rules.

pattern string

Only applicable to content rules. The glob-style pattern to match against.

PushCondition
Name Type Description
is string

Required for room_member_count conditions. A decimal integer optionally prefixed by one of, ==, <, >, >= or <=. A prefix of < matches rooms where the member count is strictly less than the given number and so forth. If no prefix is present, this parameter defaults to ==.

key string

Required for event_match, event_property_is and event_property_contains conditions. The dot-separated field of the event to match.

Required for sender_notification_permission conditions. The field in the power level event the user needs a minimum power level for. Fields must be specified under the notifications property in the power level event’s content.

kind string

Required: The kind of condition to apply. See conditions for more information on the allowed kinds and how they work.

pattern string

Required for event_match conditions. The glob-style pattern to match against.

value string|integer|boolean|null

Required for event_property_is and event_property_contains conditions. A non-compound canonical JSON value to match against.

Request body example

{
  "actions": [
    "notify"
  ],
  "pattern": "cake*lie"
}

Responses

Status Description
200 The push rule was created/updated.
400 There was a problem configuring this push rule.
404 The push rule does not exist (when updating a push rule).
429 This request was rate-limited.

200 response

{}

400 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_UNKNOWN",
  "error": "before/after rule not found: someRuleId"
}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND",
  "error": "The push rule was not found."
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

DELETE /_matrix/client/v3/pushrules/global/{kind}/{ruleId}


This endpoint removes the push rule defined in the path.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
kind string

Required: The kind of rule

One of: [override, underride, sender, room, content].

ruleId string

Required: The identifier for the rule.


Responses

Status Description
200 The push rule was deleted.
404 The push rule does not exist.

200 response

{}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND",
  "error": "The push rule was not found."
}

GET /_matrix/client/v3/pushrules/global/{kind}/{ruleId}/actions


This endpoint get the actions for the specified push rule.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
kind string

Required: The kind of rule

One of: [override, underride, sender, room, content].

ruleId string

Required: The identifier for the rule.


Responses

Status Description
200 The actions for this push rule.
404 The push rule does not exist.

200 response

Name Type Description
actions [string|object]

Required: The action(s) to perform for this rule.

{
  "actions": [
    "notify",
    {
      "set_tweak": "sound",
      "value": "bing"
    }
  ]
}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND",
  "error": "The push rule was not found."
}

PUT /_matrix/client/v3/pushrules/global/{kind}/{ruleId}/actions


This endpoint allows clients to change the actions of a push rule. This can be used to change the actions of builtin rules.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
kind string

Required: The kind of rule

One of: [override, underride, sender, room, content].

ruleId string

Required: The identifier for the rule.

Request body

Name Type Description
actions [string|object]

Required: The action(s) to perform for this rule.

Request body example

{
  "actions": [
    "notify",
    {
      "set_tweak": "highlight"
    }
  ]
}

Responses

Status Description
200 The actions for the push rule were set.
404 The push rule does not exist.

200 response

{}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND",
  "error": "The push rule was not found."
}

GET /_matrix/client/v3/pushrules/global/{kind}/{ruleId}/enabled


This endpoint gets whether the specified push rule is enabled.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
kind string

Required: The kind of rule

One of: [override, underride, sender, room, content].

ruleId string

Required: The identifier for the rule.


Responses

Status Description
200 Whether the push rule is enabled.
404 The push rule does not exist.

200 response

Name Type Description
enabled boolean

Required: Whether the push rule is enabled or not.

{
  "enabled": true
}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND",
  "error": "The push rule was not found."
}

PUT /_matrix/client/v3/pushrules/global/{kind}/{ruleId}/enabled


This endpoint allows clients to enable or disable the specified push rule.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
kind string

Required: The kind of rule

One of: [override, underride, sender, room, content].

ruleId string

Required: The identifier for the rule.

Request body

Name Type Description
enabled boolean

Required: Whether the push rule is enabled or not.

Request body example

{
  "enabled": true
}

Responses

Status Description
200 The push rule was enabled or disabled.
404 The push rule does not exist.

200 response

{}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND",
  "error": "The push rule was not found."
}

Push Rules: Events

When a user changes their push rules a m.push_rules event is sent to all clients in the account_data section of their next /sync request. The content of the event is the current push rules for the user.

m.push_rules


Describes all push rules for this user.

Event type: Message event

Content

Name Type Description
global Ruleset

The global ruleset

Ruleset
Name Type Description
content [PushRule]
override [PushRule]
room [PushRule]
sender [PushRule]
underride [PushRule]
PushRule
Name Type Description
actions [string|object]

Required: The actions to perform when this rule is matched.

conditions [PushCondition]

The conditions that must hold true for an event in order for a rule to be applied to an event. A rule with no conditions always matches. Only applicable to underride and override rules.

default boolean

Required: Whether this is a default rule, or has been set explicitly.

enabled boolean

Required: Whether the push rule is enabled or not.

pattern string

The glob-style pattern to match against. Only applicable to content rules.

rule_id string

Required: The ID of this rule.

PushCondition
Name Type Description
is string

Required for room_member_count conditions. A decimal integer optionally prefixed by one of, ==, <, >, >= or <=. A prefix of < matches rooms where the member count is strictly less than the given number and so forth. If no prefix is present, this parameter defaults to ==.

key string

Required for event_match, event_property_is and event_property_contains conditions. The dot-separated field of the event to match.

Required for sender_notification_permission conditions. The field in the power level event the user needs a minimum power level for. Fields must be specified under the notifications property in the power level event’s content.

kind string

Required: The kind of condition to apply. See conditions for more information on the allowed kinds and how they work.

pattern string

Required for event_match conditions. The glob-style pattern to match against.

value string|integer|boolean|null

Required for event_property_is and event_property_contains conditions. A non-compound canonical JSON value to match against.

Examples

{
  "content": {
    "global": {
      "content": [
        {
          "actions": [
            "notify",
            {
              "set_tweak": "sound",
              "value": "default"
            },
            {
              "set_tweak": "highlight"
            }
          ],
          "default": true,
          "enabled": true,
          "pattern": "alice",
          "rule_id": ".m.rule.contains_user_name"
        }
      ],
      "override": [
        {
          "actions": [],
          "conditions": [],
          "default": true,
          "enabled": false,
          "rule_id": ".m.rule.master"
        },
        {
          "actions": [],
          "conditions": [
            {
              "key": "content.msgtype",
              "kind": "event_match",
              "pattern": "m.notice"
            }
          ],
          "default": true,
          "enabled": true,
          "rule_id": ".m.rule.suppress_notices"
        }
      ],
      "room": [],
      "sender": [],
      "underride": [
        {
          "actions": [
            "notify",
            {
              "set_tweak": "sound",
              "value": "ring"
            },
            {
              "set_tweak": "highlight",
              "value": false
            }
          ],
          "conditions": [
            {
              "key": "type",
              "kind": "event_match",
              "pattern": "m.call.invite"
            }
          ],
          "default": true,
          "enabled": true,
          "rule_id": ".m.rule.call"
        },
        {
          "actions": [
            "notify",
            {
              "set_tweak": "sound",
              "value": "default"
            },
            {
              "set_tweak": "highlight"
            }
          ],
          "conditions": [
            {
              "kind": "contains_display_name"
            }
          ],
          "default": true,
          "enabled": true,
          "rule_id": ".m.rule.contains_display_name"
        },
        {
          "actions": [
            "notify",
            {
              "set_tweak": "sound",
              "value": "default"
            },
            {
              "set_tweak": "highlight",
              "value": false
            }
          ],
          "conditions": [
            {
              "is": "2",
              "kind": "room_member_count"
            },
            {
              "key": "type",
              "kind": "event_match",
              "pattern": "m.room.message"
            }
          ],
          "default": true,
          "enabled": true,
          "rule_id": ".m.rule.room_one_to_one"
        },
        {
          "actions": [
            "notify",
            {
              "set_tweak": "sound",
              "value": "default"
            },
            {
              "set_tweak": "highlight",
              "value": false
            }
          ],
          "conditions": [
            {
              "key": "type",
              "kind": "event_match",
              "pattern": "m.room.member"
            },
            {
              "key": "content.membership",
              "kind": "event_match",
              "pattern": "invite"
            },
            {
              "key": "state_key",
              "kind": "event_match",
              "pattern": "@alice:example.com"
            }
          ],
          "default": true,
          "enabled": true,
          "rule_id": ".m.rule.invite_for_me"
        },
        {
          "actions": [
            "notify",
            {
              "set_tweak": "highlight",
              "value": false
            }
          ],
          "conditions": [
            {
              "key": "type",
              "kind": "event_match",
              "pattern": "m.room.member"
            }
          ],
          "default": true,
          "enabled": true,
          "rule_id": ".m.rule.member_event"
        },
        {
          "actions": [
            "notify",
            {
              "set_tweak": "highlight",
              "value": false
            }
          ],
          "conditions": [
            {
              "key": "type",
              "kind": "event_match",
              "pattern": "m.room.message"
            }
          ],
          "default": true,
          "enabled": true,
          "rule_id": ".m.rule.message"
        }
      ]
    }
  },
  "type": "m.push_rules"
}
Examples

To create a rule that suppresses notifications for the room with ID !dj234r78wl45Gh4D:matrix.org:

curl -X PUT -H "Content-Type: application/json" "https://example.com/_matrix/client/v3/pushrules/global/room/%21dj234r78wl45Gh4D%3Amatrix.org?access_token=123456" -d \
'{
   "actions" : []
 }'

To suppress notifications for the user @spambot:matrix.org:

curl -X PUT -H "Content-Type: application/json" "https://example.com/_matrix/client/v3/pushrules/global/sender/%40spambot%3Amatrix.org?access_token=123456" -d \
'{
   "actions" : []
 }'

To always notify for messages that contain the work ‘cake’ and set a specific sound (with a rule_id of SSByZWFsbHkgbGlrZSBjYWtl):

curl -X PUT -H "Content-Type: application/json" "https://example.com/_matrix/client/v3/pushrules/global/content/SSByZWFsbHkgbGlrZSBjYWtl?access_token=123456" -d \
'{
   "pattern": "cake",
   "actions" : ["notify", {"set_tweak":"sound", "value":"cakealarm.wav"}]
 }'

To add a rule suppressing notifications for messages starting with ‘cake’ but ending with ’lie’, superseding the previous rule:

curl -X PUT -H "Content-Type: application/json" "https://example.com/_matrix/client/v3/pushrules/global/content/U3BvbmdlIGNha2UgaXMgYmVzdA?access_token=123456&before=SSByZWFsbHkgbGlrZSBjYWtl" -d \
'{
   "pattern": "cake*lie",
   "actions" : ["notify"]
 }'

To add a custom sound for notifications messages containing the word ‘beer’ in any rooms with 10 members or fewer (with greater importance than the room, sender and content rules):

curl -X PUT -H "Content-Type: application/json" "https://example.com/_matrix/client/v3/pushrules/global/override/U2VlIHlvdSBpbiBUaGUgRHVrZQ?access_token=123456" -d \
'{
   "conditions": [
     {"kind": "event_match", "key": "content.body", "pattern": "beer" },
     {"kind": "room_member_count", "is": "<=10"}
   ],
   "actions" : [
     "notify",
     {"set_tweak":"sound", "value":"beeroclock.wav"}
   ]
 }'

Client behaviour

Clients MUST configure a Pusher before they will receive push notifications. There is a single API endpoint for this, as described below.

GET /_matrix/client/v3/pushers


Gets all currently active pushers for the authenticated user.

Rate-limited: No
Requires authentication: Yes

Request

No request parameters or request body.


Responses

Status Description
200 The pushers for this user.

200 response

Name Type Description
pushers [Pusher]

An array containing the current pushers for the user

Pusher
Name Type Description
app_display_name string

Required: A string that will allow the user to identify what application owns this pusher.

app_id string

Required: This is a reverse-DNS style identifier for the application. Max length, 64 chars.

data PusherData

Required: A dictionary of information for the pusher implementation itself.

device_display_name string

Required: A string that will allow the user to identify what device owns this pusher.

kind string

Required: The kind of pusher. "http" is a pusher that sends HTTP pokes.

lang string

Required: The preferred language for receiving notifications (e.g. ’en' or ’en-US')

profile_tag string

This string determines which set of device specific rules this pusher executes.

pushkey string

Required: This is a unique identifier for this pusher. See /set for more detail. Max length, 512 bytes.

PusherData
Name Type Description
format string

The format to use when sending notifications to the Push Gateway.

url URI

Required if kind is http. The URL to use to send notifications to.

{
  "pushers": [
    {
      "app_display_name": "Appy McAppface",
      "app_id": "face.mcapp.appy.prod",
      "data": {
        "url": "https://example.com/_matrix/push/v1/notify"
      },
      "device_display_name": "Alice's Phone",
      "kind": "http",
      "lang": "en-US",
      "profile_tag": "xyz",
      "pushkey": "Xp/MzCt8/9DcSNE9cuiaoT5Ac55job3TdLSSmtmYl4A="
    }
  ]
}

POST /_matrix/client/v3/pushers/set


This endpoint allows the creation, modification and deletion of pushers for this user ID. The behaviour of this endpoint varies depending on the values in the JSON body.

If kind is not null, the pusher with this app_id and pushkey for this user is updated, or it is created if it doesn’t exist. If kind is null, the pusher with this app_id and pushkey for this user is deleted.

Rate-limited: Yes
Requires authentication: Yes

Request

Request body

Name Type Description
app_display_name string

Required if kind is not null. A string that will allow the user to identify what application owns this pusher.

app_id string

Required: This is a reverse-DNS style identifier for the application. It is recommended that this end with the platform, such that different platform versions get different app identifiers. Max length, 64 chars.

If the kind is "email", this is "m.email".

append boolean

If true, the homeserver should add another pusher with the given pushkey and App ID in addition to any others with different user IDs. Otherwise, the homeserver must remove any other pushers with the same App ID and pushkey for different users. The default is false.

data PusherData

Required if kind is not null. A dictionary of information for the pusher implementation itself.

If kind is http, this MUST contain url which is the URL to use for sending notifications. Clients MAY use this object to pass custom data to their push gateway. Servers MUST forward the entire content including format and any custom keys but excluding url when calling /_matrix/push/v1/notify.

device_display_name string

Required if kind is not null. A string that will allow the user to identify what device owns this pusher.

kind string|null

Required: The kind of pusher to configure. "http" makes a pusher that sends HTTP pokes. "email" makes a pusher that emails the user with unread notifications. null deletes the pusher.

lang string

Required if kind is not null. The preferred language for receiving notifications (e.g. ’en’ or ’en-US’).

profile_tag string

This string determines which set of device specific rules this pusher executes.

pushkey string

Required: This is a unique identifier for this pusher. The value you should use for this is the routing or destination address information for the notification, for example, the APNS token for APNS or the Registration ID for GCM. If your notification client has no such concept, use any unique identifier. Max length, 512 bytes.

If the kind is "email", this is the email address to send notifications to.

PusherData
Name Type Description
format string

The format to send notifications in to Push Gateways if the kind is http. The details about what fields the homeserver should send to the push gateway are defined in the Push Gateway Specification. Currently the only format available is ’event_id_only'.

url URI

Required if kind is http. The URL to use for sending notifications. MUST be an HTTPS URL with a path of /_matrix/push/v1/notify.

Request body example

{
  "app_display_name": "Mat Rix",
  "app_id": "com.example.app.ios",
  "append": false,
  "data": {
    "format": "event_id_only",
    "url": "https://push-gateway.location.here/_matrix/push/v1/notify"
  },
  "device_display_name": "iPhone 9",
  "kind": "http",
  "lang": "en",
  "profile_tag": "xxyyzz",
  "pushkey": "APA91bHPRgkF3JUikC4ENAHEeMrd41Zxv3hVZjC9KtT8OvPVGJ-hQMRKRrZuJAEcl7B338qju59zJMjw2DELjzEvxwYv7hH5Ynpc1ODQ0aT4U4OFEeco8ohsN5PjL1iC2dNtk2BAokeMCg2ZXKqpc8FXKmhX94kIxQ"
}

Responses

Status Description
200 The pusher was set.
400 One or more of the pusher values were invalid.
429 This request was rate-limited.

200 response

{}

400 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_MISSING_PARAM",
  "error": "Missing parameters: lang, data"
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}
Listing Notifications

A client can retrieve a list of events that it has been notified about. This may be useful so that users can see a summary of what important messages they have received.

GET /_matrix/client/v3/notifications


This API is used to paginate through the list of events that the user has been, or would have been notified about.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

query parameters
Name Type Description
from string

Pagination token to continue from. This should be the next_token returned from an earlier call to this endpoint.

limit integer

Limit on the number of events to return in this request.

only string

Allows basic filtering of events returned. Supply highlight to return only events where the notification had the highlight tweak set.


Responses

Status Description
200 A batch of events is being returned

200 response

Name Type Description
next_token string

The token to supply in the from param of the next /notifications request in order to request more events. If this is absent, there are no more results.

notifications [Notification]

Required: The list of events that triggered notifications.

Notification
Name Type Description
actions [object|string]

Required: The action(s) to perform when the conditions for this rule are met. See Push Rules: API.

event Event

Required: The Event object for the event that triggered the notification.

profile_tag string

The profile tag of the rule that matched this event.

read boolean

Required: Indicates whether the user has sent a read receipt indicating that they have read this message.

room_id string

Required: The ID of the room in which the event was posted.

ts integer

Required: The unix timestamp at which the event notification was sent, in milliseconds.

Event
Name Type Description
content object

Required: The body of this event, as created by the client which sent it.

event_id string

Required: The globally unique identifier for this event.

origin_server_ts integer

Required: Timestamp (in milliseconds since the unix epoch) on originating homeserver when this event was sent.

sender string

Required: Contains the fully-qualified ID of the user who sent this event.

state_key string

Present if, and only if, this event is a state event. The key making this piece of state unique in the room. Note that it is often an empty string.

State keys starting with an @ are reserved for referencing user IDs, such as room members. With the exception of a few events, state events set with a given user’s ID as the state key MUST only be set by that user.

type string

Required: The type of the event.

unsigned UnsignedData

Contains optional extra information about the event.

UnsignedData
Name Type Description
age integer

The time in milliseconds that has elapsed since the event was sent. This field is generated by the local homeserver, and may be incorrect if the local time on at least one of the two servers is out of sync, which can cause the age to either be negative or greater than it actually is.

membership string

The room membership of the user making the request, at the time of the event.

This property is the value of the membership property of the requesting user’s m.room.member state at the point of the event, including any changes caused by the event. If the user had yet to join the room at the time of the event (i.e, they have no m.room.member state), this property is set to leave.

Homeservers SHOULD populate this property wherever practical, but they MAY omit it if necessary (for example, if calculating the value is expensive, servers might choose to only implement it in encrypted rooms). The property is not normally populated in events pushed to application services via the application service transaction API (where there is no clear definition of “requesting user”).

Added in v1.11

prev_content EventContent

The previous content for this event. This field is generated by the local homeserver, and is only returned if the event is a state event, and the client has permission to see the previous content.

Changed in v1.2: Previously, this field was specified at the top level of returned events rather than in unsigned (with the exception of the GET .../notifications endpoint), though in practice no known server implementations honoured this.

redacted_because ClientEventWithoutRoomID

The event that redacted this event, if any.

transaction_id string

The client-supplied transaction ID, for example, provided via PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}, if the client being given the event is the same one which sent it.

{
  "next_token": "abcdef",
  "notifications": [
    {
      "actions": [
        "notify"
      ],
      "event": {
        "content": {
          "body": "This is an example text message",
          "format": "org.matrix.custom.html",
          "formatted_body": "<b>This is an example text message</b>",
          "msgtype": "m.text"
        },
        "event_id": "$143273582443PhrSn:example.org",
        "origin_server_ts": 1432735824653,
        "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
        "sender": "@example:example.org",
        "type": "m.room.message",
        "unsigned": {
          "age": 1234,
          "membership": "join"
        }
      },
      "profile_tag": "hcbvkzxhcvb",
      "read": true,
      "room_id": "!abcdefg:example.com",
      "ts": 1475508881945
    }
  ]
}
Receiving notifications

Servers MUST include the number of unread notifications in a client’s /sync stream, and MUST update it as it changes. Notifications are determined by the push rules which apply to an event.

For encrypted events, the homeserver has limited access to the event content and properly processing push rules falls on the client. Clients should process push rules for each incoming event after decrypting them. This may result in needing to modify the number of unread notifications received from the homeserver.

Marking notifications as read

When the user updates their read receipt (either by using the API or by sending an event), notifications prior to and including that event MUST be marked as read. Which specific events are affected can vary depending on whether a threaded read receipt was used. Note that users can send both an m.read and m.read.private receipt, both of which are capable of clearing notifications.

If the user has both m.read and m.read.private set in the room then the receipt which is more recent/ahead must be used to determine where the user has read up to. For example, given an oldest-first set of events A, B, C, and D the m.read receipt could be at event C and m.read.private at event A - the user is considered to have read up to event C. If the m.read.private receipt is then updated to point to B or C, the user’s notification state doesn’t change (the m.read receipt is still more ahead), however if the m.read.private receipt were to be updated to event D then the user has read up to D (the m.read receipt is now behind the m.read.private receipt).

[Added in v1.4] When handling threaded read receipts, the server is to partition the notification count to each thread (with the main timeline being its own thread). To determine if an event is part of a thread the server follows the event relationship until it finds a thread root via an m.thread relation (as specified by the threading module), however it is not recommended that the server traverse infinitely. Instead, implementations are encouraged to do a maximum of 3 hops to find a thread before deciding that the event does not belong to a thread. This is primarily to ensure that future events, like m.reaction, are correctly considered “part of” a given thread.

Server behaviour

When receiving a new event homeservers process push rules for each of the local users in the room (excluding the sender). This may result in:

  • Generating a new number of unread notifications for the user.
  • Making a request to the configured push gateway.

The updated notification count from a new event MUST appear in the same /sync response as the event itself.

Push Gateway behaviour

Recommendations for APNS

The exact format for sending APNS notifications is flexible and up to the client app and its push gateway to agree on. As APNS requires that the sender has a private key owned by the app developer, each app must have its own push gateway. It is recommended that:

  • The APNS token be base64 encoded and used as the pushkey.
  • A different app_id be used for apps on the production and sandbox APS environments.
  • APNS push gateways do not attempt to wait for errors from the APNS gateway before returning and instead to store failures and return ‘rejected’ responses next time that pushkey is used.

Security considerations

Clients specify the Push Gateway URL to use to send event notifications to. This URL should be over HTTPS and never over HTTP.

As push notifications will pass through a Push Provider, message content shouldn’t be sent in the push itself where possible. Instead, Push Gateways should send a “sync” command to instruct the client to get new events from the homeserver directly.

Third-party invites

This module adds in support for inviting new members to a room where their Matrix user ID is not known, instead addressing them by a third-party identifier such as an email address. There are two flows here; one if a Matrix user ID is known for the third-party identifier, and one if not. Either way, the client calls /invite with the details of the third-party identifier.

The homeserver asks the identity server whether a Matrix user ID is known for that identifier:

  • If it is, an invite is simply issued for that user.
  • If it is not, the homeserver asks the identity server to record the details of the invitation, and to notify the invitee’s homeserver of this pending invitation if it gets a binding for this identifier in the future. The identity server returns a token and public key to the inviting homeserver.

When the invitee’s homeserver receives the notification of the binding, it should insert an m.room.member event into the room’s graph for that user, with content.membership = invite, as well as a content.third_party_invite property which contains proof that the invitee does indeed own that third-party identifier. See the m.room.member schema for more information.

Events

m.room.third_party_invite


Acts as an m.room.member invite event, where there isn’t a target user_id to invite. This event contains a token and a public key whose private key must be used to sign the token. Any user who can present that signature may use this invitation to join the target room.

Event type: State event
State key The token, of which a signature must be produced in order to join the room.

Content

Name Type Description
display_name string

Required: A user-readable string which represents the user who has been invited. This should not contain the user’s third-party ID, as otherwise when the invite is accepted it would leak the association between the matrix ID and the third-party ID.

key_validity_url string

Required: A URL which can be fetched, with querystring public_key=public_key, to validate whether the key has been revoked. The URL must return a JSON object containing a boolean property named ‘valid’.

public_key string

Required: A base64-encoded ed25519 key with which token must be signed (though a signature from any entry in public_keys is also sufficient). This exists for backwards compatibility.

public_keys [PublicKeys]

Keys with which the token may be signed.

PublicKeys
Name Type Description
key_validity_url string

An optional URL which can be fetched, with querystring public_key=public_key, to validate whether the key has been revoked. The URL must return a JSON object containing a boolean property named ‘valid’. If this URL is absent, the key must be considered valid indefinitely.

public_key string

Required: A base-64 encoded ed25519 key with which token may be signed.

Examples

{
  "content": {
    "display_name": "Alice Margatroid",
    "key_validity_url": "https://magic.forest/verifykey",
    "public_key": "abc123",
    "public_keys": [
      {
        "key_validity_url": "https://magic.forest/verifykey",
        "public_key": "def456"
      }
    ]
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "state_key": "pc98",
  "type": "m.room.third_party_invite",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

Client behaviour

A client asks a server to invite a user by their third-party identifier.

POST /_matrix/client/v3/rooms/{roomId}/invite


Note that there are two forms of this API, which are documented separately. This version of the API does not require that the inviter know the Matrix identifier of the invitee, and instead relies on third-party identifiers. The homeserver uses an identity server to perform the mapping from third-party identifier to a Matrix identifier. The other is documented in the joining rooms section.

This API invites a user to participate in a particular room. They do not start participating in the room until they actually join the room.

Only users currently in a particular room can invite other users to join that room.

If the identity server did know the Matrix user identifier for the third-party identifier, the homeserver will append a m.room.member event to the room.

If the identity server does not know a Matrix user identifier for the passed third-party identifier, the homeserver will issue an invitation which can be accepted upon providing proof of ownership of the third- party identifier. This is achieved by the identity server generating a token, which it gives to the inviting homeserver. The homeserver will add an m.room.third_party_invite event into the graph for the room, containing that token.

When the invitee binds the invited third-party identifier to a Matrix user ID, the identity server will give the user a list of pending invitations, each containing:

  • The room ID to which they were invited

  • The token given to the homeserver

  • A signature of the token, signed with the identity server’s private key

  • The matrix user ID who invited them to the room

If a token is requested from the identity server, the homeserver will append a m.room.third_party_invite event to the room.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string

Required: The room identifier (not alias) to which to invite the user.

Request body

Name Type Description
address string

Required: The invitee’s third-party identifier.

id_access_token string

Required: An access token previously registered with the identity server. Servers can treat this as optional to distinguish between r0.5-compatible clients and this specification version.

id_server string

Required: The hostname+port of the identity server which should be used for third-party identifier lookups.

medium string

Required: The kind of address being passed in the address field, for example email (see the list of recognised values).

Request body example

{
  "address": "[email protected]",
  "id_access_token": "abc123_OpaqueString",
  "id_server": "matrix.org",
  "medium": "email"
}

Responses

Status Description
200 The user has been invited to join the room.
403

You do not have permission to invite the user to the room. A meaningful errcode and description error text will be returned. Example reasons for rejections are:

  • The invitee has been banned from the room.
  • The invitee is already a member of the room.
  • The inviter is not currently in the room.
  • The inviter’s power level is insufficient to invite users to the room.
429 This request was rate-limited.

200 response

{}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_FORBIDDEN",
  "error": "@cheeky_monkey:matrix.org is banned from the room"
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

Server behaviour

Upon receipt of an /invite, the server is expected to look up the third-party identifier with the provided identity server. If the lookup yields a result for a Matrix User ID then the normal invite process can be initiated. This process ends up looking like this:

    +---------+                         +-------------+                                    +-----------------+
    | Client  |                         | Homeserver  |                                    | IdentityServer  |
    +---------+                         +-------------+                                    +-----------------+
        |                                     |                                                    |
        | POST /invite                        |                                                    |
        |------------------------------------>|                                                    |
        |                                     |                                                    |
        |                                     | GET /lookup                                        |
        |                                     |--------------------------------------------------->|
        |                                     |                                                    |
        |                                     |                                     User ID result |
        |                                     |<---------------------------------------------------|
        |                                     |                                                    |
        |                                     | Invite process for the discovered User ID          |
        |                                     |------------------------------------------          |
        |                                     |                                         |          |
        |                                     |<-----------------------------------------          |
        |                                     |                                                    |
        |        Complete the /invite request |                                                    |
        |<------------------------------------|                                                    |
        |                                     |                                                    |

However, if the lookup does not yield a bound User ID, the homeserver must store the invite on the identity server and emit a valid m.room.third_party_invite event to the room. This process ends up looking like this:

    +---------+                         +-------------+                                               +-----------------+
    | Client  |                         | Homeserver  |                                               | IdentityServer  |
    +---------+                         +-------------+                                               +-----------------+
        |                                     |                                                               |
        | POST /invite                        |                                                               |
        |------------------------------------>|                                                               |
        |                                     |                                                               |
        |                                     | GET /lookup                                                   |
        |                                     |-------------------------------------------------------------->|
        |                                     |                                                               |
        |                                     |                                             "no users" result |
        |                                     |<--------------------------------------------------------------|
        |                                     |                                                               |
        |                                     | POST /store-invite                                            |
        |                                     |-------------------------------------------------------------->|
        |                                     |                                                               |
        |                                     |          Information needed for the m.room.third_party_invite |
        |                                     |<--------------------------------------------------------------|
        |                                     |                                                               |
        |                                     | Emit m.room.third_party_invite to the room                    |
        |                                     |-------------------------------------------                    |
        |                                     |                                          |                    |
        |                                     |<------------------------------------------                    |
        |                                     |                                                               |
        |        Complete the /invite request |                                                               |
        |<------------------------------------|                                                               |
        |                                     |                                                               |

All homeservers MUST verify the signature in the event’s content.third_party_invite.signed object.

The third-party user will then need to verify their identity, which results in a call from the identity server to the homeserver that bound the third-party identifier to a user. The homeserver then exchanges the m.room.third_party_invite event in the room for a complete m.room.member event for membership: invite for the user that has bound the third-party identifier.

If a homeserver is joining a room for the first time because of an m.room.third_party_invite, the server which is already participating in the room (which is chosen as per the standard server-server specification) MUST validate that the public key used for signing is still valid, by checking key_validity_url in the above described way.

No other homeservers may reject the joining of the room on the basis of key_validity_url, this is so that all homeservers have a consistent view of the room. They may, however, indicate to their clients that a member’s membership is questionable.

For example, given H1, H2, and H3 as homeservers, UserA as a user of H1, and an identity server IS, the full sequence for a third-party invite would look like the following. This diagram assumes H1 and H2 are residents of the room while H3 is attempting to join.

    +-------+ +-----------------+         +-----+                                          +-----+           +-----+                      +-----+
    | UserA | | ThirdPartyUser  |         | H1  |                                          | H2  |           | H3  |                      | IS  |
    +-------+ +-----------------+         +-----+                                          +-----+           +-----+                      +-----+
        |              |                     |                                                |                 |                            |
        | POST /invite for ThirdPartyUser    |                                                |                 |                            |
        |----------------------------------->|                                                |                 |                            |
        |              |                     |                                                |                 |                            |
        |              |                     | GET /lookup                                    |                 |                            |
        |              |                     |---------------------------------------------------------------------------------------------->|
        |              |                     |                                                |                 |                            |
        |              |                     |                                                |                Lookup results (empty object) |
        |              |                     |<----------------------------------------------------------------------------------------------|
        |              |                     |                                                |                 |                            |
        |              |                     | POST /store-invite                             |                 |                            |
        |              |                     |---------------------------------------------------------------------------------------------->|
        |              |                     |                                                |                 |                            |
        |              |                     |                                                |      Token, keys, etc for third-party invite |
        |              |                     |<----------------------------------------------------------------------------------------------|
        |              |                     |                                                |                 |                            |
        |              |                     | (Federation) Emit m.room.third_party_invite    |                 |                            |
        |              |                     |----------------------------------------------->|                 |                            |
        |              |                     |                                                |                 |                            |
        |           Complete /invite request |                                                |                 |                            |
        |<-----------------------------------|                                                |                 |                            |
        |              |                     |                                                |                 |                            |
        |              | Verify identity     |                                                |                 |                            |
        |              |-------------------------------------------------------------------------------------------------------------------->|
        |              |                     |                                                |                 |                            |
        |              |                     |                                                |                 |          POST /3pid/onbind |
        |              |                     |                                                |                 |<---------------------------|
        |              |                     |                                                |                 |                            |
        |              |                     |                         PUT /exchange_third_party_invite/:roomId |                            |
        |              |                     |<-----------------------------------------------------------------|                            |
        |              |                     |                                                |                 |                            |
        |              |                     | Verify the request                             |                 |                            |
        |              |                     |-------------------                             |                 |                            |
        |              |                     |                  |                             |                 |                            |
        |              |                     |<------------------                             |                 |                            |
        |              |                     |                                                |                 |                            |
        |              |                     | (Federation) Emit m.room.member for invite     |                 |                            |
        |              |                     |----------------------------------------------->|                 |                            |
        |              |                     |                                                |                 |                            |
        |              |                     |                                                |                 |                            |
        |              |                     | (Federation) Emit the m.room.member event sent to H2             |                            |
        |              |                     |----------------------------------------------------------------->|                            |
        |              |                     |                                                |                 |                            |
        |              |                     | Complete /exchange_third_party_invite/:roomId request            |                            |
        |              |                     |----------------------------------------------------------------->|                            |
        |              |                     |                                                |                 |                            |
        |              |                     |                                                |                 | Participate in the room    |
        |              |                     |                                                |                 |------------------------    |
        |              |                     |                                                |                 |                       |    |
        |              |                     |                                                |                 |<-----------------------    |
        |              |                     |                                                |                 |                            |

Note that when H1 sends the m.room.member event to H2 and H3 it does not have to block on either server’s receipt of the event. Likewise, H1 may complete the /exchange_third_party_invite request at the same time as sending the m.room.member event to H2 and H3. Additionally, H3 may complete the /3pid/onbind request it got from IS at any time - the completion is not shown in the diagram.

H1 MUST verify the request from H3 to ensure the signed property is correct as well as the key_validity_url as still being valid. This is done by making a request to the identity server /isvalid endpoint, using the provided URL rather than constructing a new one. The query string and response for the provided URL must match the Identity Service Specification.

The reason that no other homeserver may reject the event based on checking key_validity_url is that we must ensure event acceptance is deterministic. If some other participating server doesn’t have a network path to the keyserver, or if the keyserver were to go offline, or revoke its keys, that other server would reject the event and cause the participating servers’ graphs to diverge. This relies on participating servers trusting each other, but that trust is already implied by the server-server protocol. Also, the public key signature verification must still be performed, so the attack surface here is minimized.

Security considerations

There are a number of privacy and trust implications to this module.

It is important for user privacy that leaking the mapping between a matrix user ID and a third-party identifier is hard. In particular, being able to look up all third-party identifiers from a matrix user ID (and accordingly, being able to link each third-party identifier) should be avoided wherever possible. To this end, the third-party identifier is not put in any event, rather an opaque display name provided by the identity server is put into the events. Clients should not remember or display third-party identifiers from invites, other than for the use of the inviter themself.

Homeservers are not required to trust any particular identity server(s). It is generally a client’s responsibility to decide which identity servers it trusts, not a homeserver’s. Accordingly, this API takes identity servers as input from end users, and doesn’t have any specific trusted set. It is possible some homeservers may want to supply defaults, or reject some identity servers for its users, but no homeserver is allowed to dictate which identity servers other homeservers’ users trust.

There is some risk of denial of service attacks by flooding homeservers or identity servers with many requests, or much state to store. Defending against these is left to the implementer’s discretion.

The search API allows clients to perform full text search across events in all rooms that the user has been in, including those that they have left. Only events that the user is allowed to see will be searched, e.g. it won’t include events in rooms that happened after you left.

Client behaviour

There is a single HTTP API for performing server-side search, documented below.

POST /_matrix/client/v3/search


Performs a full text search across different categories.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

query parameters
Name Type Description
next_batch string

The point to return events from. If given, this should be a next_batch result from a previous call to this endpoint.

Request body

Name Type Description
search_categories Categories

Required: Describes which categories to search in and their criteria.

Categories
Name Type Description
room_events Room Events Criteria

Mapping of category name to search criteria.

Room Events Criteria
Name Type Description
event_context Include Event Context

Configures whether any context for the events returned are included in the response.

filter Filter

This takes a filter.

groupings Groupings

Requests that the server partitions the result set based on the provided list of keys.

include_state boolean

Requests the server return the current state for each room returned.

keys [string]

The keys to search. Defaults to all.

order_by string

The order in which to search for results. By default, this is "rank".

One of: [recent, rank].

search_term string

Required: The string to search events for

Include Event Context
Name Type Description
after_limit integer

How many events after the result are returned. By default, this is 5.

before_limit integer

How many events before the result are returned. By default, this is 5.

include_profile boolean

Requests that the server returns the historic profile information for the users that sent the events that were returned. By default, this is false.

Filter
Name Type Description
contains_url boolean

If true, includes only events with a url key in their content. If false, excludes those events. If omitted, url key is not considered for filtering.

include_redundant_members boolean

If true, sends all membership events for all events, even if they have already been sent to the client. Does not apply unless lazy_load_members is true. See Lazy-loading room members for more information. Defaults to false.

lazy_load_members boolean

If true, enables lazy-loading of membership events. See Lazy-loading room members for more information. Defaults to false.

limit integer

The maximum number of events to return, must be an integer greater than 0.

Servers should apply a default value, and impose a maximum value to avoid resource exhaustion.

not_rooms [string]

A list of room IDs to exclude. If this list is absent then no rooms are excluded. A matching room will be excluded even if it is listed in the 'rooms' filter.

not_senders [string]

A list of sender IDs to exclude. If this list is absent then no senders are excluded. A matching sender will be excluded even if it is listed in the 'senders' filter.

not_types [string]

A list of event types to exclude. If this list is absent then no event types are excluded. A matching type will be excluded even if it is listed in the 'types' filter. A ‘*’ can be used as a wildcard to match any sequence of characters.

rooms [string]

A list of room IDs to include. If this list is absent then all rooms are included.

senders [string]

A list of senders IDs to include. If this list is absent then all senders are included.

types [string]

A list of event types to include. If this list is absent then all event types are included. A '*' can be used as a wildcard to match any sequence of characters.

unread_thread_notifications boolean

If true, enables per-thread notification counts. Only applies to the /sync endpoint. Defaults to false.

Added in v1.4

Groupings
Name Type Description
group_by [Group]

List of groups to request.

Group
Name Type Description
key string

Key that defines the group.

One of: [room_id, sender].

Request body example

{
  "search_categories": {
    "room_events": {
      "groupings": {
        "group_by": [
          {
            "key": "room_id"
          }
        ]
      },
      "keys": [
        "content.body"
      ],
      "order_by": "recent",
      "search_term": "martians and men"
    }
  }
}

Responses

Status Description
200 Results of the search.
400 Part of the request was invalid.
429 This request was rate-limited.

200 response

Results
Name Type Description
search_categories Result Categories

Required: Describes which categories to search in and their criteria.

Result Categories
Name Type Description
room_events Result Room Events

Mapping of category name to search criteria.

Result Room Events
Name Type Description
count integer

An approximate count of the total number of results found.

groups {string: {string: Group Value}}

Any groups that were requested.

The outer string key is the group key requested (eg: room_id or sender). The inner string key is the grouped value (eg: a room’s ID or a user’s ID).

highlights [string]

List of words which should be highlighted, useful for stemming which may change the query terms.

next_batch string

Token that can be used to get the next batch of results, by passing as the next_batch parameter to the next call. If this field is absent, there are no more results.

results [Result]

List of results in the requested order.

state {Room ID: [ClientEvent]}

The current state for every room in the results. This is included if the request had the include_state key set with a value of true.

The key is the room ID for which the State Event array belongs to.

Group Value
Name Type Description
next_batch string

Token that can be used to get the next batch of results in the group, by passing as the next_batch parameter to the next call. If this field is absent, there are no more results in this group.

order integer

Key that can be used to order different groups.

results [string]

Which results are in this group.

Result
Name Type Description
context Event Context

Context for result, if requested.

rank number

A number that describes how closely this result matches the search. Higher is closer.

result Event

The event that matched.

Event Context
Name Type Description
end string

Pagination token for the end of the chunk

events_after [Event]

Events just after the result.

events_before [Event]

Events just before the result.

profile_info {User ID: User Profile}

The historic profile information of the users that sent the events returned.

The key is the user ID for which the profile belongs to.

start string

Pagination token for the start of the chunk

Event
Name Type Description
content object

Required: The body of this event, as created by the client which sent it.

event_id string

Required: The globally unique identifier for this event.

origin_server_ts integer

Required: Timestamp (in milliseconds since the unix epoch) on originating homeserver when this event was sent.

room_id string

Required: The ID of the room associated with this event.

sender string

Required: Contains the fully-qualified ID of the user who sent this event.

state_key string

Present if, and only if, this event is a state event. The key making this piece of state unique in the room. Note that it is often an empty string.

State keys starting with an @ are reserved for referencing user IDs, such as room members. With the exception of a few events, state events set with a given user’s ID as the state key MUST only be set by that user.

type string

Required: The type of the event.

unsigned UnsignedData

Contains optional extra information about the event.

UnsignedData
Name Type Description
age integer

The time in milliseconds that has elapsed since the event was sent. This field is generated by the local homeserver, and may be incorrect if the local time on at least one of the two servers is out of sync, which can cause the age to either be negative or greater than it actually is.

membership string

The room membership of the user making the request, at the time of the event.

This property is the value of the membership property of the requesting user’s m.room.member state at the point of the event, including any changes caused by the event. If the user had yet to join the room at the time of the event (i.e, they have no m.room.member state), this property is set to leave.

Homeservers SHOULD populate this property wherever practical, but they MAY omit it if necessary (for example, if calculating the value is expensive, servers might choose to only implement it in encrypted rooms). The property is not normally populated in events pushed to application services via the application service transaction API (where there is no clear definition of “requesting user”).

Added in v1.11

prev_content EventContent

The previous content for this event. This field is generated by the local homeserver, and is only returned if the event is a state event, and the client has permission to see the previous content.

Changed in v1.2: Previously, this field was specified at the top level of returned events rather than in unsigned (with the exception of the GET .../notifications endpoint), though in practice no known server implementations honoured this.

redacted_because ClientEvent

The event that redacted this event, if any.

transaction_id string

The client-supplied transaction ID, for example, provided via PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}, if the client being given the event is the same one which sent it.

User Profile
Name Type Description
avatar_url URI
displayname string
ClientEvent
Name Type Description
content object

Required: The body of this event, as created by the client which sent it.

event_id string

Required: The globally unique identifier for this event.

origin_server_ts integer

Required: Timestamp (in milliseconds since the unix epoch) on originating homeserver when this event was sent.

room_id string

Required: The ID of the room associated with this event.

sender string

Required: Contains the fully-qualified ID of the user who sent this event.

state_key string

Present if, and only if, this event is a state event. The key making this piece of state unique in the room. Note that it is often an empty string.

State keys starting with an @ are reserved for referencing user IDs, such as room members. With the exception of a few events, state events set with a given user’s ID as the state key MUST only be set by that user.

type string

Required: The type of the event.

unsigned UnsignedData

Contains optional extra information about the event.

{
  "search_categories": {
    "room_events": {
      "count": 1224,
      "groups": {
        "room_id": {
          "!qPewotXpIctQySfjSy:localhost": {
            "next_batch": "BdgFsdfHSf-dsFD",
            "order": 1,
            "results": [
              "$144429830826TWwbB:localhost"
            ]
          }
        }
      },
      "highlights": [
        "martians",
        "men"
      ],
      "next_batch": "5FdgFsd234dfgsdfFD",
      "results": [
        {
          "rank": 0.00424866,
          "result": {
            "content": {
              "body": "This is an example text message",
              "format": "org.matrix.custom.html",
              "formatted_body": "<b>This is an example text message</b>",
              "msgtype": "m.text"
            },
            "event_id": "$144429830826TWwbB:localhost",
            "origin_server_ts": 1432735824653,
            "room_id": "!qPewotXpIctQySfjSy:localhost",
            "sender": "@example:example.org",
            "type": "m.room.message",
            "unsigned": {
              "age": 1234,
              "membership": "join"
            }
          }
        }
      ]
    }
  }
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

Search Categories

The search API allows clients to search in different categories of items. Currently the only specified category is room_events.

room_events

This category covers all events that the user is allowed to see, including events in rooms that they have left. The search is performed on certain keys of certain event types.

The supported keys to search over are:

  • content.body in m.room.message
  • content.name in m.room.name
  • content.topic in m.room.topic

The search will not include rooms that are end to end encrypted.

The results include a rank key that can be used to sort the results by relevancy. The higher the rank the more relevant the result is.

The value of count gives an approximation of the total number of results. Homeservers may give an estimate rather than an exact value for this field.

Ordering

The client can specify the ordering that the server returns results in. The two allowed orderings are:

  • rank, which returns the most relevant results first.
  • recent, which returns the most recent results first.

The default ordering is rank.

Groups

The client can request that the results are returned along with grouping information, e.g. grouped by room_id. In this case the response will contain a group entry for each distinct value of room_id. Each group entry contains at least a list of the event_ids that are in that group, as well as potentially other metadata about the group.

The current required supported groupings are:

  • room_id
  • sender

Pagination

The server may return a next_batch key at various places in the response. These are used to paginate the results. To fetch more results, the client should send the same request to the server with a next_batch query parameter set to that of the token.

The scope of the pagination is defined depending on where the next_batch token was returned. For example, using a token inside a group will return more results from within that group.

The currently supported locations for the next_batch token are:

  • search_categories.<category>.next_batch
  • search_categories.<category>.groups.<group_key>.<group_id>.next_batch

A server need not support pagination, even if there are more matching results. In that case, they must not return a next_batch token in the response.

Security considerations

The server must only return results that the user has permission to see.

Guest Access

There are times when it is desirable for clients to be able to interact with rooms without having to fully register for an account on a homeserver or join the room. This module specifies how these clients should interact with servers in order to participate in rooms as guests.

Guest users retrieve access tokens from a homeserver using the ordinary register endpoint, specifying the kind parameter as guest. They may then interact with the client-server API as any other user would, but will only have access to a subset of the API as described the Client behaviour subsection below. Homeservers may choose not to allow this access at all to their local users, but have no information about whether users on other homeservers are guests or not.

Guest users can also upgrade their account by going through the ordinary register flow, but specifying the additional POST parameter guest_access_token containing the guest’s access token. They are also required to specify the username parameter to the value of the local part of their username, which is otherwise optional.

This module does not fully factor in federation; it relies on individual homeservers properly adhering to the rules set out in this module, rather than allowing all homeservers to enforce the rules on each other.

Events

m.room.guest_access


This event controls whether guest users are allowed to join rooms. If this event is absent, servers should act as if it is present and has the guest_access value “forbidden”.

Event type: State event
State key A zero-length string.

Content

Name Type Description
guest_access string

Required: Whether guests can join the room.

One of: [can_join, forbidden].

Examples

{
  "content": {
    "guest_access": "can_join"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "state_key": "",
  "type": "m.room.guest_access",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

Client behaviour

The following API endpoints are allowed to be accessed by guest accounts for retrieving events and associated media:

The following API endpoints are allowed to be accessed by guest accounts for sending events:

The following API endpoints are allowed to be accessed by guest accounts for their own account maintenance:

The following API endpoints are allowed to be accessed by guest accounts for end-to-end encryption:

Server behaviour

Servers MUST only allow guest users to join rooms if the m.room.guest_access state event is present on the room, and has the guest_access value can_join. If the m.room.guest_access event is changed to stop this from being the case, the server MUST set those users’ m.room.member state to leave.

Security considerations

Each homeserver manages its own guest accounts itself, and whether an account is a guest account or not is not information passed from server to server. Accordingly, any server participating in a room is trusted to properly enforce the permissions outlined in this section.

Homeservers may want to enable protections such as captchas for guest registration to prevent spam, denial of service, and similar attacks.

Homeservers may want to put stricter rate limits on guest accounts, particularly for sending state events.

Room Previews

It is sometimes desirable to offer a preview of a room, where a user can “lurk” and read messages posted to the room, without joining the room. This can be particularly effective when combined with Guest Access.

Previews are implemented via the world_readable Room History Visibility. setting, along with a special version of the GET /events endpoint.

Client behaviour

A client wishing to view a room without joining it should call GET /rooms/:room_id/initialSync, followed by GET /events. Clients will need to do this in parallel for each room they wish to view.

Clients can of course also call other endpoints such as GET /rooms/:room_id/messages and GET /search to access events outside the /events stream.

GET /_matrix/client/v3/events


This will listen for new events related to a particular room and return them to the caller. This will block until an event is received, or until the timeout is reached.

This API is the same as the normal /events endpoint, but can be called by users who have not joined the room.

Note that the normal /events endpoint has been deprecated. This API will also be deprecated at some point, but its replacement is not yet known.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

query parameters
Name Type Description
from string

The token to stream from. This token is either from a previous request to this API or from the initial sync API.

room_id string

The room ID for which events should be returned.

timeout integer

The maximum time in milliseconds to wait for an event.


Responses

Status Description
200 The events received, which may be none.
400 Bad pagination from parameter.

200 response

Name Type Description
chunk [Event]

An array of events.

end string

A token which correlates to the last value in chunk. This token should be used in the next request to /events.

start string

A token which correlates to the first value in chunk. This is usually the same token supplied to from=.

Event
Name Type Description
content object

Required: The body of this event, as created by the client which sent it.

event_id string

Required: The globally unique identifier for this event.

origin_server_ts integer

Required: Timestamp (in milliseconds since the unix epoch) on originating homeserver when this event was sent.

room_id string

Required: The ID of the room associated with this event.

sender string

Required: Contains the fully-qualified ID of the user who sent this event.

state_key string

Present if, and only if, this event is a state event. The key making this piece of state unique in the room. Note that it is often an empty string.

State keys starting with an @ are reserved for referencing user IDs, such as room members. With the exception of a few events, state events set with a given user’s ID as the state key MUST only be set by that user.

type string

Required: The type of the event.

unsigned UnsignedData

Contains optional extra information about the event.

UnsignedData
Name Type Description
age integer

The time in milliseconds that has elapsed since the event was sent. This field is generated by the local homeserver, and may be incorrect if the local time on at least one of the two servers is out of sync, which can cause the age to either be negative or greater than it actually is.

membership string

The room membership of the user making the request, at the time of the event.

This property is the value of the membership property of the requesting user’s m.room.member state at the point of the event, including any changes caused by the event. If the user had yet to join the room at the time of the event (i.e, they have no m.room.member state), this property is set to leave.

Homeservers SHOULD populate this property wherever practical, but they MAY omit it if necessary (for example, if calculating the value is expensive, servers might choose to only implement it in encrypted rooms). The property is not normally populated in events pushed to application services via the application service transaction API (where there is no clear definition of “requesting user”).

Added in v1.11

prev_content EventContent

The previous content for this event. This field is generated by the local homeserver, and is only returned if the event is a state event, and the client has permission to see the previous content.

Changed in v1.2: Previously, this field was specified at the top level of returned events rather than in unsigned (with the exception of the GET .../notifications endpoint), though in practice no known server implementations honoured this.

redacted_because ClientEvent

The event that redacted this event, if any.

transaction_id string

The client-supplied transaction ID, for example, provided via PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}, if the client being given the event is the same one which sent it.

{
  "chunk": [
    {
      "content": {
        "body": "This is an example text message",
        "format": "org.matrix.custom.html",
        "formatted_body": "<b>This is an example text message</b>",
        "msgtype": "m.text"
      },
      "event_id": "$143273582443PhrSn:example.org",
      "origin_server_ts": 1432735824653,
      "room_id": "!somewhere:over.the.rainbow",
      "sender": "@example:example.org",
      "type": "m.room.message",
      "unsigned": {
        "age": 1234,
        "membership": "join"
      }
    }
  ],
  "end": "s3457_9_0",
  "start": "s3456_9_0"
}

Server behaviour

For clients which have not joined a room, servers are required to only return events where the room state at the event had the m.room.history_visibility state event present with history_visibility value world_readable.

Security considerations

Clients may wish to display to their users that rooms which are world_readable may be showing messages to non-joined users. There is no way using this module to find out whether any non-joined guest users do see events in the room, or to list or count any lurking users.

Room Tagging

Users can add tags to rooms. Tags are namespaced strings used to label rooms. A room may have multiple tags. Tags are only visible to the user that set them but are shared across all their devices.

Events

The tags on a room are received as single m.tag event in the account_data section of a room. The content of the m.tag event is a tags key whose value is an object mapping the name of each tag to another object.

The JSON object associated with each tag gives information about the tag, e.g how to order the rooms with a given tag.

Ordering information is given under the order key as a number between 0 and 1. The numbers are compared such that 0 is displayed first. Therefore a room with an order of 0.2 would be displayed before a room with an order of 0.7. If a room has a tag without an order key then it should appear after the rooms with that tag that have an order key.

The name of a tag MUST NOT exceed 255 bytes.

The tag namespace is defined as follows:

  • The namespace m.* is reserved for tags defined in the Matrix specification. Clients must ignore any tags in this namespace they don’t understand.
  • The namespace u.* is reserved for user-defined tags. The portion of the string after the u. is defined to be the display name of this tag. No other semantics should be inferred from tags in this namespace.
  • A client or app willing to use special tags for advanced functionality should namespace them similarly to state keys: tld.name.*
  • Any tag in the tld.name.* form but not matching the namespace of the current client should be ignored
  • Any tag not matching the above rules should be interpreted as a user tag from the u.* namespace, as if the name had already had u. stripped from the start (ie. the name of the tag is used as the display name directly). These non-namespaced tags are supported for historical reasons. New tags should use one of the defined namespaces above.

Several special names are listed in the specification: The following tags are defined in the m.* namespace:

  • m.favourite: The user’s favourite rooms. These should be shown with higher precedence than other rooms.
  • m.lowpriority: These should be shown with lower precedence than others.
  • m.server_notice: Used to identify Server Notice Rooms.

m.tag


Informs the client of tags on a room.

Event type: Message event

Content

Name Type Description
tags {string: Tag}

The tags on the room and their contents.

Tag
Name Type Description
order number

A number in a range [0,1] describing a relative position of the room under the given tag.

Examples

{
  "content": {
    "tags": {
      "u.work": {
        "order": 0.9
      }
    }
  },
  "type": "m.tag"
}

Client Behaviour

GET /_matrix/client/v3/user/{userId}/rooms/{roomId}/tags


List the tags set by a user on a room.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string

Required: The ID of the room to get tags for.

userId string

Required: The id of the user to get tags for. The access token must be authorized to make requests for this user ID.


Responses

Status Description
200 The list of tags for the user for the room.

200 response

Name Type Description
tags {string: Tag}
Tag
Name Type Description
order number

A number in a range [0,1] describing a relative position of the room under the given tag.

{
  "tags": {
    "m.favourite": {
      "order": 0.1
    },
    "u.Customers": {},
    "u.Work": {
      "order": 0.7
    }
  }
}

PUT /_matrix/client/v3/user/{userId}/rooms/{roomId}/tags/{tag}


Add a tag to the room.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string

Required: The ID of the room to add a tag to.

tag string

Required: The tag to add.

userId string

Required: The id of the user to add a tag for. The access token must be authorized to make requests for this user ID.

Request body

Tag
Name Type Description
order number

A number in a range [0,1] describing a relative position of the room under the given tag.

Request body example

{
  "order": 0.25
}

Responses

Status Description
200 The tag was successfully added.

200 response

{}

DELETE /_matrix/client/v3/user/{userId}/rooms/{roomId}/tags/{tag}


Remove a tag from the room.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string

Required: The ID of the room to remove a tag from.

tag string

Required: The tag to remove.

userId string

Required: The id of the user to remove a tag for. The access token must be authorized to make requests for this user ID.


Responses

Status Description
200 The tag was successfully removed.

200 response

{}

Client Config

Clients can store custom config data for their account on their homeserver. This account data will be synced between different devices and can persist across installations on a particular device. Users may only view the account data for their own account.

The account data may be either global or scoped to a particular room. There is no inheritance mechanism here: a given type of data missing from a room’s account data does not fall back to the global account data with the same type.

Events

The client receives the account data as events in the account_data sections of a /sync response.

These events can also be received in a /events response or in the account_data section of a room in a /sync response. m.tag events appearing in /events will have a room_id with the room the tags are for.

Client Behaviour

GET /_matrix/client/v3/user/{userId}/account_data/{type}


Get some account data for the client. This config is only visible to the user that set the account data.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
type string

Required: The event type of the account data to get. Custom types should be namespaced to avoid clashes.

userId string

Required: The ID of the user to get account data for. The access token must be authorized to make requests for this user ID.


Responses

Status Description
200 The account data content for the given type.
403 The access token provided is not authorized to retrieve this user’s account data. Errcode: M_FORBIDDEN.
404 No account data has been provided for this user with the given type. Errcode: M_NOT_FOUND.

200 response

{
  "custom_account_data_key": "custom_config_value"
}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_FORBIDDEN",
  "error": "Cannot add account data for other users."
}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND",
  "error": "Account data not found."
}

PUT /_matrix/client/v3/user/{userId}/account_data/{type}


Set some account data for the client. This config is only visible to the user that set the account data. The config will be available to clients through the top-level account_data field in the homeserver response to /sync.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
type string

Required: The event type of the account data to set. Custom types should be namespaced to avoid clashes.

userId string

Required: The ID of the user to set account data for. The access token must be authorized to make requests for this user ID.

Request body

Request body example

{
  "custom_account_data_key": "custom_config_value"
}

Responses

Status Description
200 The account data was successfully added.
400 The request body is not a JSON object. Errcode: M_BAD_JSON or M_NOT_JSON.
403 The access token provided is not authorized to modify this user’s account data. Errcode: M_FORBIDDEN.
405 This type of account data is controlled by the server; it cannot be modified by clients. Errcode: M_BAD_JSON.

200 response

{}

400 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_JSON",
  "error": "Content must be a JSON object."
}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_FORBIDDEN",
  "error": "Cannot add account data for other users."
}

405 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_BAD_JSON",
  "error": "Cannot set m.fully_read through this API."
}

GET /_matrix/client/v3/user/{userId}/rooms/{roomId}/account_data/{type}


Get some account data for the client on a given room. This config is only visible to the user that set the account data.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string

Required: The ID of the room to get account data for.

type string

Required: The event type of the account data to get. Custom types should be namespaced to avoid clashes.

userId string

Required: The ID of the user to get account data for. The access token must be authorized to make requests for this user ID.


Responses

Status Description
200 The account data content for the given type.
400 The given roomID is not a valid room ID. Errcode: M_INVALID_PARAM.
403 The access token provided is not authorized to retrieve this user’s account data. Errcode: M_FORBIDDEN.
404 No account data has been provided for this user and this room with the given type. Errcode: M_NOT_FOUND.

200 response

{
  "custom_account_data_key": "custom_config_value"
}

400 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_INVALID_PARAM",
  "error": "@notaroomid:example.org is not a valid room ID."
}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_FORBIDDEN",
  "error": "Cannot add account data for other users."
}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND",
  "error": "Room account data not found."
}

PUT /_matrix/client/v3/user/{userId}/rooms/{roomId}/account_data/{type}


Set some account data for the client on a given room. This config is only visible to the user that set the account data. The config will be delivered to clients in the per-room entries via /sync.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string

Required: The ID of the room to set account data on.

type string

Required: The event type of the account data to set. Custom types should be namespaced to avoid clashes.

userId string

Required: The ID of the user to set account data for. The access token must be authorized to make requests for this user ID.

Request body

Request body example

{
  "custom_account_data_key": "custom_account_data_value"
}

Responses

Status Description
200 The account data was successfully added.
400 The request body is not a JSON object (errcode M_BAD_JSON or M_NOT_JSON), or the given roomID is not a valid room ID (errcode M_INVALID_PARAM).
403 The access token provided is not authorized to modify this user’s account data. Errcode: M_FORBIDDEN.
405 This type of account data is controlled by the server; it cannot be modified by clients. Errcode: M_BAD_JSON.

200 response

{}

400 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_JSON",
  "error": "Content must be a JSON object."
}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_FORBIDDEN",
  "error": "Cannot add account data for other users."
}

405 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_BAD_JSON",
  "error": "Cannot set m.fully_read through this API."
}

Server Behaviour

Servers MUST reject setting account data for event types that the server manages by using a 405 error response. Currently, this only includes m.fully_read and m.push_rules. This applies to both global and room-specific account data.

[Changed in v1.10] m.push_rules was added to the rejection list.

Servers must allow clients to read the above event types as normal.

Server Administration

This module adds capabilities for server administrators to inspect server state and data.

Client Behaviour

GET /_matrix/client/v3/admin/whois/{userId}


Gets information about a particular user.

This API may be restricted to only be called by the user being looked up, or by a server admin. Server-local administrator privileges are not specified in this document.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
userId string

Required: The user to look up.


Responses

Status Description
200 The lookup was successful.

200 response

Name Type Description
devices {string: DeviceInfo}

Each key is an identifier for one of the user’s devices.

user_id string

The Matrix user ID of the user.

DeviceInfo
Name Type Description
sessions [SessionInfo]

A user’s sessions (i.e. what they did with an access token from one login).

SessionInfo
Name Type Description
connections [ConnectionInfo]

Information particular connections in the session.

ConnectionInfo
Name Type Description
ip string

Most recently seen IP address of the session.

last_seen integer

Unix timestamp that the session was last active.

user_agent string

User agent string last seen in the session.

{
  "devices": {
    "teapot": {
      "sessions": [
        {
          "connections": [
            {
              "ip": "127.0.0.1",
              "last_seen": 1411996332123,
              "user_agent": "curl/7.31.0-DEV"
            },
            {
              "ip": "10.0.0.2",
              "last_seen": 1411996332123,
              "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.120 Safari/537.36"
            }
          ]
        }
      ]
    }
  },
  "user_id": "@peter:rabbit.rocks"
}

Event Context

This API returns a number of events that happened just before and after the specified event. This allows clients to get the context surrounding an event.

Client behaviour

There is a single HTTP API for retrieving event context, documented below.

GET /_matrix/client/v3/rooms/{roomId}/context/{eventId}


This API returns a number of events that happened just before and after the specified event. This allows clients to get the context surrounding an event.

Note: This endpoint supports lazy-loading of room member events. See Lazy-loading room members for more information.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
eventId string

Required: The event to get context around.

roomId string

Required: The room to get events from.

query parameters
Name Type Description
filter string

A JSON RoomEventFilter to filter the returned events with. The filter is only applied to events_before, events_after, and state. It is not applied to the event itself. The filter may be applied before or/and after the limit parameter - whichever the homeserver prefers.

See Filtering for more information.

limit integer

The maximum number of context events to return. The limit applies to the sum of the events_before and events_after arrays. The requested event ID is always returned in event even if limit is 0. Defaults to 10.


Responses

Status Description
200 The events and state surrounding the requested event.

200 response

Name Type Description
end string

A token that can be used to paginate forwards with.

event ClientEvent

Details of the requested event.

events_after [ClientEvent]

A list of room events that happened just after the requested event, in chronological order.

events_before [ClientEvent]

A list of room events that happened just before the requested event, in reverse-chronological order.

start string

A token that can be used to paginate backwards with.

state [ClientEvent]

The state of the room at the last event returned.

ClientEvent
Name Type Description
content object

Required: The body of this event, as created by the client which sent it.

event_id string

Required: The globally unique identifier for this event.

origin_server_ts integer

Required: Timestamp (in milliseconds since the unix epoch) on originating homeserver when this event was sent.

room_id string

Required: The ID of the room associated with this event.

sender string

Required: Contains the fully-qualified ID of the user who sent this event.

state_key string

Present if, and only if, this event is a state event. The key making this piece of state unique in the room. Note that it is often an empty string.

State keys starting with an @ are reserved for referencing user IDs, such as room members. With the exception of a few events, state events set with a given user’s ID as the state key MUST only be set by that user.

type string

Required: The type of the event.

unsigned UnsignedData

Contains optional extra information about the event.

UnsignedData
Name Type Description
age integer

The time in milliseconds that has elapsed since the event was sent. This field is generated by the local homeserver, and may be incorrect if the local time on at least one of the two servers is out of sync, which can cause the age to either be negative or greater than it actually is.

membership string

The room membership of the user making the request, at the time of the event.

This property is the value of the membership property of the requesting user’s m.room.member state at the point of the event, including any changes caused by the event. If the user had yet to join the room at the time of the event (i.e, they have no m.room.member state), this property is set to leave.

Homeservers SHOULD populate this property wherever practical, but they MAY omit it if necessary (for example, if calculating the value is expensive, servers might choose to only implement it in encrypted rooms). The property is not normally populated in events pushed to application services via the application service transaction API (where there is no clear definition of “requesting user”).

Added in v1.11

prev_content EventContent

The previous content for this event. This field is generated by the local homeserver, and is only returned if the event is a state event, and the client has permission to see the previous content.

Changed in v1.2: Previously, this field was specified at the top level of returned events rather than in unsigned (with the exception of the GET .../notifications endpoint), though in practice no known server implementations honoured this.

redacted_because ClientEvent

The event that redacted this event, if any.

transaction_id string

The client-supplied transaction ID, for example, provided via PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}, if the client being given the event is the same one which sent it.

{
  "end": "t29-57_2_0_2",
  "event": {
    "content": {
      "body": "filename.jpg",
      "info": {
        "h": 398,
        "mimetype": "image/jpeg",
        "size": 31037,
        "w": 394
      },
      "msgtype": "m.image",
      "url": "mxc://example.org/JWEIFJgwEIhweiWJE"
    },
    "event_id": "$f3h4d129462ha:example.com",
    "origin_server_ts": 1432735824653,
    "room_id": "!636q39766251:example.com",
    "sender": "@example:example.org",
    "type": "m.room.message",
    "unsigned": {
      "age": 1234,
      "membership": "join"
    }
  },
  "events_after": [
    {
      "content": {
        "body": "This is an example text message",
        "format": "org.matrix.custom.html",
        "formatted_body": "<b>This is an example text message</b>",
        "msgtype": "m.text"
      },
      "event_id": "$143273582443PhrSn:example.org",
      "origin_server_ts": 1432735824653,
      "room_id": "!636q39766251:example.com",
      "sender": "@example:example.org",
      "type": "m.room.message",
      "unsigned": {
        "age": 1234,
        "membership": "join"
      }
    }
  ],
  "events_before": [
    {
      "content": {
        "body": "something-important.doc",
        "filename": "something-important.doc",
        "info": {
          "mimetype": "application/msword",
          "size": 46144
        },
        "msgtype": "m.file",
        "url": "mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe"
      },
      "event_id": "$143273582443PhrSn:example.org",
      "origin_server_ts": 1432735824653,
      "room_id": "!636q39766251:example.com",
      "sender": "@example:example.org",
      "type": "m.room.message",
      "unsigned": {
        "age": 1234,
        "membership": "join"
      }
    }
  ],
  "start": "t27-54_2_0_2",
  "state": [
    {
      "content": {
        "m.federate": true,
        "predecessor": {
          "event_id": "$something:example.org",
          "room_id": "!oldroom:example.org"
        },
        "room_version": "11"
      },
      "event_id": "$143273582443PhrSn:example.org",
      "origin_server_ts": 1432735824653,
      "room_id": "!636q39766251:example.com",
      "sender": "@example:example.org",
      "state_key": "",
      "type": "m.room.create",
      "unsigned": {
        "age": 1234,
        "membership": "join"
      }
    },
    {
      "content": {
        "avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
        "displayname": "Alice Margatroid",
        "membership": "join",
        "reason": "Looking for support"
      },
      "event_id": "$143273582443PhrSn:example.org",
      "origin_server_ts": 1432735824653,
      "room_id": "!636q39766251:example.com",
      "sender": "@example:example.org",
      "state_key": "@alice:example.org",
      "type": "m.room.member",
      "unsigned": {
        "age": 1234,
        "membership": "join"
      }
    }
  ]
}

Security considerations

The server must only return results that the user has permission to see.

SSO client login/authentication

Single Sign-On (SSO) is a generic term which refers to protocols which allow users to log into applications via a single web-based authentication portal. Examples include OpenID Connect, “Central Authentication Service” (CAS) and SAML.

This module allows a Matrix homeserver to delegate user authentication to an external authentication server supporting one of these protocols. In this process, there are three systems involved:

  • A Matrix client, using the APIs defined in this specification, which is seeking to authenticate a user to a Matrix homeserver.
  • A Matrix homeserver, implementing the APIs defined in this specification, but which is delegating user authentication to the authentication server.
  • An “authentication server”, which is responsible for authenticating the user.

This specification is concerned only with communication between the Matrix client and the homeserver, and is independent of the SSO protocol used to communicate with the authentication server. Different Matrix homeserver implementations might support different SSO protocols.

Clients and homeservers implementing the SSO flow will need to consider both login and user-interactive authentication. The flow is similar in both cases, but there are slight differences.

Typically, SSO systems require a single “callback” URI to be configured at the authentication server. Once the user is authenticated, their browser is redirected to that URI. It is up to the Matrix homeserver implementation to implement a suitable endpoint. For example, for CAS authentication the homeserver should provide a means for the administrator to configure where the CAS server is and the REST endpoints which consume the ticket.

Homeservers may optionally expose multiple possible SSO options for the user to pursue, typically in the form of several “log in with $provider” buttons. These are known as “identity providers” (IdPs).

Client login via SSO

An overview of the process is as follows:

  1. The Matrix client calls GET /login to find the supported login types, and the homeserver includes a flow with "type": "m.login.sso" in the response.
  2. To initiate the m.login.sso login type, the Matrix client instructs the user’s browser to navigate to the /login/sso/redirect endpoint on the user’s homeserver. Note that this may be the IdP-dependent version of the endpoint if the user has selected one of the identity_providers from the flow.
  3. The homeserver responds with an HTTP redirect to the SSO user interface, which the browser follows.
  4. The authentication server and the homeserver interact to verify the user’s identity and other authentication information, potentially using a number of redirects.
  5. The browser is directed to the redirectUrl provided by the client with a loginToken query parameter for the client to log in with.
  6. The client exchanges the login token for an access token by calling the /login endpoint with a type of m.login.token.

For native applications, typically steps 1 to 4 are carried out by opening an embedded web view.

These steps are illustrated as follows:

    Matrix Client                        Matrix Homeserver      Auth Server
        |                                       |                   |
        |-------------(0) GET /login----------->|                   |
        |<-------------login types--------------|                   |
        |                                       |                   |
        |   Webview                             |                   |
        |      |                                |                   |
        |----->|                                |                   |
        |      |--(1) GET /login/sso/redirect-->|                   |
        |      |<---------(2) 302---------------|                   |
        |      |                                |                   |
        |      |<========(3) Authentication process================>|
        |      |                                |                   |
        |      |<--(4) redirect to redirectUrl--|                   |
        |<-----|                                |                   |
        |                                       |                   |
        |---(5) POST /login with login token--->|                   |
        |<-------------access token-------------|                   |

In the older r0.4.0 version of this specification it was possible to authenticate via CAS when the homeserver provides a m.login.cas login flow. This specification deprecates the use of m.login.cas to instead prefer m.login.sso, which is the same process with the only change being which redirect endpoint to use: for m.login.cas, use /cas/redirect and for m.login.sso use /sso/redirect (described below). The endpoints are otherwise the same.

m.login.sso flow schema


m.login.sso flow schema
Name Type Description
identity_providers [IdP]

Optional identity providers (IdPs) to present to the user. These would appear (typically) as distinct buttons for the user to interact with, and would map to the appropriate IdP-dependent redirect endpoint for that IdP.

type string

Required: The string m.login.sso

One of: [m.login.sso].

IdP
Name Type Description
brand string

Optional UI hint for what kind of common SSO provider is being described in this IdP. Matrix maintains a registry of identifiers in the matrix-spec repo to ensure clients and servers are aligned on major/common brands.

Clients should prefer the brand over the icon, when both are provided. Clients are not required to support any particular brand, including those in the registry, though are expected to be able to present any IdP based off the name/icon to the user regardless.

Unregistered brands are permitted using the Common Namespaced Identifier Grammar, though excluding the namespace requirements. For example, examplesso is a valid brand which is not in the registry but still permitted. Servers should be mindful that clients might not support their unregistered brand usage as intended by the server.

icon string

Optional mxc:// URI to provide an image/icon representing the IdP. Intended to be shown alongside the name if provided.

Clients SHOULD use the deprecated /download and /thumbnail endpoints to retrieve this media item because clients will not have an access token they can authenticate with yet. Servers SHOULD ensure media used for IdP icons is excluded from the freeze described by the Content Repository module’s Client Behaviour section.

This may be addressed in the future with proposals like MSC4148, or removed entirely through the transition to OIDC.

id string

Required: Opaque string chosen by the homeserver, uniquely identifying the IdP from other IdPs the homeserver might support. Should use the Opaque identifier Grammar.

name string

Required: Human readable description for the IdP, intended to be shown to the user.

Examples

{
  "identity_providers": [
    {
      "brand": "github",
      "id": "com.example.idp.github",
      "name": "GitHub"
    },
    {
      "icon": "mxc://example.com/abc123",
      "id": "com.example.idp.gitlab",
      "name": "GitLab"
    }
  ],
  "type": "m.login.sso"
}
Client behaviour

The client starts the process by instructing the browser to navigate to /login/sso/redirect (or /login/sso/redirect/{idpId} when using one of the identity_providers) with an appropriate redirectUrl. Once authentication is successful, the browser will be redirected to that redirectUrl.

GET /_matrix/client/v3/login/sso/redirect


Added in v1.1

A web-based Matrix client should instruct the user’s browser to navigate to this endpoint in order to log in via SSO.

The server MUST respond with an HTTP redirect to the SSO interface, or present a page which lets the user select an IdP to continue with in the event multiple are supported by the server.

Rate-limited: No
Requires authentication: No

Request

Request parameters

query parameters
Name Type Description
redirectUrl string

Required: URI to which the user will be redirected after the homeserver has authenticated the user with SSO.


Responses

Status Description
302 A redirect to the SSO interface.

GET /_matrix/client/v3/login/sso/redirect/{idpId}


Added in v1.1

This endpoint is the same as /login/sso/redirect, though with an IdP ID from the original identity_providers array to inform the server of which IdP the client/user would like to continue with.

The server MUST respond with an HTTP redirect to the SSO interface for that IdP.

Rate-limited: No
Requires authentication: No

Request

Request parameters

path parameters
Name Type Description
idpId string

Required: The id of the IdP from the m.login.sso identity_providers array denoting the user’s selection.

query parameters
Name Type Description
redirectUrl string

Required: URI to which the user will be redirected after the homeserver has authenticated the user with SSO.


Responses

Status Description
302 A redirect to the SSO interface.
404 The IdP ID was not recognized by the server. The server is encouraged to provide a user-friendly page explaining the error given the user will be navigated to it.
Security considerations
  1. CSRF attacks via manipulation of parameters on the redirectUrl

    Clients should validate any requests to the redirectUrl. In particular, it may be possible for attackers to falsify any query parameters, leading to cross-site request forgery (CSRF) attacks.

    For example, consider a web-based client at https://client.example.com, which wants to initiate SSO login on the homeserver with server name server.example.org. It does this by storing the server name in a query parameter for the redirectUrl: it redirects to https://server.example.org/login/sso/redirect?redirectUrl=https://client.example.com?hs=server.example.org.

    An attacker could trick a victim into following a link to https://server.example.org/login/sso/redirect?redirectUrl=https://client.example.com?hs=evil.com, which would result in the client sending a login token for the victim’s account to the attacker-controlled site evil.com.

    To guard against this, clients MUST NOT store state (such as the address of the homeserver being logged into) anywhere it can be modified by external processes.

    Instead, the state could be stored in localStorage or in a cookie.

  2. For added security, clients SHOULD include a unique identifier in the redirectUrl and reject any callbacks that do not contain a recognised identifier, to guard against unsolicited login attempts and replay attacks.

Server behaviour

Servers should note that identity_providers are optional, and older clients might not interpret the value correctly. In these cases, the client will use the generic /redirect endpoint instead of the /redirect/{idpId} endpoint.

Redirecting to the Authentication server

The server should handle /_matrix/client/v3/login/sso/redirect as follows:

  1. It should build a suitable request for the SSO system.
  2. It should store enough state that the flow can be securely resumed after the SSO process completes. One way to do this is by storing a cookie which is stored in the user’s browser, by adding a Set-Cookie header to the response.
  3. It should redirect the user’s browser to the SSO login page with the appropriate parameters.

See also the “Security considerations” below.

Handling the callback from the Authentication server

Note that there will normally be a single callback URI which is used for both login and user-interactive authentication: it is up to the homeserver implementation to distinguish which is taking place.

The homeserver should validate the response from the SSO system: this may require additional calls to the authentication server, and/or may require checking a signature on the response.

The homeserver then proceeds as follows:

  1. The homeserver MUST map the user details received from the authentication server to a valid Matrix user identifier. The guidance in Mapping from other character sets may be useful.
  2. If the generated user identifier represents a new user, it should be registered as a new user.
  3. The homeserver should generate a short-term login token. This is an opaque token, suitable for use with the m.login.token type of the /login API. The lifetime of this token SHOULD be limited to around five seconds.
  4. The homeserver adds a query parameter of loginToken, with the value of the generated login token, to the redirectUrl given in the /_matrix/client/v3/login/sso/redirect request. (Note: redirectURL may or may not include existing query parameters. If it already includes one or more loginToken parameters, they should be removed before adding the new one.)
  5. The homeserver redirects the user’s browser to the URI thus built.
Security considerations
  1. Homeservers should ensure that login tokens are not sent to malicious clients.

    For example, consider a homeserver at server.example.org. An attacker tricks a victim into following a link to https://server.example.org/login/sso/redirect?redirectUrl=https://evil.com, resulting in a login token being sent to the attacker-controlled site evil.com. This is a form of cross-site request forgery (CSRF).

    To mitigate this, Homeservers SHOULD confirm with the user that they are happy to grant access to their matrix account to the site named in the redirectUrl. This can be done either before redirecting to the SSO login page when handling the /_matrix/client/v3/login/sso/redirect endpoint, or after login when handling the callback from the authentication server. (If the check is performed before redirecting, it is particularly important that the homeserver guards against unsolicited authentication attempts as below).

    It may be appropriate to whitelist a set of known-trusted client URLs in this process. In particular, the homeserver’s own login fallback implementation could be excluded.

  2. For added security, homeservers SHOULD guard against unsolicited authentication attempts by tracking pending requests. One way to do this is to set a cookie when handling /_matrix/client/v3/login/sso/redirect, which is checked and cleared when handling the callback from the authentication server.

SSO during User-Interactive Authentication

User-interactive authentication is used by client-server endpoints which require additional confirmation of the user’s identity (beyond holding an access token). Typically this means that the user must re-enter their password, but for homeservers which delegate to an SSO server, this means redirecting to the authentication server during user-interactive auth.

The implementation of this is based on the Fallback mechanism for user-interactive auth.

Client behaviour

Clients do not need to take any particular additional steps beyond ensuring that the fallback mechanism has been implemented, and treating the m.login.sso authentication type the same as any other unknown type (i.e. they should open a browser window for /_matrix/client/v3/auth/m.login.sso/fallback/web?session=<session_id>. Once the flow has completed, the client retries the request with the session only.)

Server behaviour

Redirecting to the Authentication server

The server should handle /_matrix/client/v3/auth/m.login.sso/fallback/web in much the same way as /_matrix/client/v3/login/sso/redirect, which is to say:

  1. It should build a suitable request for the SSO system.
  2. It should store enough state that the flow can be securely resumed after the SSO process completes. One way to do this is by storing a cookie which is stored in the user’s browser, by adding a Set-Cookie header to the response.
  3. It should redirect the user’s browser to the SSO login page with the appropriate parameters.

See also the “Security considerations” below.

Handling the callback from the Authentication server

Note that there will normally be a single callback URI which is used for both login and user-interactive authentication: it is up to the homeserver implementation to distinguish which is taking place.

The homeserver should validate the response from the SSO system: this may require additional calls to the authentication server, and/or may require checking a signature on the response.

The homeserver then returns the user-interactive authentication fallback completion page to the user’s browser.

Security considerations
  1. Confirming the operation

    The homeserver SHOULD confirm that the user is happy for the operation to go ahead. The goal of the user-interactive authentication operation is to guard against a compromised access_token being used to take over the user’s account. Simply redirecting the user to the SSO system is insufficient, since they may not realise what is being asked of them, or the SSO system may even confirm the authentication automatically.

    For example, the homeserver might serve a page with words to the effect of:

    A client is trying to remove a device from your account. To confirm this action, re-authenticate with single sign-on. If you did not expect this, your account may be compromised!

    This confirmation could take place before redirecting to the SSO authentication page (when handling the /_matrix/client/v3/auth/m.login.sso/fallback/web endpoint), or after authentication when handling the callback from the authentication server. (If the check is performed before redirecting, it is particularly important that the homeserver guards against unsolicited authentication attempts as below).

  2. For added security, homeservers SHOULD guard against unsolicited authentication attempts by tracking pending requests. One way to do this is to set a cookie when handling /_matrix/client/v3/auth/m.login.sso/fallback/web, which is checked and cleared when handling the callback from the authentication server.

Direct Messaging

All communication over Matrix happens within a room. It is sometimes desirable to offer users the concept of speaking directly to one particular person. This module defines a way of marking certain rooms as ‘direct chats’ with a given person. This does not restrict the chat to being between exactly two people since this would preclude the presence of automated ‘bot’ users or even a ‘personal assistant’ who is able to answer direct messages on behalf of the user in their absence.

A room may not necessarily be considered ‘direct’ by all members of the room, but a signalling mechanism exists to propagate the information of whether a chat is ‘direct’ to an invitee.

Events

m.direct


A map of which rooms are considered ‘direct’ rooms for specific users is kept in account_data in an event of type m.direct. The content of this event is an object where the keys are the user IDs and values are lists of room ID strings of the ‘direct’ rooms for that user ID.

Event type: Message event

Content

Type Description
{User ID: [string]}

The mapping of user ID to a list of room IDs of the ‘direct’ rooms for that user ID.

Examples

{
  "content": {
    "@bob:example.com": [
      "!abcdefgh:example.com",
      "!hgfedcba:example.com"
    ]
  },
  "type": "m.direct"
}

Client behaviour

To start a direct chat with another user, the inviting user’s client should set the is_direct flag to /createRoom. The client should do this whenever the flow the user has followed is one where their intention is to speak directly with another person, as opposed to bringing that person in to a shared room. For example, clicking on ‘Start Chat’ beside a person’s profile picture would imply the is_direct flag should be set.

The invitee’s client may use the is_direct flag in the m.room.member event to automatically mark the room as a direct chat but this is not required: it may for example, prompt the user, or ignore the flag altogether.

Both the inviting client and the invitee’s client should record the fact that the room is a direct chat by storing an m.direct event in the account data using /user/<user_id>/account_data/<type>.

Server behaviour

When the is_direct flag is given to /createRoom, the home server must set the is_direct flag in the invite member event for any users invited in the /createRoom call.

Ignoring Users

With all the communication through Matrix it may be desirable to ignore a particular user for whatever reason. This module defines how clients and servers can implement the ignoring of users.

Events

m.ignored_user_list


A map of users which are considered ignored is kept in account_data in an event type of m.ignored_user_list.

Event type: Message event

Content

Name Type Description
ignored_users {User ID: object}

Required: The map of users to ignore. This is a mapping of user ID to empty object.

Examples

{
  "content": {
    "ignored_users": {
      "@someone:example.org": {}
    }
  },
  "type": "m.ignored_user_list"
}

Client behaviour

To ignore a user, effectively blocking them, the client should add the target user to the m.ignored_user_list event in their account data using /user/<user_id>/account_data/<type>. Once ignored, the client will no longer receive events sent by that user, with the exception of state events. The client should either hide previous content sent by the newly ignored user or perform a new /sync with no previous token.

Invites to new rooms by ignored users will not be sent to the client. The server may optionally reject the invite on behalf of the client.

State events will still be sent to the client, even if the user is ignored. This is to ensure parts, such as the room name, do not appear different to the user just because they ignored the sender.

To remove a user from the ignored users list, remove them from the account data event. The server will resume sending events from the previously ignored user, however it should not send events that were missed while the user was ignored. To receive the events that were sent while the user was ignored the client should perform a fresh sync. The client may also un-hide any events it previously hid due to the user becoming ignored.

Server behaviour

Following an update of the m.ignored_user_list, the sync API for all clients should immediately start ignoring (or un-ignoring) the user. Clients are responsible for determining if they should hide previously sent events or to start a new sync stream.

Servers must still send state events sent by ignored users to clients.

Servers must not send room invites from ignored users to clients. Servers may optionally decide to reject the invite, however.

Sticker Messages

This module allows users to send sticker messages in to rooms or direct messaging sessions.

Sticker messages are specialised image messages that are displayed without controls (e.g. no “download” link, or light-box view on click, as would be displayed for for m.image events).

Sticker messages are intended to provide simple “reaction” events in the message timeline. The matrix client should provide some mechanism to display the sticker “body” e.g. as a tooltip on hover, or in a modal when the sticker image is clicked.

Events

Sticker events are received as a single m.sticker event in the timeline section of a room, in a /sync.

m.sticker


This message represents a single sticker image.

Event type: Message event

Content

Name Type Description
body string

Required: A textual representation or associated description of the sticker image. This could be the alt text of the original image, or a message to accompany and further describe the sticker.

info ImageInfo

Required: Metadata about the image referred to in url including a thumbnail representation.

url string

Required: The URL to the sticker image. This must be a valid mxc:// URI.

ImageInfo
Name Type Description
h integer

The intended display height of the image in pixels. This may differ from the intrinsic dimensions of the image file.

mimetype string

The mimetype of the image, e.g. image/jpeg.

size integer

Size of the image in bytes.

thumbnail_file EncryptedFile

Information on the encrypted thumbnail file, as specified in End-to-end encryption. Only present if the thumbnail is encrypted.

thumbnail_info ThumbnailInfo

Metadata about the image referred to in thumbnail_url.

thumbnail_url string

The URL (typically mxc:// URI) to a thumbnail of the image. Only present if the thumbnail is unencrypted.

w integer

The intended display width of the image in pixels. This may differ from the intrinsic dimensions of the image file.

ThumbnailInfo
Name Type Description
h integer

The intended display height of the image in pixels. This may differ from the intrinsic dimensions of the image file.

mimetype string

The mimetype of the image, e.g. image/jpeg.

size integer

Size of the image in bytes.

w integer

The intended display width of the image in pixels. This may differ from the intrinsic dimensions of the image file.

Examples

{
  "content": {
    "body": "Landing",
    "info": {
      "h": 200,
      "mimetype": "image/png",
      "size": 73602,
      "thumbnail_info": {
        "h": 200,
        "mimetype": "image/png",
        "size": 73602,
        "w": 140
      },
      "thumbnail_url": "mxc://matrix.org/sHhqkFCvSkFwtmvtETOtKnLP",
      "w": 140
    },
    "url": "mxc://matrix.org/sHhqkFCvSkFwtmvtETOtKnLP"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "type": "m.sticker",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

Client behaviour

Clients supporting this message type should display the image content from the event URL directly in the timeline.

A thumbnail image should be provided in the info object. This is largely intended as a fallback for clients that do not fully support the m.sticker event type. In most cases it is fine to set the thumbnail URL to the same URL as the main event content.

It is recommended that sticker image content should be 512x512 pixels in size or smaller. The dimensions of the image file should be twice the intended display size specified in the info object in order to assist rendering sharp images on higher DPI screens.

Reporting Content

Users may encounter content which they find inappropriate and should be able to report it to the server administrators or room moderators for review. This module defines a way for users to report content.

Client behaviour

POST /_matrix/client/v3/rooms/{roomId}/report


Added in v1.13

Reports a room as inappropriate to the server, which may then notify the appropriate people. How such information is delivered is left up to implementations. The caller is not required to be joined to the room to report it.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string

Required: The room being reported.

Request body

Name Type Description
reason string

The reason the room is being reported.

Request body example

{
  "reason": "this makes me sad"
}

Responses

Status Description
200 The room has been reported successfully.
404 The room was not found on the homeserver.
429 This request was rate-limited.

200 response

{}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND",
  "error": "The room was not found."
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

POST /_matrix/client/v3/rooms/{roomId}/report/{eventId}


Changed in v1.8: This endpoint now requires the user to be joined to the room.

Reports an event as inappropriate to the server, which may then notify the appropriate people. The caller must be joined to the room to report it.

It might be possible for clients to deduce whether an event exists by timing the response, as only a report for an event that does exist will require the homeserver to check whether a user is joined to the room. To combat this, homeserver implementations should add a random delay when generating a response.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
eventId string

Required: The event to report.

roomId string

Required: The room in which the event being reported is located.

Request body

Name Type Description
reason string

The reason the content is being reported.

score integer

The score to rate this content as where -100 is most offensive and 0 is inoffensive.

Request body example

{
  "reason": "this makes me sad",
  "score": -100
}

Responses

Status Description
200 The event has been reported successfully.
404

The event was not found or you are not joined to the room where the event resides.

Homeserver implementations can additionally return this error if the reported event has been redacted.

200 response

{}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND",
  "error": "The event was not found or you are not joined to the room."
}

Server behaviour

Servers are free to handle the reported content however they desire. This may be a dedicated room to alert server administrators to the reported content or some other mechanism for notifying the appropriate people.

Particularly during waves of harmful content, users may report whole rooms instead of individual events. Server administrators and safety teams should, therefore, be cautious not to shut down rooms that might otherwise be legitimate.

[Changed in v1.8] When processing event reports, servers MUST verify that the reporting user is currently joined to the room the event is in before accepting a report.

[Added in v1.13] Contrarily, servers MUST NOT restrict room reports based on whether or not the reporting user is joined to the room. This is because users can be exposed to harmful content without being joined to a room. For instance, through room directories or invites.

Third-party Networks

Application services can provide access to third-party networks via bridging. This allows Matrix users to communicate with users on other communication platforms, with messages ferried back and forth by the application service. A single application service may bridge multiple third-party networks, and many individual locations within those networks. A single third-party network location may be bridged to multiple Matrix rooms.

Third-party Lookups

A client may wish to provide a rich interface for joining third-party locations and connecting with third-party users. Information necessary for such an interface is provided by third-party lookups.

GET /_matrix/client/v3/thirdparty/location


Retrieve an array of third-party network locations from a Matrix room alias.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

query parameters
Name Type Description
alias string

Required: The Matrix room alias to look up.


Responses

Status Description
200 All found third-party locations.
404 The Matrix room alias was not found

200 response

Array of Location.

Location
Name Type Description
alias string

Required: An alias for a matrix room.

fields object

Required: Information used to identify this third-party location.

protocol string

Required: The protocol ID that the third-party location is a part of.

[
  {
    "alias": "#freenode_#matrix:matrix.org",
    "fields": {
      "channel": "#matrix",
      "network": "freenode"
    },
    "protocol": "irc"
  }
]

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND"
}

GET /_matrix/client/v3/thirdparty/location/{protocol}


Requesting this endpoint with a valid protocol name results in a list of successful mapping results in a JSON array. Each result contains objects to represent the Matrix room or rooms that represent a portal to this third-party network. Each has the Matrix room alias string, an identifier for the particular third-party network protocol, and an object containing the network-specific fields that comprise this identifier. It should attempt to canonicalise the identifier as much as reasonably possible given the network type.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
protocol string

Required: The protocol used to communicate to the third-party network.

query parameters
Name Type Description
fields {string: string}

One or more custom fields to help identify the third-party location.


Responses

Status Description
200 At least one portal room was found.
404 No portal rooms were found.

200 response

Array of Location.

Location
Name Type Description
alias string

Required: An alias for a matrix room.

fields object

Required: Information used to identify this third-party location.

protocol string

Required: The protocol ID that the third-party location is a part of.

[
  {
    "alias": "#freenode_#matrix:matrix.org",
    "fields": {
      "channel": "#matrix",
      "network": "freenode"
    },
    "protocol": "irc"
  }
]

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND"
}

GET /_matrix/client/v3/thirdparty/protocol/{protocol}


Fetches the metadata from the homeserver about a particular third-party protocol.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
protocol string

Required: The name of the protocol.


Responses

Status Description
200 The protocol was found and metadata returned.
404 The protocol is unknown.

200 response

Protocol
Name Type Description
field_types {string: Field Type}

Required: The type definitions for the fields defined in user_fields and location_fields. Each entry in those arrays MUST have an entry here. The string key for this object is the field name itself.

May be an empty object if no fields are defined.

icon string

Required: A content URI representing an icon for the third-party protocol.

instances [Protocol Instance]

Required: A list of objects representing independent instances of configuration. For example, multiple networks on IRC if multiple are provided by the same application service.

location_fields [string]

Required: Fields which may be used to identify a third-party location. These should be ordered to suggest the way that entities may be grouped, where higher groupings are ordered first. For example, the name of a network should be searched before the name of a channel.

user_fields [string]

Required: Fields which may be used to identify a third-party user. These should be ordered to suggest the way that entities may be grouped, where higher groupings are ordered first. For example, the name of a network should be searched before the nickname of a user.

Field Type
Name Type Description
placeholder string

Required: A placeholder serving as a valid example of the field value.

regexp string

Required: A regular expression for validation of a field’s value. This may be relatively coarse to verify the value as the application service providing this protocol may apply additional validation or filtering.

Protocol Instance
Name Type Description
desc string

Required: A human-readable description for the protocol, such as the name.

fields object

Required: Preset values for fields the client may use to search by.

icon string

An optional content URI representing the protocol. Overrides the one provided at the higher level Protocol object.

network_id string

Required: A unique identifier across all instances.

{
  "field_types": {
    "channel": {
      "placeholder": "#foobar",
      "regexp": "#[^\\s]+"
    },
    "network": {
      "placeholder": "irc.example.org",
      "regexp": "([a-z0-9]+\\.)*[a-z0-9]+"
    },
    "nickname": {
      "placeholder": "username",
      "regexp": "[^\\s#]+"
    }
  },
  "icon": "mxc://example.org/aBcDeFgH",
  "instances": [
    {
      "desc": "Freenode",
      "fields": {
        "network": "freenode"
      },
      "icon": "mxc://example.org/JkLmNoPq",
      "network_id": "freenode"
    }
  ],
  "location_fields": [
    "network",
    "channel"
  ],
  "user_fields": [
    "network",
    "nickname"
  ]
}

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND"
}

GET /_matrix/client/v3/thirdparty/protocols


Fetches the overall metadata about protocols supported by the homeserver. Includes both the available protocols and all fields required for queries against each protocol.

Rate-limited: No
Requires authentication: Yes

Request

No request parameters or request body.


Responses

Status Description
200 The protocols supported by the homeserver.

200 response

Type Description
{string: Protocol}

Dictionary of supported third-party protocols.

Protocol
Name Type Description
field_types {string: Field Type}

Required: The type definitions for the fields defined in user_fields and location_fields. Each entry in those arrays MUST have an entry here. The string key for this object is the field name itself.

May be an empty object if no fields are defined.

icon string

Required: A content URI representing an icon for the third-party protocol.

instances [Protocol Instance]

Required: A list of objects representing independent instances of configuration. For example, multiple networks on IRC if multiple are provided by the same application service.

location_fields [string]

Required: Fields which may be used to identify a third-party location. These should be ordered to suggest the way that entities may be grouped, where higher groupings are ordered first. For example, the name of a network should be searched before the name of a channel.

user_fields [string]

Required: Fields which may be used to identify a third-party user. These should be ordered to suggest the way that entities may be grouped, where higher groupings are ordered first. For example, the name of a network should be searched before the nickname of a user.

Field Type
Name Type Description
placeholder string

Required: A placeholder serving as a valid example of the field value.

regexp string

Required: A regular expression for validation of a field’s value. This may be relatively coarse to verify the value as the application service providing this protocol may apply additional validation or filtering.

Protocol Instance
Name Type Description
desc string

Required: A human-readable description for the protocol, such as the name.

fields object

Required: Preset values for fields the client may use to search by.

icon string

An optional content URI representing the protocol. Overrides the one provided at the higher level Protocol object.

network_id string

Required: A unique identifier across all instances.

{
  "gitter": {
    "field_types": {
      "room": {
        "placeholder": "matrix-org/matrix-doc",
        "regexp": "[^\\s]+\\/[^\\s]+"
      },
      "username": {
        "placeholder": "@username",
        "regexp": "@[^\\s]+"
      }
    },
    "instances": [
      {
        "desc": "Gitter",
        "fields": {},
        "icon": "mxc://example.org/zXyWvUt",
        "network_id": "gitter"
      }
    ],
    "location_fields": [
      "room"
    ],
    "user_fields": [
      "username"
    ]
  },
  "irc": {
    "field_types": {
      "channel": {
        "placeholder": "#foobar",
        "regexp": "#[^\\s]+"
      },
      "network": {
        "placeholder": "irc.example.org",
        "regexp": "([a-z0-9]+\\.)*[a-z0-9]+"
      },
      "nickname": {
        "placeholder": "username",
        "regexp": "[^\\s]+"
      }
    },
    "icon": "mxc://example.org/aBcDeFgH",
    "instances": [
      {
        "desc": "Freenode",
        "fields": {
          "network": "freenode.net"
        },
        "icon": "mxc://example.org/JkLmNoPq",
        "network_id": "freenode"
      }
    ],
    "location_fields": [
      "network",
      "channel"
    ],
    "user_fields": [
      "network",
      "nickname"
    ]
  }
}

GET /_matrix/client/v3/thirdparty/user


Retrieve an array of third-party users from a Matrix User ID.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

query parameters
Name Type Description
userid string

Required: The Matrix User ID to look up.


Responses

Status Description
200 An array of third-party users.
404 The Matrix User ID was not found.

200 response

Array of User.

User
Name Type Description
fields object

Required: Information used to identify this third-party location.

protocol string

Required: The protocol ID that the third-party location is a part of.

userid string

Required: A Matrix User ID representing a third-party user.

[
  {
    "fields": {
      "user": "jim"
    },
    "protocol": "gitter",
    "userid": "@_gitter_jim:matrix.org"
  }
]

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND"
}

GET /_matrix/client/v3/thirdparty/user/{protocol}


Retrieve a Matrix User ID linked to a user on the third-party service, given a set of user parameters.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
protocol string

Required: The name of the protocol.

query parameters
Name Type Description
fields {string: string}

One or more custom fields that are passed to the AS to help identify the user.


Responses

Status Description
200 The Matrix User IDs found with the given parameters.
404 The Matrix User ID was not found.

200 response

Array of User.

User
Name Type Description
fields object

Required: Information used to identify this third-party location.

protocol string

Required: The protocol ID that the third-party location is a part of.

userid string

Required: A Matrix User ID representing a third-party user.

[
  {
    "fields": {
      "user": "jim"
    },
    "protocol": "gitter",
    "userid": "@_gitter_jim:matrix.org"
  }
]

404 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_NOT_FOUND"
}

OpenID

This module allows users to verify their identity with a third-party service. The third-party service does need to be matrix-aware in that it will need to know to resolve matrix homeservers to exchange the user’s token for identity information.

POST /_matrix/client/v3/user/{userId}/openid/request_token


Gets an OpenID token object that the requester may supply to another service to verify their identity in Matrix. The generated token is only valid for exchanging for user information from the federation API for OpenID.

The access token generated is only valid for the OpenID API. It cannot be used to request another OpenID access token or call /sync, for example.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
userId string

Required: The user to request an OpenID token for. Should be the user who is authenticated for the request.

Request body

Request body example

{}

Responses

Status Description
200 OpenID token information. This response is nearly compatible with the response documented in the OpenID Connect 1.0 Specification with the only difference being the lack of an id_token. Instead, the Matrix homeserver’s name is provided.
429 This request was rate-limited.

200 response

OpenIdCredentials
Name Type Description
access_token string

Required: An access token the consumer may use to verify the identity of the person who generated the token. This is given to the federation API GET /openid/userinfo to verify the user’s identity.

expires_in integer

Required: The number of seconds before this token expires and a new one must be generated.

matrix_server_name string

Required: The homeserver domain the consumer should use when attempting to verify the user’s identity.

token_type string

Required: The string Bearer.

{
  "access_token": "SomeT0kenHere",
  "expires_in": 3600,
  "matrix_server_name": "example.com",
  "token_type": "Bearer"
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

Server Access Control Lists (ACLs) for rooms

In some scenarios room operators may wish to prevent a malicious or untrusted server from participating in their room. Sending an m.room.server_acl state event into a room is an effective way to prevent the server from participating in the room at the federation level.

Server ACLs can also be used to make rooms only federate with a limited set of servers, or retroactively make the room no longer federate with any other server, similar to setting the m.federate value on the m.room.create event.

m.room.server_acl


An event to indicate which servers are permitted to participate in the room. Server ACLs may allow or deny groups of hosts. All servers participating in the room, including those that are denied, are expected to uphold the server ACL. Servers that do not uphold the ACLs MUST be added to the denied hosts list in order for the ACLs to remain effective.

The allow and deny lists are lists of glob-style patterns. When comparing against the server ACLs, the suspect server’s port number must not be considered. Therefore evil.com, evil.com:8448, and evil.com:1234 would all match rules that apply to evil.com, for example.

The ACLs are applied to servers when they make requests, and are applied in the following order:

  1. If there is no m.room.server_acl event in the room state, allow.
  2. If the server name is an IP address (v4 or v6) literal, and allow_ip_literals is present and false, deny.
  3. If the server name matches an entry in the deny list, deny.
  4. If the server name matches an entry in the allow list, allow.
  5. Otherwise, deny.

Note: Server ACLs do not restrict the events relative to the room DAG via authorisation rules, but instead act purely at the network layer to determine which servers are allowed to connect and interact with a given room.

Warning: Failing to provide an allow rule of some kind will prevent all servers from participating in the room, including the sender. This renders the room unusable. A common allow rule is [ "*" ] which would still permit the use of the deny list without losing the room.

Warning: All compliant servers must implement server ACLs. However, legacy or noncompliant servers exist which do not uphold ACLs, and these MUST be manually appended to the denied hosts list when setting an ACL to prevent them from leaking events from banned servers into a room. Currently, the only way to determine noncompliant hosts is to check the prev_events of leaked events, therefore detecting servers which are not upholding the ACLs. Server versions can also be used to try to detect hosts that will not uphold the ACLs, although this is not comprehensive. Server ACLs were added in Synapse v0.32.0, although other server implementations and versions exist in the world.

Event type: State event
State key A zero-length string.

Content

Name Type Description
allow [string]

The server names to allow in the room, excluding any port information. Each entry is interpreted as a glob-style pattern.

This defaults to an empty list when not provided, effectively disallowing every server.

allow_ip_literals boolean

True to allow server names that are IP address literals. False to deny. Defaults to true if missing or otherwise not a boolean.

This is strongly recommended to be set to false as servers running with IP literal names are strongly discouraged in order to require legitimate homeservers to be backed by a valid registered domain name.

deny [string]

The server names to disallow in the room, excluding any port information. Each entry is interpreted as a glob-style pattern.

This defaults to an empty list when not provided.

Examples

{
  "content": {
    "allow": [
      "*"
    ],
    "allow_ip_literals": false,
    "deny": [
      "*.evil.com",
      "evil.com"
    ]
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "state_key": "",
  "type": "m.room.server_acl",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

Port numbers are not supported because it is unclear to parsers whether a port number should be matched or an IP address literal. Additionally, it is unlikely that one would trust a server running on a particular domain’s port but not a different port, especially considering the server host can easily change ports.

CIDR notation is not supported for IP addresses because Matrix does not encourage the use of IPs for identifying servers. Instead, a blanket allow_ip_literals is provided to cover banning them.

Client behaviour

Clients are not expected to perform any additional duties beyond sending the event. Clients should describe changes to the server ACLs to the user in the user interface, such as in the timeline.

Clients may wish to kick affected users from the room prior to denying a server access to the room to help prevent those servers from participating and to provide feedback to the users that they have been excluded from the room.

Server behaviour

Servers MUST prevent blacklisted servers from sending events or participating in the room when an m.room.server_acl event is present in the room state. Which APIs are specifically affected are described in the Server-Server API specification.

Servers should still send events to denied servers if they are still residents of the room.

Security considerations

Server ACLs are only effective if every server in the room honours them. Servers that do not honour the ACLs may still permit events sent by denied servers into the room, leaking them to other servers in the room. To effectively enforce an ACL in a room, the servers that do not honour the ACLs should be denied in the room as well.

User and room mentions

[Changed in v1.7]

This module allows users to “mention” other users and rooms within a room event. This is primarily used as an indicator that the recipient should receive a notification about the event. This is achieved by including metadata in the m.mentions content property of the event to reference the entity being mentioned.

m.mentions is defined as follows:

m.mentions


Describes whether the event mentions other users or the room. This is contained within the event’s content alongside other fields for the relevant event type.

m.mentions
Name Type Description
room boolean

A boolean set to true to mention the room, for an @room notification. (room should otherwise not be included on the event.)

user_ids [string]

A list of Matrix IDs of mentioned users.

Examples

{
  "user_ids": [
    "@alice:example.org"
  ]
}

An event’s content will then look like this:

{
    "body": "Hello Alice!",
    "msgtype": "m.text",
    "format": "org.matrix.custom.html",
    "formatted_body": "Hello <a href='https://matrix.to/#/@alice:example.org'>Alice</a>!",
    "m.mentions": {
        "user_ids": ["@alice:example.org"]
    }
}

Additionally, see the .m.rule.is_user_mention and .m.rule.is_room_mention push rules. Users should not add their own Matrix ID to the m.mentions property as outgoing messages cannot self-notify.

If an encrypted event contains an m.mentions in its payload, it should be encrypted as normal. To properly process mentions in encrypted rooms, events must be decrypted first. See receiving notifications.

Note that, for backwards compatibility, push rules such as .m.rule.contains_display_name, .m.rule.contains_user_name, and .m.rule.roomnotif continue to match if the body of the event contains the user’s display name or ID. To avoid unintentional notifications, it is recommended that clients include a m.mentions property on each event. (If there are no mentions to include it can be an empty object.)

In previous versions of the specification, mentioning users was done by including the user’s display name or the localpart of their Matrix ID and room mentions were done by including the string “@room” in the plaintext body of the event. This was prone to confusing and buggy behaviour.

Client behaviour

Although it is possible to silently mention users, it is recommended to include a Matrix URI in the HTML body of an m.room.message event. This applies only to m.room.message events where the msgtype is m.text, m.emote, or m.notice. The format for the event must be org.matrix.custom.html and therefore requires a formatted_body.

Clients should use the following guidelines when adding a Matrix URI representing a mention to events to be sent:

  • When linking to users, use the user’s potentially ambiguous display name for the anchor’s text. If the user does not have a display name, use the user’s ID.
  • When linking to rooms, use the canonical alias for the room. If the room does not have a canonical alias, prefer one of the aliases listed on the room. If no alias can be found, fall back to the room ID. In all cases, use the alias/room ID being linked to as the anchor’s text.

The text component of the anchor should be used in the event’s body where the link would normally be represented, as shown in the example above.

Clients should display mentions differently from other elements. For example, this may be done by changing the background color of the mention to indicate that it is different from a normal link.

If the current user is mentioned in a message, the client should show that mention differently from other mentions, such as by using a red background color to signify to the user that they were mentioned. Note that it is possible for a user to be mentioned without including their Matrix URI in the event.

When clicked, the mention should navigate the user to the appropriate user or room information.

Room Upgrades

From time to time, a room may need to be upgraded to a different room version for a variety of reasons. This module defines a way for rooms to upgrade to a different room version when needed.

Events

m.room.tombstone


A state event signifying that a room has been upgraded to a different room version, and that clients should go there.

Event type: State event
State key A zero-length string.

Content

Name Type Description
body string

Required: A server-defined message.

replacement_room string

Required: The room ID of the new room the client should be visiting.

Examples

{
  "content": {
    "body": "This room has been replaced",
    "replacement_room": "!newroom:example.org"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "state_key": "",
  "type": "m.room.tombstone",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

Client behaviour

Clients which understand m.room.tombstone events and the predecessor field on m.room.create events should communicate to the user that the room was upgraded. One way of accomplishing this would be hiding the old room from the user’s room list and showing banners linking between the old and new room - ensuring that permalinks work when referencing the old room. Another approach may be to virtually merge the rooms such that the old room’s timeline seamlessly continues into the new timeline without the user having to jump between the rooms.

POST /_matrix/client/v3/rooms/{roomId}/upgrade


Upgrades the given room to a particular room version.

Rate-limited: No
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string

Required: The ID of the room to upgrade.

Request body

Name Type Description
new_version string

Required: The new version for the room.

Request body example

{
  "new_version": "2"
}

Responses

Status Description
200 The room was successfully upgraded.
400 The request was invalid. One way this can happen is if the room version requested is not supported by the homeserver.
403 The user is not permitted to upgrade the room.

200 response

Name Type Description
replacement_room string

Required: The ID of the new room.

{
  "replacement_room": "!newroom:example.org"
}

400 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_UNSUPPORTED_ROOM_VERSION",
  "error": "This server does not support that room version"
}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_FORBIDDEN",
  "error": "You cannot upgrade this room"
}

Server behaviour

When the client requests to upgrade a known room to a known version, the server:

  1. Checks that the user has permission to send m.room.tombstone events in the room.

  2. [Changed in v1.4] Creates a replacement room with a m.room.create event containing a predecessor field, the applicable room_version, and a type field which is copied from the predecessor room. If no type is set on the previous room, no type is specified on the new room’s create event either.

  3. Replicates transferable state events to the new room. The exact details for what is transferred is left as an implementation detail, however the recommended state events to transfer are:

    • m.room.server_acl
    • m.room.encryption
    • m.room.name
    • m.room.avatar
    • m.room.topic
    • m.room.guest_access
    • m.room.history_visibility
    • m.room.join_rules
    • m.room.power_levels

    Membership events should not be transferred to the new room due to technical limitations of servers not being able to impersonate people from other homeservers. Additionally, servers should not transfer state events which are sensitive to who sent them, such as events outside of the Matrix namespace where clients may rely on the sender to match certain criteria.

  4. Moves any local aliases to the new room.

  5. Sends a m.room.tombstone event to the old room to indicate that it is not intended to be used any further.

  6. If possible, the power levels in the old room should also be modified to prevent sending of events and inviting new users. For example, setting events_default and invite to the greater of 50 and users_default + 1.

When a user joins the new room, the server should automatically transfer/replicate some of the user’s personalized settings such as notifications, tags, etc.

Server Notices

Homeserver hosts often want to send messages to users in an official capacity, or have resource limits which affect a user’s ability to use the homeserver. For example, the homeserver may be limited to a certain number of active users per month and has exceeded that limit. To communicate this failure to users, the homeserver would use the Server Notices room.

The aesthetics of the room (name, topic, avatar, etc) are left as an implementation detail. It is recommended that the homeserver decorate the room such that it looks like an official room to users.

Events

Notices are sent to the client as normal m.room.message events with a msgtype of m.server_notice in the server notices room. Events with a m.server_notice msgtype outside of the server notice room must be ignored by clients.

The specified values for server_notice_type are:

m.server_notice.usage_limit_reached The server has exceeded some limit which requires the server administrator to intervene. The limit_type describes the kind of limit reached. The specified values for limit_type are:

monthly_active_user The server’s number of active users in the last 30 days has exceeded the maximum. New connections are being refused by the server. What defines “active” is left as an implementation detail, however servers are encouraged to treat syncing users as “active”.

m.room.message with msgtype: m.server_notice


Represents a server notice for a user.

Event type: Message event

Content

Name Type Description
admin_contact string

A URI giving a contact method for the server administrator. Required if the notice type is m.server_notice.usage_limit_reached.

body string

Required: A human-readable description of the notice.

limit_type string

The kind of usage limit the server has exceeded. Required if the notice type is m.server_notice.usage_limit_reached.

msgtype string

Required:

One of: [m.server_notice].

server_notice_type string

Required: The type of notice being represented.

Examples

{
  "content": {
    "admin_contact": "mailto:[email protected]",
    "body": "Human-readable message to explain the notice",
    "limit_type": "monthly_active_user",
    "msgtype": "m.server_notice",
    "server_notice_type": "m.server_notice.usage_limit_reached"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "type": "m.room.message",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

Client behaviour

Clients can identify the server notices room by the m.server_notice tag on the room. Active notices are represented by the pinned events in the server notices room. Server notice events pinned in that room should be shown to the user through special UI and not through the normal pinned events interface in the client. For example, clients may show warning banners or bring up dialogs to get the user’s attention. Events which are not server notice events and are pinned in the server notices room should be shown just like any other pinned event in a room.

The client must not expect to be able to reject an invite to join the server notices room. Attempting to reject the invite must result in a M_CANNOT_LEAVE_SERVER_NOTICE_ROOM error. Servers should not prevent the user leaving the room after joining the server notices room, however the same error code must be used if the server will prevent leaving the room.

Server behaviour

Servers should manage exactly 1 server notices room per user. Servers must identify this room to clients with the m.server_notice tag. Servers should invite the target user rather than automatically join them to the server notice room.

How servers send notices to clients, and which user they use to send the events, is left as an implementation detail for the server.

Moderation policy lists

With Matrix being an open network where anyone can participate, a very wide range of content exists and it is important that users are empowered to select which content they wish to see, and which content they wish to block. By extension, room moderators and server admins should also be able to select which content they do not wish to host in their rooms and servers.

The protocol’s position on this is one of neutrality: it should not be deciding what content is undesirable for any particular entity and should instead be empowering those entities to make their own decisions. As such, a generic framework for communicating “moderation policy lists” or “moderation policy rooms” is described. Note that this module only describes the data structures and not how they should be interpreted: the entity making the decisions on filtering is best positioned to interpret the rules how it sees fit.

Moderation policy lists are stored as room state events. There are no restrictions on how the rooms can be configured (they could be public, private, encrypted, etc).

There are currently 3 kinds of entities which can be affected by rules: user, server, and room. All 3 are described with m.policy.rule.<kind> state events. The state_key for a policy rule is an arbitrary string decided by the sender of the rule.

Rules contain recommendations and reasons for the rule existing. The reason is a human-readable string which describes the recommendation. Currently only one recommendation, m.ban, is specified.

m.ban recommendation

When this recommendation is used, the entities affected by the rule should be banned from participation where possible. The enforcement of this is deliberately left as an implementation detail to avoid the protocol imposing its opinion on how the policy list is to be interpreted. However, a suggestion for a simple implementation is as follows:

  • Is a user rule…
    • Applied to a user: The user should be added to the subscriber’s ignore list.
    • Applied to a room: The user should be banned from the room (either on sight or immediately).
    • Applied to a server: The user should not be allowed to send invites to users on the server.
  • Is a room rule…
    • Applied to a user: The user should leave the room and not join it (MSC2270-style ignore).
    • Applied to a room: No-op because a room cannot ban itself.
    • Applied to a server: The server should prevent users from joining the room and from receiving invites to it.
  • Is a server rule…
    • Applied to a user: The user should not receive events or invites from the server.
    • Applied to a room: The server is added as a denied server in the ACLs.
    • Applied to a server: The subscriber should avoid federating with the server as much as possible by blocking invites from the server and not sending traffic unless strictly required (no outbound invites).

Subscribing to policy lists

This is deliberately left as an implementation detail. For implementations using the Client-Server API, this could be as easy as joining or peeking the room. Joining or peeking is not required, however: an implementation could poll for updates or use a different technique for receiving updates to the policy’s rules.

Events

The entity described by the state events is interpreted as a glob-style pattern. Note that rules against rooms can describe a room ID or room alias - the subscriber is responsible for resolving the alias to a room ID if desired.

m.policy.rule.user


A moderation policy rule which affects users.

Event type: State event
State key An arbitrary string decided upon by the sender.

Content

Name Type Description
entity string

Required: The entity affected by this rule. Glob characters * and ? can be used to match zero or more characters or exactly one character respectively.

reason string

Required: The human-readable description for the recommendation.

recommendation string

Required: The suggested action to take. Currently only m.ban is specified.

Examples

{
  "content": {
    "entity": "@alice*:example.org",
    "reason": "undesirable behaviour",
    "recommendation": "m.ban"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "state_key": "rule:@alice*:example.org",
  "type": "m.policy.rule.user",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

m.policy.rule.room


A moderation policy rule which affects room IDs and room aliases.

Event type: State event
State key An arbitrary string decided upon by the sender.

Content

Name Type Description
entity string

Required: The entity affected by this rule. Glob characters * and ? can be used to match zero or more characters or exactly one character respectively.

reason string

Required: The human-readable description for the recommendation.

recommendation string

Required: The suggested action to take. Currently only m.ban is specified.

Examples

{
  "content": {
    "entity": "#*:example.org",
    "reason": "undesirable content",
    "recommendation": "m.ban"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "state_key": "rule:#*:example.org",
  "type": "m.policy.rule.room",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

m.policy.rule.server


A moderation policy rule which affects servers.

Event type: State event
State key An arbitrary string decided upon by the sender.

Content

Name Type Description
entity string

Required: The entity affected by this rule. Glob characters * and ? can be used to match zero or more characters or exactly one character respectively.

reason string

Required: The human-readable description for the recommendation.

recommendation string

Required: The suggested action to take. Currently only m.ban is specified.

Examples

{
  "content": {
    "entity": "*.example.org",
    "reason": "undesirable engagement",
    "recommendation": "m.ban"
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "state_key": "rule:*.example.org",
  "type": "m.policy.rule.server",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

Client behaviour

As described above, the client behaviour is deliberately left undefined.

Server behaviour

Servers have no additional requirements placed on them by this module.

Security considerations

This module could be used to build a system of shared blacklists, which may create a divide within established communities if not carefully deployed. This may well not be a suitable solution for all communities.

Depending on how implementations handle subscriptions, user IDs may be linked to policy lists and therefore expose the views of that user. For example, a client implementation which joins the user to the policy room would expose the user’s ID to observers of the policy room. In future, MSC1228 and MSC1777 (or similar) could help solve this concern.

Spaces

[Added in v1.2]

Often used to group rooms of similar subject matter (such as a public “Official matrix.org rooms” space or personal “Work stuff” space), spaces are a way to organise rooms while being represented as rooms themselves.

A space is defined by the m.space room type, making it known as a “space-room”. The space’s name, topic, avatar, aliases, etc are all defined through the existing relevant state events within the space-room.

Sending normal m.room.message events within the space-room is discouraged - clients are not generally expected to have a way to render the timeline of the room. As such, space-rooms should be created with m.room.power_levels which prohibit normal events by setting events_default to a suitably high number. In the default power level structure, this would be 100. Clients might wish to go a step further and explicitly ignore notification counts on space-rooms.

Membership of a space is defined and controlled by the existing mechanisms which govern a room: m.room.member, m.room.history_visibility, and m.room.join_rules. Public spaces are encouraged to have a similar setup to public rooms: world_readable history visibility, published canonical alias, and suitably public join_rule. Invites, including third-party invites, still work just as they do in normal rooms as well.

All other aspects of regular rooms are additionally carried over, such as the ability to set arbitrary state events, hold room account data, etc. Spaces are just rooms with extra functionality on top.

Managing rooms/spaces included in a space

Spaces form a hierarchy of rooms which clients can use to structure their room list into a tree-like view. The parent/child relationship can be defined in two ways: with m.space.child state events in the space-room, or with m.space.parent state events in the child room.

In most cases, both the child and parent relationship should be defined to aid discovery of the space and its rooms. When only a m.space.child is used, the space is effectively a curated list of rooms which the rooms themselves might not be aware of. When only a m.space.parent is used, the rooms are “secretly” added to spaces with the effect of not being advertised directly by the space.

Considering spaces are rooms themselves, it is possible to nest spaces within spaces, and it is possible to create a loop. Though the creation of loops is explicitly disallowed, implementations might still encounter them and must be careful not to loop infinitely when this happens.

Clients and servers should additionally be aware of excessively long trees which may cause performance issues.

m.space.child relationship

When using this approach, the state events get sent into the space-room which is the parent to the room. The state_key for the event is the child room’s ID.

For example, to achieve the following:

#space:example.org
    #general:example.org (!abcdefg:example.org)
    !private:example.org

the state of #space:example.org would consist of:

Unimportant fields trimmed for brevity.

{
    "type": "m.space.child",
    "state_key": "!abcdefg:example.org",
    "content": {
        "via": ["example.org"]
    }
}
{
    "type": "m.space.child",
    "state_key": "!private:example.org",
    "content": {
        "via": ["example.org"]
    }
}

No state events in the child rooms themselves would be required (though they can also be present). This allows for users to define personal/private spaces to organise their own rooms without needing explicit permission from the room moderators/admins.

Child rooms can be removed from a space by omitting the via key of content on the relevant state event, such as through redaction or otherwise clearing the content.

m.space.child


Defines the relationship of a child room to a space-room. Has no effect in rooms which are not spaces.

Event type: State event
State key The child room ID being described.

Content

Name Type Description
order string

Optional string to define ordering among space children. These are lexicographically compared against other children’s order, if present.

Must consist of ASCII characters within the range \x20 (space) and \x7E (~), inclusive. Must not exceed 50 characters.

order values with the wrong type, or otherwise invalid contents, are to be treated as though the order key was not provided.

See Ordering of children within a space for information on how the ordering works.

suggested boolean

Optional (default false) flag to denote whether the child is “suggested” or of interest to members of the space. This is primarily intended as a rendering hint for clients to display the room differently, such as eagerly rendering them in the room list.

via [string]

Required: A list of servers to try and join through. See also: Routing.

When not present or invalid, the child room is not considered to be part of the space.

Examples

{
  "content": {
    "order": "lexicographically_compare_me",
    "suggested": true,
    "via": [
      "example.org",
      "other.example.org"
    ]
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "state_key": "!roomid:example.org",
  "type": "m.space.child",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}
Ordering of children within a space

When the client is displaying the children of a space, the children should be ordered using the algorithm below. In some cases, like a traditional left side room list, the client may override the ordering to provide better user experience. A theoretical space summary view would however show the children ordered.

Taking the set of space children, first order the children with a valid order key lexicographically by Unicode code-points such that \x20 (space) is sorted before \x7E (~). Then, take the remaining children and order them by the origin_server_ts of their m.space.child event in ascending numeric order, placing them after the children with a valid order key in the resulting set.

In cases where the order values are the same, the children are ordered by their timestamps. If the timestamps are the same, the children are ordered lexicographically by their room IDs (state keys) in ascending order.

Noting the careful use of ASCII spaces here, the following demonstrates a set of space children being ordered appropriately:

Unimportant fields trimmed for brevity.

[
    {
        "type": "m.space.child",
        "state_key": "!b:example.org",
        "origin_server_ts": 1640341000000,
        "content": {
            "order": " ",
            "via": ["example.org"]
        }
    },
    {
        "type": "m.space.child",
        "state_key": "!a:example.org",
        "origin_server_ts": 1640141000000,
        "content": {
            "order": "aaaa",
            "via": ["example.org"]
        }
    },
    {
        "type": "m.space.child",
        "state_key": "!c:example.org",
        "origin_server_ts": 1640841000000,
        "content": {
            "order": "first",
            "via": ["example.org"]
        }
    },
    {
        "type": "m.space.child",
        "state_key": "!e:example.org",
        "origin_server_ts": 1640641000000,
        "content": {
            "via": ["example.org"]
        }
    },
    {
        "type": "m.space.child",
        "state_key": "!d:example.org",
        "origin_server_ts": 1640741000000,
        "content": {
            "via": ["example.org"]
        }
    }
]
  1. !b:example.org is first because \x20 is before aaaa lexically.
  2. !a:example.org is next because aaaa is before first lexically.
  3. !c:example.org is next because first is the last order value.
  4. !e:example.org is next because the event timestamp is smallest.
  5. !d:example.org is last because the event timestamp is largest.
m.space.parent relationships

Rooms can additionally claim to be part of a space by populating their own state with a parent event. Similar to child events within spaces, the parent event’s state_key is the room ID of the parent space, and they have a similar via list within their content to denote both whether or not the link is valid and which servers might be possible to join through.

To avoid situations where a room falsely claims it is part of a given space, m.space.parent events should be ignored unless one of the following is true:

  • A corresponding m.space.child event can be found in the supposed parent space.
  • The sender of the m.space.parent event has sufficient power level in the supposed parent space to send m.space.child state events (there doesn’t need to be a matching child event).

Clients might need to peek into a parent space to inspect the room state if they aren’t already joined. If the client is unable to peek the state, the link should be assumed to be invalid.

A consequence of the second condition is that a room admin being demoted in the parent space, leaving the parent space, or otherwise being removed from the parent space can mean that a previously valid m.space.parent event becomes invalid.

m.space.parent events can additionally include a canonical boolean key in their content to denote that the parent space is the main/primary space for the room. This can be used to, for example, have the client find other rooms by peeking into that space and suggesting them to the user. Only one canonical parent should exist, though this is not enforced. To tiebreak, use the lowest room ID sorted lexicographically by Unicode code-points.

m.space.parent


Defines the relationship of a room to a parent space-room.

Event type: State event
State key The parent room ID.

Content

Name Type Description
canonical boolean

Optional (default false) flag to denote this parent is the primary parent for the room.

When multiple canonical parents are found, the lowest parent when ordering by room ID lexicographically by Unicode code-points should be used.

via [string]

Required: A list of servers to try and join through. See also: Routing.

When not present or invalid, the room is not considered to be part of the parent space.

Examples

{
  "content": {
    "canonical": true,
    "via": [
      "example.org",
      "other.example.org"
    ]
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "state_key": "!parent_roomid:example.org",
  "type": "m.space.parent",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

Discovering rooms within spaces

Often the client will want to assist the user in exploring what rooms/spaces are part of a space. This can be done with crawling m.space.child state events in the client and peeking into the rooms to get information like the room name, though this is impractical for most cases.

Instead, a hierarchy API is provided to walk the space tree and discover the rooms with their aesthetic details.

The GET /hierarchy API works in a depth-first manner: when it encounters another space as a child it recurses into that space before returning non-space children.

Though prohibited, it is still possible for loops to occur. Servers should gracefully break loops.

Additionally, a given child room might appear multiple times in the response as a grandchild (for example).

GET /_matrix/client/v1/rooms/{roomId}/hierarchy


Added in v1.2

Paginates over the space tree in a depth-first manner to locate child rooms of a given space.

Where a child room is unknown to the local server, federation is used to fill in the details. The servers listed in the via array should be contacted to attempt to fill in missing rooms.

Only m.space.child state events of the room are considered. Invalid child rooms and parent events are not covered by this endpoint.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string

Required: The room ID of the space to get a hierarchy for.

query parameters
Name Type Description
from string

A pagination token from a previous result. If specified, max_depth and suggested_only cannot be changed from the first request.

limit integer

Optional limit for the maximum number of rooms to include per response. Must be an integer greater than zero.

Servers should apply a default value, and impose a maximum value to avoid resource exhaustion.

max_depth integer

Optional limit for how far to go into the space. Must be a non-negative integer.

When reached, no further child rooms will be returned.

Servers should apply a default value, and impose a maximum value to avoid resource exhaustion.

suggested_only boolean

Optional (default false) flag to indicate whether or not the server should only consider suggested rooms. Suggested rooms are annotated in their m.space.child event contents.


Responses

Status Description
200 A portion of the space tree, starting at the provided room ID.
400

The request was invalid in some way. A meaningful errcode and description error text will be returned. Example reasons for rejection are:

  • The from token is unknown to the server.
  • suggested_only or max_depth changed during pagination.
403

The user cannot view or peek on the room. A meaningful errcode and description error text will be returned. Example reasons for rejection are:

  • The room is not set up for peeking.
  • The user has been banned from the room.
  • The room does not exist.
429 This request was rate-limited.

200 response

Name Type Description
next_batch string

A token to supply to from to keep paginating the responses. Not present when there are no further results.

rooms [SpaceHierarchyRoomsChunk]

Required: The rooms for the current page, with the current filters.

SpaceHierarchyRoomsChunk
Name Type Description
avatar_url URI

The URL for the room’s avatar, if one is set.

canonical_alias string

The canonical alias of the room, if any.

children_state [StrippedChildStateEvent]

Required: The m.space.child events of the space-room, represented as Stripped State Events with an added origin_server_ts key.

If the room is not a space-room, this should be empty.

guest_can_join boolean

Required: Whether guest users may join the room and participate in it. If they can, they will be subject to ordinary power level rules like any other user.

join_rule string

The room’s join rule. When not present, the room is assumed to be public.

name string

The name of the room, if any.

num_joined_members integer

Required: The number of members joined to the room.

room_id string

Required: The ID of the room.

room_type string

The type of room (from m.room.create), if any.

Added in v1.4

topic string

The topic of the room, if any.

world_readable boolean

Required: Whether the room may be viewed by guest users without joining.

StrippedChildStateEvent
Name Type Description
content EventContent

Required: The content for the event.

origin_server_ts integer

Required: The origin_server_ts for the event.

sender string

Required: The sender for the event.

state_key string

Required: The state_key for the event.

type string

Required: The type for the event.

{
  "next_batch": "next_batch_token",
  "rooms": [
    {
      "avatar_url": "mxc://example.org/abcdef",
      "canonical_alias": "#general:example.org",
      "children_state": [
        {
          "content": {
            "via": [
              "example.org"
            ]
          },
          "origin_server_ts": 1629413349153,
          "sender": "@alice:example.org",
          "state_key": "!a:example.org",
          "type": "m.space.child"
        }
      ],
      "guest_can_join": false,
      "join_rule": "public",
      "name": "The First Space",
      "num_joined_members": 42,
      "room_id": "!space:example.org",
      "room_type": "m.space",
      "topic": "No other spaces were created first, ever",
      "world_readable": true
    }
  ]
}

400 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_INVALID_PARAM",
  "error": "suggested_only and max_depth cannot change on paginated requests"
}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_FORBIDDEN",
  "error": "You are not allowed to view this room."
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}
Server behaviour

In the case where the server does not have access to the state of a child room, it can request the information over federation with the GET /hierarchy API. The response to this endpoint should be cached for a period of time. The response might additionally contain information about rooms the requesting user is already a member of, or that the server is aware of - the local data should be used instead of the remote server’s data.

Note that the response to the client endpoint is contextual based on the user. Servers are encouraged to cache the data for a period of time, though permission checks may need to be performed to ensure the response is accurate for that user.

Event replacements

[Added in v1.4]

Event replacements, or “message edit events”, are events that use an event relationship with a rel_type of m.replace, which indicates that the original event is intended to be replaced.

An example of a message edit event might look like this:

{
    "type": "m.room.message",
    "content": {
        "body": "* Hello! My name is bar",
        "msgtype": "m.text",
        "m.new_content": {
            "body": "Hello! My name is bar",
            "msgtype": "m.text"
        },
        "m.relates_to": {
            "rel_type": "m.replace",
            "event_id": "$some_event_id"
        }
    },
    // ... other fields required by events
}

The content of the replacement must contain a m.new_content property which defines the replacement content. The normal content properties (body, msgtype etc.) provide a fallback for clients which do not understand replacement events.

m.new_content can include any properties that would normally be found in an event’s content property, such as formatted_body (see m.room.message msgtypes).

Validity of replacement events

There are a number of requirements on replacement events, which must be satisfied for the replacement to be considered valid:

  • As with all event relationships, the original event and replacement event must have the same room_id (i.e. you cannot send an event in one room and then an edited version in a different room).

  • The original event and replacement event must have the same sender (i.e. you cannot edit someone else’s messages).

  • The replacement and original events must have the same type (i.e. you cannot change the original event’s type).

  • The replacement and original events must not have a state_key property (i.e. you cannot edit state events at all).

  • The original event must not, itself, have a rel_type of m.replace (i.e. you cannot edit an edit — though you can send multiple edits for a single original event).

  • The replacement event (once decrypted, if appropriate) must have an m.new_content property.

If any of these criteria are not satisfied, implementations should ignore the replacement event (the content of the original should not be replaced, and the edit should not be included in the server-side aggregation).

Note that the msgtype property of replacement m.room.message events does not need to be the same as in the original event. For example, it is legitimate to replace an m.text event with an m.emote.

Editing encrypted events

If the original event was encrypted, the replacement should be too. In that case, m.new_content is placed in the content of the encrypted payload. As with all event relationships, the m.relates_to property must be sent in the unencrypted (cleartext) part of the event.

For example, a replacement for an encrypted event might look like this:

{
    "type": "m.room.encrypted",
    "content": {
        "m.relates_to": {
            "rel_type": "m.replace",
            "event_id": "$some_event_id"
        },
        "algorithm": "m.megolm.v1.aes-sha2",
        "sender_key": "<sender_curve25519_key>",
        "device_id": "<sender_device_id>",
        "session_id": "<outbound_group_session_id>",
        "ciphertext": "<encrypted_payload_base_64>"
    }
    // irrelevant fields not shown
}

… and, once decrypted, the payload might look like this:

{
    "type": "m.room.<event_type>",
    "room_id": "!some_room_id",
    "content": {
        "body": "* Hello! My name is bar",
        "msgtype": "m.text",
        "m.new_content": {
            "body": "Hello! My name is bar",
            "msgtype": "m.text"
        }
    }
}

Note that:

  • There is no m.relates_to property in the encrypted payload. If there was, it would be ignored.
  • There is no m.new_content property in the cleartext content of the m.room.encrypted event. As above, if there was then it would be ignored.

The payload of an encrypted replacement event must be encrypted as normal, including ratcheting any Megolm session as normal. The original Megolm ratchet entry should not be re-used.

Applying m.new_content

When applying a replacement, the content of the original event is treated as being overwritten entirely by m.new_content, with the exception of m.relates_to, which is left unchanged. Any m.relates_to property within m.new_content is ignored.

For example, given a pair of events:

{
    "event_id": "$original_event",
    "type": "m.room.message",
    "content": {
        "body": "I really like cake",
        "msgtype": "m.text",
        "formatted_body": "I really like cake",
    }
}
{
    "event_id": "$edit_event",
    "type": "m.room.message",
    "content": {
        "body": "* I really like *chocolate* cake",
        "msgtype": "m.text",
        "m.new_content": {
            "body": "I really like *chocolate* cake",
            "msgtype": "m.text",
            "com.example.extension_property": "chocolate"
        },
        "m.relates_to": {
            "rel_type": "m.replace",
            "event_id": "$original_event_id"
        }
    }
}

… then the end result is an event as shown below:

{
    "event_id": "$original_event",
    "type": "m.room.message",
    "content": {
        "body": "I really like *chocolate* cake",
        "msgtype": "m.text",
        "com.example.extension_property": "chocolate"
    }
}

Note that formatted_body is now absent, because it was absent in the replacement event.

Server behaviour

Server-side aggregation of m.replace relationships

[Changed in v1.7]

Note that there can be multiple events with an m.replace relationship to a given event (for example, if an event is edited multiple times). These should be aggregated by the homeserver.

The aggregation format of m.replace relationships gives the most recent replacement event, formatted as normal.

The most recent event is determined by comparing origin_server_ts; if two or more replacement events have identical origin_server_ts, the event with the lexicographically largest event_id is treated as more recent.

As with any other aggregation of child events, the m.replace aggregation is included under the m.relations property in unsigned for any event that is the target of an m.replace relationship. For example:

{
  "event_id": "$original_event_id",
  "type": "m.room.message",
  "content": {
    "body": "I really like cake",
    "msgtype": "m.text",
    "formatted_body": "I really like cake"
  },
  "unsigned": {
    "m.relations": {
      "m.replace": {
        "event_id": "$latest_edit_event_id",
        "origin_server_ts": 1649772304313,
        "sender": "@editing_user:localhost"
        "type": "m.room.message",
        "content": {
          "body": "* I really like *chocolate* cake",
          "msgtype": "m.text",
          "m.new_content": {
            "body": "I really like *chocolate* cake",
            "msgtype": "m.text"
          },
          "m.relates_to": {
            "rel_type": "m.replace",
            "event_id": "$original_event_id"
          }
        }
      }
    }
  }
  // irrelevant fields not shown
}

If the original event is redacted, any m.replace relationship should not be bundled with it (whether or not any subsequent replacements are themselves redacted). Note that this behaviour is specific to the m.replace relationship. See also redactions of edited events below.

Note: the content of the original event is left intact. In particular servers should not replace the content with that of the replacement event.

In previous versions of the specification, servers were expected to replace the content of an edited event whenever it was served to clients (with the exception of the GET /_matrix/client/v3/rooms/{roomId}/event/{eventId} endpoint). However, that behaviour made reliable client-side implementation difficult, and servers should no longer make this replacement.

Client behaviour

Since the server will not replace the content of any edited events, clients should take note of any replacement events they receive, and apply the replacement whenever possible and appropriate.

Client authors are reminded to take note of the requirements for Validity of replacement events, and to ignore any invalid replacement events that are received.

When creating links to events (also known as permalinks), clients build links which reference the event that the creator of the permalink is viewing at that point (which might be a message edit event).

The client viewing the permalink should resolve this reference to the original event, and then display the most recent version of that event.

Redactions of edited events

When an event using a rel_type of m.replace is redacted, it removes that edit revision. This has little effect if there were subsequent edits. However, if it was the most recent edit, the event is in effect reverted to its content before the redacted edit.

Redacting the original message in effect removes the message, including all subsequent edits, from the visible timeline. In this situation, homeservers will return an empty content for the original event as with any other redacted event, and as above the replacement events will not be included in the aggregation bundled with the original event. Note that the subsequent edits are not actually redacted themselves: they simply serve no purpose within the visible timeline.

Edits of events with mentions

When editing an event with user and room mentions the replacement event will have two m.mentions properties:

  • One at the top-level of the content, which should contain mentions due to this edit revision.
  • One inside the m.new_content property, which should contain the resolved mentions for the final version of the event.

The difference between these properties ensures that users will not be notified for each edit revision of an event, but allows for new users to be mentioned (or for re-notifying if the sending client feels a large enough revision was made).

For example, if there is an event mentioning Alice:

{
    "event_id": "$original_event",
    "type": "m.room.message",
    "content": {
        "body": "Hello Alice!",
        "m.mentions": {
            "user_ids": ["@alice:example.org"]
        }
    }
}

And an edit to also mention Bob:

{
  "content": {
    "body": "* Hello Alice & Bob!",
    "m.mentions": {
      "user_ids": [
        // Include only the newly mentioned user.
        "@bob:example.org"
      ]
    },
    "m.new_content": {
      "body": "Hello Alice & Bob!",
      "m.mentions": {
        "user_ids": [
          // Include all of the mentioned users.
          "@alice:example.org",
          "@bob:example.org"
        ]
      },
    },
    "m.relates_to": {
      "rel_type": "m.replace",
      "event_id": "$original_event"
    }
  },
  // other fields as required by events
}

If an edit revision removes a user’s mention then that user’s Matrix ID should be included in neither m.mentions property.

Clients may also wish to modify the client behaviour of determining if an event mentions the current user by checking the m.mentions property under m.new_content.

Edits of replies

A particular constraint applies to events which replace a reply: in contrast to the original reply, there should be no m.in_reply_to property in the the m.relates_to object, since it would be redundant (see Applying m.new_content above, which notes that the original event’s m.relates_to is preserved), as well as being contrary to the spirit of the event relationships mechanism which expects only one “parent” per event.

[Changed in v1.13] In previous versions of the specification, events which replace a reply could include a fallback in the content. This is no longer the case.

An example of an edit to a reply is as follows:

{
  "type": "m.room.message",
  // irrelevant fields not shown
  "content": {
    "body": "* reply",
    "msgtype": "m.text",
    "m.new_content": {
      "body": "reply",
      "msgtype": "m.text",
    },
    "m.relates_to": {
      "rel_type": "m.replace",
      "event_id": "$original_reply_event"
    }
  }
}

Event annotations and reactions

[Added in v1.7]

m.annotation relationship type

Annotations are events that use an event relationship with a rel_type of m.annotation.

Annotations are normally used for “reactions”: for example, if the user wants to react to an event with a thumbs-up, then the client sends an annotation event with the corresponding emoji (👍). Another potential usage is to allow bots to send an event indicating the success or failure of a command.

Along with the normal properties event_id and rel_type, an m.relates_to property with rel_type: m.annotation should contain a key that indicates the annotation being applied. For example, when reacting with emojis, the key contains the emoji being used.

An example m.annotation relationship is shown below:

"m.relates_to": {
    "rel_type": "m.annotation",
    "event_id": "$some_event_id",
    "key": "👍"
}

Any type of event is eligible for an annotation, including state events.

Events

m.reaction


Indicates a reaction to a previous event.

Has no defined content properties of its own. Its only purpose is to hold an m.relates_to property.

Since they contain no content other than m.relates_to, m.reaction events are normally not encrypted, as there would be no benefit in doing so.

Event type: Message event

Content

Name Type Description
m.relates_to ReactionRelatesTo

Indicates the event being reacted to, and the type of reaction.

ReactionRelatesTo
Name Type Description
event_id string

The event ID of the event that this is a reaction to.

key string

The reaction being made, usually an emoji.

If this is an emoji, it should include the unicode emoji presentation selector (\uFE0F) for codepoints which allow it (see the emoji variation sequences list).

rel_type string

One of: [m.annotation].

Examples

{
  "content": {
    "m.relates_to": {
      "event_id": "$some_event_id",
      "key": "👍",
      "rel_type": "m.annotation"
    }
  },
  "event_id": "$143273582443PhrSn:example.org",
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "type": "m.reaction",
  "unsigned": {
    "age": 1234,
    "membership": "join"
  }
}

Client behaviour

The intention of annotations is that they are counted up, rather than being displayed individually. Clients must keep count of the number of annotations with a given event type and annotation key they observe for each event; these counts are typically presented alongside the event in the timeline.

When performing this count:

  • Each event type and annotation key should normally be counted separately, though whether to actually do so is an implementation decision.

  • Annotation events sent by ignored users should be excluded from the count.

  • Multiple identical annotations (i.e., with the same event type and annotation key) from the same user (i.e., events with the same sender) should be treated as a single annotation.

  • Implementations should ignore any annotation event which refers to an event which itself has an m.relates_to with rel_type: m.annotation or rel_type: m.replace. In other words, it is not possible to annotate a replacement event or an annotation. Annotations should instead refer to the original event.

  • When an annotation is redacted, it is removed from the count.

It is not possible to edit a reaction, since replacement events do not change m.relates_to (see Applying m.new_content), and there is no other meaningful content within m.reaction. If a user wishes to change their reaction, the original reaction should be redacted and a new one sent in its place.

The key field in m.reaction can be any string so clients must take care to render long reactions in a sensible manner. For example, clients can elide overly-long reactions.

Server behaviour

Avoiding duplicate annotations

Homeservers should prevent users from sending a second annotation for a given event with identical event type and annotation key (unless the first event has been redacted).

Attempts to send such an annotation should be rejected with a 400 error and an error code of M_DUPLICATE_ANNOTATION.

Note that this does not guarantee that duplicate annotations will not arrive over federation. Clients are responsible for deduplicating received annotations when counting annotations.

Server-side aggregation of m.annotation relationships

m.annotation relationships are not aggregated by the server. In other words, m.annotation is not included in the m.relations property.

Threading

[Added in v1.4]

Threads allow users to visually branch their conversations in a room. Typically mostly used when a room is discussing multiple topics, threads provide more organisation of communication that traditional rich replies can’t always offer.

Clients SHOULD render threads differently to regular messages or replies in the timeline, such as by providing some context to what is going on in the thread but keeping the full conversation history behind a disclosure.

Threads are established using a rel_type of m.thread and reference the thread root (the main timeline event to which the thread events refer). It is not possible to create a thread from an event which itself is the child of an event relationship (i.e., one with an m.relates_to property with a rel_type property - see Relationship types). It is therefore also not possible to nest threads.

Unlike rich reply chains, all events in a thread reference the thread root instead of the most recent message.

As a worked example, the following represents a thread and how it would be formed:

{
  // irrelevant fields excluded
  "type": "m.room.message",
  "event_id": "$alice_hello",
  "sender": "@alice:example.org",
  "content": {
    "msgtype": "m.text",
    "body": "Hello world! How are you?"
  }
}
{
  // irrelevant fields excluded
  "type": "m.room.message",
  "event_id": "$bob_hello",
  "sender": "@bob:example.org",
  "content": {
    "m.relates_to": {
      "rel_type": "m.thread",
      "event_id": "$alice_hello"
    },
    "msgtype": "m.text",
    "body": "I'm doing okay, thank you! How about yourself?"
  }
}
{
  // irrelevant fields excluded
  "type": "m.room.message",
  "event_id": "$alice_reply",
  "sender": "@alice:example.org",
  "content": {
    "m.relates_to": {
      "rel_type": "m.thread",
      "event_id": "$alice_hello" // note: always references the *thread root*
    },
    "msgtype": "m.text",
    "body": "I'm doing great! Thanks for asking."
  }
}

As shown, any event without a rel_type can become a thread root by simply referencing it using an m.thread relationship.

Fallback for unthreaded clients

Clients which understand how to work with threads should simply do so, however clients which might not be aware of threads (due to age or scope) might not be able to helpfully represent the conversation history to its users.

To work around this, events sent by clients which understand threads SHOULD include rich reply metadata to attempt to form a reply chain representation of the conversation. This representation is not ideal for heavily threaded rooms, but allows for users to have context as to what is being discussed with respect to other messages in the room.

This representation is achieved by merging the two relationships and setting a new is_falling_back flag to true.

// within an event's content...
"m.relates_to": {
  // The m.thread relationship structure
  "rel_type": "m.thread",
  "event_id": "$root",

  // The rich reply structure
  "m.in_reply_to": {
    // The most recent message known to the client in the thread.
    // This should be something with a high chance of being rendered by the other client,
    // such as an `m.room.message` event.
    "event_id": "$target"
  },

  // A flag to denote that this is a thread with reply fallback
  "is_falling_back": true
}

Clients which are acutely aware of threads (they do not render threads, but are otherwise aware of the feature existing in the spec) can treat rich replies to an event with a rel_type of m.thread as a threaded reply, for conversation continuity on the threaded client’s side.

To do this, copy the event_id (thread root) from the event being replied to, add the m.in_reply_to metadata, and add is_falling_back: true to m.relates_to.

Replies within threads

In the fallback for unthreaded clients section, a new is_falling_back flag is added to m.relates_to. This flag defaults to false when not provided, which also allows a threaded message to contain a reply itself.

Aside from is_falling_back being false (or not specified), the fallback for unthreaded clients is used to create a reply within a thread: clients should render the event accordingly.

Server behaviour

Validation of m.thread relationships

Servers SHOULD reject client requests which attempt to start a thread off an event with an m.relates_to property. If the client attempts to target an event which itself has an m.relates_to property, then it should receive a HTTP 400 error response with appropriate error message, as per the standard error response structure.

A specific error code is not currently available for this case: servers should use M_UNKNOWN alongside the HTTP 400 status code.

Server-side aggregation of m.thread relationships

Given threads always reference the thread root, an event can have multiple “child” events which then form the thread itself. These events should be aggregated by the server.

The aggregation for threads includes some information about the user’s participation in the thread, the approximate number of events in the thread (as known to the server), and the most recent event in the thread (topologically).

As with any other aggregation of child events, the m.thread aggregation is included under the m.relations property in unsigned for the thread root. For example:

{
  "event_id": "$root_event",
  // irrelevant fields not shown
  "unsigned": {
    "m.relations": {
      "m.thread": {
        "latest_event": {
          // A serialized copy of the latest event in the thread.
          // Some fields are not shown here for brevity.
          "event_id": "$message",
          "sender": "@alice:example.org",
          "room_id": "!room:example.org",
          "type": "m.room.message",
          "content": {
            "msgtype": "m.text",
            "body": "Woo! Threads!"
          },
          "unsigned": {
            "m.relations": {
              // ...
            }
          }
        },
        "count": 7,
        "current_user_participated": true
      }
    }
  }
}

latest_event is the most recent event (topologically to the server) in the thread sent by an un-ignored user.

Note that, as in the example above, child events of the latest_event should themselves be aggregated and included under m.relations for that event. The server should be careful to avoid loops, though loops are not currently possible due to m.thread not being permitted to target an event with an m.relates_to property.

count is simply the number of events using m.thread as a rel_type pointing to the target event. It does not include events sent by ignored users.

current_user_participated is true when the authenticated user is either:

  1. The sender of the thread root event.
  2. The sender of an event which references the thread root with a rel_type of m.thread.

Querying threads in a room

Clients looking to get all the events in a thread can use GET /relations/{threadRootId}/m.thread, however getting all threads in a room is done through a dedicated API:

GET /_matrix/client/v1/rooms/{roomId}/threads


Added in v1.4

This API is used to paginate through the list of the thread roots in a given room.

Optionally, the returned list may be filtered according to whether the requesting user has participated in the thread.

Rate-limited: Yes
Requires authentication: Yes

Request

Request parameters

path parameters
Name Type Description
roomId string

Required: The room ID where the thread roots are located.

query parameters
Name Type Description
from string

A pagination token from a previous result. When not provided, the server starts paginating from the most recent event visible to the user (as per history visibility rules; topologically).

include string

Optional (default all) flag to denote which thread roots are of interest to the caller. When all, all thread roots found in the room are returned. When participated, only thread roots for threads the user has participated in will be returned.

One of: [all, participated].

limit integer

Optional limit for the maximum number of thread roots to include per response. Must be an integer greater than zero.

Servers should apply a default value, and impose a maximum value to avoid resource exhaustion.


Responses

Status Description
200 A portion of the available thread roots in the room, based on the filter criteria.
400

The request was invalid in some way. A meaningful errcode and description error text will be returned. Example reasons for rejection are:

  • The from token is unknown to the server.
403

The user cannot view or peek on the room. A meaningful errcode and description error text will be returned. Example reasons for rejection are:

  • The room is not set up for peeking.
  • The user has been banned from the room.
  • The room does not exist.
429 This request was rate-limited.

200 response

Name Type Description
chunk [ClientEvent]

Required: The thread roots, ordered by the latest_event in each event’s aggregated children. All events returned include bundled aggregations.

If the thread root event was sent by an ignored user, the event is returned redacted to the caller. This is to simulate the same behaviour of a client doing aggregation locally on the thread.

next_batch string

A token to supply to from to keep paginating the responses. Not present when there are no further results.

ClientEvent
Name Type Description
content object

Required: The body of this event, as created by the client which sent it.

event_id string

Required: The globally unique identifier for this event.

origin_server_ts integer

Required: Timestamp (in milliseconds since the unix epoch) on originating homeserver when this event was sent.

room_id string

Required: The ID of the room associated with this event.

sender string

Required: Contains the fully-qualified ID of the user who sent this event.

state_key string

Present if, and only if, this event is a state event. The key making this piece of state unique in the room. Note that it is often an empty string.

State keys starting with an @ are reserved for referencing user IDs, such as room members. With the exception of a few events, state events set with a given user’s ID as the state key MUST only be set by that user.

type string

Required: The type of the event.

unsigned UnsignedData

Contains optional extra information about the event.

UnsignedData
Name Type Description
age integer

The time in milliseconds that has elapsed since the event was sent. This field is generated by the local homeserver, and may be incorrect if the local time on at least one of the two servers is out of sync, which can cause the age to either be negative or greater than it actually is.

membership string

The room membership of the user making the request, at the time of the event.

This property is the value of the membership property of the requesting user’s m.room.member state at the point of the event, including any changes caused by the event. If the user had yet to join the room at the time of the event (i.e, they have no m.room.member state), this property is set to leave.

Homeservers SHOULD populate this property wherever practical, but they MAY omit it if necessary (for example, if calculating the value is expensive, servers might choose to only implement it in encrypted rooms). The property is not normally populated in events pushed to application services via the application service transaction API (where there is no clear definition of “requesting user”).

Added in v1.11

prev_content EventContent

The previous content for this event. This field is generated by the local homeserver, and is only returned if the event is a state event, and the client has permission to see the previous content.

Changed in v1.2: Previously, this field was specified at the top level of returned events rather than in unsigned (with the exception of the GET .../notifications endpoint), though in practice no known server implementations honoured this.

redacted_because ClientEvent

The event that redacted this event, if any.

transaction_id string

The client-supplied transaction ID, for example, provided via PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}, if the client being given the event is the same one which sent it.

{
  "chunk": [
    {
      "content": {
        "body": "This is an example text message",
        "format": "org.matrix.custom.html",
        "formatted_body": "<b>This is an example text message</b>",
        "msgtype": "m.text"
      },
      "event_id": "$143273582443PhrSn:example.org",
      "origin_server_ts": 1432735824653,
      "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
      "sender": "@example:example.org",
      "type": "m.room.message",
      "unsigned": {
        "age": 1234,
        "membership": "join"
      }
    }
  ],
  "next_batch": "next_batch_token"
}

400 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_INVALID_PARAM",
  "error": "Unknown pagination token"
}

403 response

Error
Name Type Description
errcode string

Required: An error code.

error string

A human-readable error message.

{
  "errcode": "M_FORBIDDEN",
  "error": "You are not allowed to view this room."
}

429 response

RateLimitError
Name Type Description
errcode string

Required: The M_LIMIT_EXCEEDED error code

error string

A human-readable error message.

retry_after_ms integer

The amount of time in milliseconds the client should wait before trying the request again.

{
  "errcode": "M_LIMIT_EXCEEDED",
  "error": "Too many requests",
  "retry_after_ms": 2000
}

Reference relations

[Added in v1.5]

Generically referencing another event can be done with a rel_type of m.reference as a form of relationship. There is no implied meaning behind the reference, and is usually context-dependent. One example is the key verification framework which uses reference relations to associate distinct events with a specific verification attempt.

Clients which wish to use threads or replies are expected to use other relationship types than references. References are typically used to associate data rather than messages.

Server behaviour

Server-side aggregation of m.reference

The aggregation format of m.reference relations consists of a single chunk property, which lists all the events which m.reference the event (the parent). Currently, only a single event_id field is present on the events in the chunk.

For example, given an event with the following m.reference relationship:

{
  "content": {
    "m.relates_to": {
      "rel_type": "m.reference",
      "event_id": "$another_event"
    }
    // other content fields as required
  }
  // other fields as required by events
}

The aggregation would appear similar to the following:

{
  "m.reference": {
    "chunk": [
      { "event_id": "$one" },
      { "event_id": "$two" }
    ]
  }
}