客户端-服务器 API
客户端-服务器 API 允许客户端发送消息、控制房间以及同步会话历史。该 API 既支持不存储状态、按需从服务器延迟加载数据的轻量级客户端,也支持维护完整本地持久状态副本的重量级客户端。
API 标准
Matrix 中客户端与服务器通信的强制性基线是通过 HTTP API 交换 JSON 对象。未来可以指定更高效的传输方式,作为可选扩展。
建议使用 HTTPS 进行通信。除测试环境外,不推荐使用明文 HTTP。
客户端通过不透明的 access_token 字符串进行认证(详见 客户端认证)。
除下述例外,所有 POST 和 PUT 端点均要求客户端在请求体中提供(可能为空的)JSON 对象。对于所有带有 JSON 请求体的请求,客户端应在请求头中带上 Content-Type: application/json,但这不是强制要求。
例外包括:
POST /_matrix/media/v3/upload与PUT /_matrix/media/v3/upload/{serverName}/{mediaId},两者的请求体为上传的媒体内容。POST /_matrix/client/v3/logout以及POST /_matrix/client/v3/logout/all,请求体为空。
同理,所有端点都要求服务器返回一个 JSON 对象,除非对 内容仓库模块 中媒体下载端点返回的 200 响应。服务器必须为所有 JSON 响应添加 Content-Type: application/json 响应头。
所有请求和响应中的 JSON 数据必须使用 UTF-8 编码。
参见附录中的 Matrix API 规范约定 以及下文 Web 浏览器客户端 对服务器响应的附加要求。
标准错误响应
Matrix API 层发生的任何错误必须返回“标准错误响应”。格式如下:
{
"errcode": "<error code>",
"error": "<error message>"
}
error 字符串为易读的错误消息,通常是一句简要说明出错原因的语句。
errcode 是唯一字符串,可用于处理错误信息,例如 M_FORBIDDEN。错误码的命名空间应全部大写,后接一个下划线 _。例如自定义命名空间 com.mydomain.here 和代码 FORBIDDEN,错误码应为 COM.MYDOMAIN.HERE_FORBIDDEN。本规范定义的错误码以 M_ 开头。
某些 errcode 定义了附加的键应在错误响应对象中出现,但 error 和 errcode 键必须始终存在。
一般建议通过错误码而不是 HTTP 状态码来表达错误。当遇到 M_UNKNOWN 错误码时,客户端应优先参考 HTTP 状态码,作为更可靠的问题指示。例如,若客户端收到 M_NOT_FOUND 错误码但请求返回 400 Bad Request,则应按资源未找到处理该错误。然而,若客户端收到 M_UNKNOWN 错误码和 400 Bad Request,应认为请求无效。
常见错误码
这些错误码可以被任意 API 端点返回:
M_FORBIDDEN
禁止访问,例如无权限加入房间、登录失败。
M_UNKNOWN_TOKEN
指定的访问令牌或刷新令牌未被识别。
若 HTTP 401 响应返回时包含额外参数 soft_logout,详见 软登出说明。
M_MISSING_TOKEN
请求未指定访问令牌。
M_USER_LOCKED
账户已被锁定,暂时无法使用。
M_USER_SUSPENDED
账户已被停用,目前仅可执行有限操作。
M_BAD_JSON
请求包含合法的 JSON,但某种方式上格式不正确,例如缺少必需键、键值无效等。
M_NOT_JSON
请求未包含有效的 JSON。
M_NOT_FOUND
请求未找到资源。
M_LIMIT_EXCEEDED
短时间内请求过多。请等待一段时间后重试。参见 速率限制。
M_UNRECOGNIZED
服务器无法理解请求。若端点未实现,建议返回 404;端点已实现但使用了错误 HTTP 方法,应返回 405。
M_UNKNOWN
发生未知错误。
其他错误码
下列错误码专用于特定端点。
M_UNAUTHORIZED
请求未正确验证,通常由于登录失败。
M_USER_DEACTIVATED
请求关联的用户 ID 已停用。通常见于验证身份的端点,诸如 /login。
M_USER_IN_USE
尝试注册已被占用的用户 ID 时遇到。
M_INVALID_USERNAME
尝试注册非法用户 ID 时遇到。
M_ROOM_IN_USE
createRoom API 提供的房间别名已被占用时返回。
M_INVALID_ROOM_STATE
createRoom API 提供的初始状态无效时返回。
M_THREEPID_IN_USE
API 提供的 threepid 已被另一账号使用,无法再次使用。
M_THREEPID_NOT_FOUND
API 提供的 threepid 未找到记录,无法使用。
M_THREEPID_AUTH_FAILED
无法对第三方标识符执行认证。
M_THREEPID_DENIED
服务器不允许使用该第三方标识符。例如服务器只允许特定域名的邮箱。
M_SERVER_NOT_TRUSTED
客户端请求用到的第三方服务器(如身份服务器)不被当前服务器信任。
M_UNSUPPORTED_ROOM_VERSION
客户端请求创建房间时使用了服务器不支持的房间版本。
M_INCOMPATIBLE_ROOM_VERSION
客户端尝试加入服务器不支持版本的房间。请检查错误响应中的 room_version 属性以获取房间版本。
M_BAD_STATE
无法执行请求的状态变更,例如尝试解禁未被禁言的用户。
M_GUEST_ACCESS_FORBIDDEN
房间或资源不允许访客访问。
M_CAPTCHA_NEEDED
完成请求需要验证码。
M_CAPTCHA_INVALID
提供的验证码与预期不符。
M_MISSING_PARAM
请求缺少必需参数。
M_INVALID_PARAM
请求参数值类型错误。例如,服务器期望整数却收到字符串。
M_TOO_LARGE
请求或实体体积过大。
M_EXCLUSIVE
请求的资源被应用服务保留,或发起请求的应用服务未创建该资源。
M_RESOURCE_LIMIT_EXCEEDED
由于主服务器资源限制,无法完成请求。例如,在共享主机环境的主服务器使用了过多内存或磁盘空间。错误必须包含 admin_contact 字段为用户提供联系方式。通常该错误会出现在尝试修改状态的路由(如发送消息、账户数据)而不是只读路由上(如 /sync,/user/{userId}/account_data/{type} 等)。
M_CANNOT_LEAVE_SERVER_NOTICE_ROOM
用户无法拒绝加入服务器通知房间的邀请。详见 服务器通知 模块。
M_THREEPID_MEDIUM_NOT_SUPPORTED
主服务器不支持添加指定 medium 的第三方标识符。
M_THREEPID_IN_USE
客户端指定的第三方标识符不可接受,因为已被使用。
速率限制
主服务器应当实现速率限制以降低被过载风险。当请求因速率限制被拒绝时,应返回标准错误响应格式如下:
{
"errcode": "M_LIMIT_EXCEEDED",
"error": "string",
"retry_after_ms": integer (可选,已弃用)
}
主服务器应为所有 429 状态码的响应包含 Retry-After 响应头。
retry_after_ms 属性可告知客户端需等待多少毫秒后再试。该属性已弃用,应优先采用 Retry-After 响应头。
[Changed in v1.10]:retry_after_ms 属性已弃用,请使用 Retry-After 响应头。
事务标识符
客户端-服务器 API 通常使用 HTTP PUT,在 HTTP 路径中带上传递由客户端生成的事务标识符(Transaction ID)。
事务 ID 的目的在于帮助主服务器区分新请求与前次请求的重发,实现请求幂等性。
事务 ID 仅用于此目的。
请求完成后,客户端应更换下一次请求的 {txnId}。具体生成方式由实现自定,推荐使用 version 4 UUID 或当前时间戳与单调递增整数的拼接。
主服务器应当认定若事务 ID 与以前请求一致且 HTTP 请求路径一致,则为重发。
识别为重发时,主服务器应返回与原始请求相同的 HTTP 响应码和内容。例如 PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId} 会以 200 OK 和原始请求响应体中的 event_id 返回。
事务 ID 的作用范围仅限于单一设备和单一 HTTP 端点。换言之,同一设备可以用相同的事务 ID 向 PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId} 和 PUT /_matrix/client/v3/sendToDevice/{eventType}/{txnId} 请求,两者被视为互不相关端点。同样地,客户端在两次登录之间,用相同的事务 ID 进行两次请求也算不同请求(除非登录时指定了已存在的 device_id)。但如果在刷新访问令牌后,对同一端点重复使用事务 ID,则视为重复请求并予以忽略。参见 访问令牌与设备的关系。
部分 API 端点允许或要求使用无需事务 ID 的 POST 请求。可选情况下,强烈建议使用 PUT。
v1.7 之前,事务 ID 的作用范围为“客户端会话”,而非设备。
Web 浏览器客户端
实际情况会有部分客户端以 web 浏览器或类似环境运行。此时主服务器应当响应预检请求,并在所有请求上提供跨域资源共享(CORS)头部。
服务器必须预期客户端将向其发送 OPTIONS 请求,以便客户端获取 CORS 头。本规范中的所有端点均支持 OPTIONS 方法,但服务器收到 OPTIONS 请求时不得执行端点定义的任何业务逻辑。
当客户端访问服务器发起请求,服务器应返回该路由的 CORS 头。推荐所有请求返回的 CORS 头为:
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] Access-Control-Allow-Methods 响应头的推荐值仅覆盖规范现有端点。服务器如支持额外端点或方法,应相应添加。
此章节将在端点新增方法时同步更新。未来可能支持的如 PATCH 和 HEAD。
服务器发现
为便于用户无需明确指定主服务器 URL 或其它参数即可连接到 Matrix 服务器,客户端应在登录时通过自动发现机制根据用户的 Matrix ID 确定服务器 URL。
本节下列术语含义如下:
PROMPT
以适合当前客户端用户体验的方式从用户获取特定信息(客户端如有合适交互能力),如无法良好交互则可选择放弃。
IGNORE
停止当前自动发现机制。如无可用自动发现机制,则客户端可用其它方式获取所需参数(如提示用户或使用默认值)。
FAIL_PROMPT
提示用户自动发现因数据无效/为空而失败,并要求提供相应参数。
FAIL_ERROR
通知用户自动发现未返回可用 URL,当前登录流程终止,此时已获取合法数据但无服务器可用,不应再尝试其他可能性,用户需自主决定下一步。
Well-known URI
托管 .well-known JSON 文件的服务器应提供 CORS 头,参见 CORS。
.well-known 方法指在预定位置提供 JSON 文件以指定参数值。流程如下:
- 按 服务器名 规则,从用户 Matrix ID 截取第一个冒号后片段。
- 按 语法 从服务器名提取主机名。
- 以 GET 请求
https://hostname/.well-known/matrix/client。- 返回状态码 404 时处理为
IGNORE。 - 状态码非 200 或响应体为空,处理为
FAIL_PROMPT。 - 解析响应体为 JSON 对象,如失败则
FAIL_PROMPT。 - 从
m.homeserver属性提取base_url,该值作为主服务器基础 URL。未提供则FAIL_PROMPT。 - 验证主服务器基础 URL:
- 按 URL 解析,如错误则
FAIL_ERROR。 - 客户端应连接
/_matrix/client/versions端点,确保无错误并验证响应符合预期格式。如任一步失败,处理为FAIL_ERROR。这步仅用于排除配置错误,确认地址有效。 - 注意
base_url可能带/结尾,调用方需兼容两种。
- 按 URL 解析,如错误则
- 若存在
m.identity_server,从中提取base_url用作身份服务器基础 URL。其验证与上面主服务器 URL 验证一致,连接端点为/_matrix/identity/v2。如果m.identity_server存在但无base_url,则FAIL_PROMPT。
- 返回状态码 404 时处理为
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
| 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. |
| Name | Type | Description |
|---|---|---|
base_url |
URI |
Required: The base URL for the homeserver for client-server connections. |
| 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 |
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 |
| Name | Type | Description |
|---|---|---|
email_address |
Email Address |
An email address to reach the administrator. At least one of |
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 |
role |
string |
Required: An informal description of what the contact methods are used for.
Unspecified roles are permitted through the use of Namespaced Identifiers. One of: |
{
"contacts": [
{
"email_address": "admin@example.org",
"matrix_id": "@admin:example.org",
"role": "m.role.admin"
},
{
"email_address": "security@example.org",
"role": "m.role.security"
}
],
"support_page": "https://example.org/support.html"
}
客户端认证
多数 API 端点要求用户通过访问令牌(access token)认证身份。访问令牌通常通过 登录 或 注册 流程获得,令牌可能失效;可用刷新令牌重新生成。
本规范不强制规定访问令牌格式。客户端应将其视为不透明字节序列。服务器可自由选择合适的格式。服务器实现者可参考macaroons。
使用访问令牌
可通过请求头及 Authentication Bearer 方案提供访问令牌:Authorization: Bearer TheTokenHere。
也可通过查询字符串参数 access_token=TheTokenHere 提供,但因易被日志泄露,已弃用,客户端不应使用。
主服务器必须同时支持两种方式。
[Changed in v1.11]
以查询字符串方式传递访问令牌现已弃用。
如缺少或非法凭据,请求返回 401 状态及错误码,分别为 M_MISSING_TOKEN 或 M_UNKNOWN_TOKEN。注意,M_UNKNOWN_TOKEN 可能有以下四种情况:
当客户端收到 M_UNKNOWN_TOKEN 错误码,应:
- 如有刷新令牌,尝试刷新令牌;
- 若响应中
soft_logout为true,可提示用户重新登录并保留客户端已持久化的信息; - 否则,视为用户已登出。
访问令牌与设备关系
客户端设备与访问令牌及刷新令牌密切相关。Matrix 服务器应记录每个访问令牌与刷新令牌所绑定设备,以保证请求正确处理。用刷新令牌生成新访问/刷新令牌时,新一对令牌归属于原刷新令牌关联设备。
默认情况下,登录与注册流程会自动生成新 device_id。客户端也可自定义 device_id 或(用户不变时)复用设备,并在请求体中带上 device_id;若客户端传入 device_id,服务器会使该设备此前的访问及刷新令牌失效。
刷新访问令牌
[Added in v1.3]
访问令牌可能在一定时限后过期。使用过期令牌发起 HTTP 调用会返回 M_UNKNOWN_TOKEN 错误码,且建议带有 soft_logout: true。客户端收到此错误且持有刷新令牌应调用 /refresh 刷新令牌。即使尚未过期也可主动刷新。刷新成功后应使用新令牌发起后续请求,并可用新令牌重试先前失败请求。返回新刷新令牌时,旧刷新令牌即失效,后续刷新需用新令牌。
旧刷新令牌在新访问/刷新令牌使用前仍有效,之后即被吊销,确保客户端若未取到或保存新令牌还能重复刷新。
若令牌刷新失败且错误响应带有 soft_logout: true,可视为软登出,尝试重新登录获取新访问令牌。否则,客户端应视为用户已登出。
不支持刷新令牌的客户端行为由主服务器决定;客户端通过在 /login 与 /register 请求体声明 refresh_token: true 表明支持刷新令牌。例如主服务器可允许使用永不过期的访问令牌,或依赖客户端软登出行为。
软登出
若服务器要求重新认证但不希望立即失效当前客户端会话,可通过在 M_UNKNOWN_TOKEN 错误响应中包含 soft_logout: true 标志进行软登出;soft_logout 默认为 false。若 soft_logout 被省略或为 false,则服务器已销毁会话,客户端不得重复利用;任何客户端持久信息如加密密钥和设备信息等须丢弃。若 soft_logout 为 true,客户端可复用持久状态。
[Changed in v1.3] 客户端收到软登出可优先刷新访问令牌(如有刷新令牌)。如无刷新令牌或刷新失败且为软登出,可借登录 API 指定已有设备 ID 获取新访问令牌。
[Changed in v1.12] 若响应同时携带 M_USER_LOCKED 错误码,则直到账户解锁前无法获取新的访问令牌。
用户交互式认证 API
总览
部分 API 端点需互动式认证(User-Interactive Authentication)。主服务器可提供多种认证方式,如用户名密码、单点登录(SSO)等。本规范不限制主服务器背后授权方式,仅定义标准接口以使任意客户端可登录任何主服务器。
认证过程由一系列“阶段”组成,每个阶段客户端向服务器提交一组认证数据并等待响应,响应可能为最终成功或要求下一阶段,交互继续至最终成功。
服务器为端点提供一种或多种“流”供客户端完成认证,每种流由一组有序阶段组成。客户端可自由选择认证流,但阶段必须依次完成。未按规定流程完成应返回 401。如所有阶段完成,认证即告完成,API 调用正常返回。
REST API 中的用户交互式认证
如本文档所述 REST API,认证流程由客户端与服务器之间交换 JSON 字典实现。服务器通过 401 响应体说明所需认证数据,客户端将其置于请求参数 auth 中提交。
客户端首次请求勿带 auth 参数。主服务器返回 401 状态码及如下 JSON 体:
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"
}
flows 外,该对象包含:
params: 提供客户端使用所列认证类型所需补充信息。例如某认证类型作为键存储于此字典。举例 OAuth 客户端 ID 可放此信息。session: 服务器颁发的会话标识符,客户后续认证时需返回。
客户端选定流并尝试完成首个阶段。向服务器重复发送原请求,新增 auth 字段,内容为类型名(type),会话标识符(session),以及类型相关的其它认证信息。例如阶段类型为 example.type.foo 时:
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"
}
}
如服务器认证成功但仍需更多阶段,则返回 401、响应结构与首次 401 相同,另加 completed 列表指示已完成的认证类型:
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"
}
如某阶段需多次请求才能完成,则响应同未认证,仅追加认证类型自定义内容。
若服务器认为当前阶段尝试失败,并允许重试,则返回同上,只是多了标准错误字段 errcode 和 error。例如:
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"
}
如请求失败原因为认证以外的其他原因,则依标准格式返回错误消息。例如:
HTTP/1.1 400 Bad request
Content-Type: application/json
{
"errcode": "M_EXAMPLE_ERROR",
"error": "Something was wrong"
}
如已完成某流程全部阶段,主服务器执行业务正常返回。已完成的阶段无法重试,服务器必须返回 401 响应及已完成阶段,或如阶段都已完成则返回业务结果。
某些认证类型可由 Matrix 客户端以外方式完成,例如邮箱确认可通过点击邮件链接。此时,客户端带 session 会话重试,服务器响应与正常认证一致,依类型是否在‘completed’数组确定阶段是否完成。
使用交互式认证的端点,任何无认证数据的请求均不会成功。服务器如允许无认证完成请求应提供仅含 m.login.dummy 的阶段,但必须对无认证请求返回 401。
示例
一个包含三阶段认证流的 API 调用流程如下:
_______________________
| Stage 0 |
| No auth |
| ___________________ |
| |_Request_1_________| | <-- 返回“session”键以供后续认证。
|_______________________|
|
|
_________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_________| | <-- 正常返回 API 结果
|_______________________|
认证类型
本规范定义以下认证类型:
m.login.passwordm.login.recaptcham.login.ssom.login.email.identitym.login.msisdnm.login.dummym.login.registration_token
基于密码
| 类型 | 描述 |
|---|---|
m.login.password |
客户端提交标识符和明文密码。 |
用此方式时,auth 内容如下:
{
"type": "m.login.password",
"identifier": {
...
},
"password": "<password>",
"session": "<session ID>"
}
identifier 属性为用户标识对象,详见 标识符类型。
如用 Matrix ID 登录:
{
"type": "m.login.password",
"identifier": {
"type": "m.id.user",
"user": "<user_id 或用户本地名>"
},
"password": "<password>",
"session": "<session ID>"
}
也可用 /account/3pid 绑定的 3PID 替代 user:
{
"type": "m.login.password",
"identifier": {
"type": "m.id.thirdparty",
"medium": "<第三方标识符 medium>",
"address": "<用户 third-party 地址>"
},
"password": "<password>",
"session": "<session ID>"
}
如主服务器不识别所提供的 3PID,应响应 403 Forbidden。
Google ReCaptcha
| 类型 | 描述 |
|---|---|
m.login.recaptcha |
用户完成 Google ReCaptcha 2.0 验证。 |
用法如下:
{
"type": "m.login.recaptcha",
"response": "<captcha response>",
"session": "<session ID>"
}
单点登录(SSO)
| 类型 | 描述 |
|---|---|
m.login.sso |
通过外部单点登录提供商认证。 |
客户端用 SSO 完成认证时应采用 Fallback 机制,详见交互式认证中的 SSO。
基于邮箱(身份/主服务器)
| 类型 | 描述 |
|---|---|
m.login.email.identity |
通过身份服务器(或支持的主服务器)认证邮箱。 |
用前需在身份服务器(或主服务器)完成邮箱认证。认证后需将 session 信息提交给主服务器。
示例:
{
"type": "m.login.email.identity",
"threepid_creds": {
"sid": "<身份服务器 session id>",
"client_secret": "<身份服务器客户端密钥>",
"id_server": "<认证身份服务器地址,例如 'matrix.org:8090'>",
"id_access_token": "<以前注册身份服务器的 access token>"
},
"session": "<session ID>"
}
如 /requestToken 时未包含 id_server 等,可省略该字段。
基于手机号/MSISDN(身份/主服务器)
| 类型 | 描述 |
|---|---|
m.login.msisdn |
通过身份服务器(或主服务器)认证手机号。 |
用前需在身份服务器(或主服务器)完成手机号认证,后续将 session 信息提交主服务器。
示例:
{
"type": "m.login.msisdn",
"threepid_creds": {
"sid": "<身份服务器 session id>",
"client_secret": "<身份服务器客户端密钥>",
"id_server": "<认证身份服务器地址,例如 'matrix.org:8090'>",
"id_access_token": "<以前注册身份服务器的 access token>"
},
"session": "<session ID>"
}
如 /requestToken 无 id_server,可省略该字段。
Dummy 认证
| 类型 | 描述 |
|---|---|
m.login.dummy |
Dummy 认证始终成功且无需额外参数。 |
Dummy 认证允许服务器无需任何用户交互即可完成请求,也可区分流程(例如存在子集包含关系的两个认证流,可用 Dummy 区分)。用时仅需传 type、session:
{
"type": "m.login.dummy",
"session": "<session ID>"
}
令牌注册
[Added in v1.2]
| 类型 | 描述 |
|---|---|
m.login.registration_token |
用预共享令牌认证注册账号。 |
m.login.registration_token 仅适用于 /register 端点。
此类型允许主服务器仅向有限用户开放注册(非完全开放/关闭),需带最大长度 64 的不透明标识符(参见 Opaque Identifier)。服务器可任意定义令牌数量与有效期,如:限定 100 次、2 小时等,过期作废。
用法如下:
{
"type": "m.login.registration_token",
"token": "fBVFdqVE",
"session": "<session ID>"
}
如需确认令牌有效性可用下面的 /validity API,但此 API 不保障使用时令牌一定有效。
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
| 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
| 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
| 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
}
注册时服务条款
[Added in v1.11]
| 类型 | 描述 |
|---|---|
m.login.terms |
用户需接受服务条款、隐私政策等政策性文档。 |
m.login.terms 仅适用于 /register 端点。
主服务器要求新用户接受服务条款等政策性文件。文档可能有多类型、多版本、多语言。
服务器需通过 /register 返回 401,并在 flows 中包含 m.login.terms,params 内有结构见下文。
客户端遇到无效参数应终止注册并提示用户错误。
客户端应为每个政策提供勾选框及跳转链接,用户接受后提交仅含 type、session 的 auth 字段:
{
"type": "m.login.terms",
"session": "<session ID>"
}
服务器应记录注册过程展现的文档版本。
示例
-
客户端注册请求为:
POST /_matrix/client/v3/register{ "username": "cheeky_monkey", "password": "ilovebananas" } -
服务器要求接受条款,返回:
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" } -
客户端将文档列表展现给用户并提示接受。
-
用户确认全部接受后,客户端重复注册请求:
POST /_matrix/client/v3/register{ "username": "cheeky_monkey", "password": "ilovebananas", "auth": { "type": "m.login.terms", "session": "kasgjaelkgj" } } -
所有认证步骤均已完成,请求成功:
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
m.login.terms params
Schema for m.login.terms entry in the params object in a User-Interactive Authentication response.
| 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. |
| 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, |
| 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 |
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
客户端无法预期支持所有认证类型。如遇不识别的认证类型可引导用户于浏览器打开备用页面,完成此阶段。URL 格式:
/_matrix/client/v3/auth/<auth type>/fallback/web?session=<session ID>
其中 auth type 为类型名,session ID 为会话标识。
回调页面必须返回 HTML,可以完成该认证并用如下 JS 通知客户端:
if (window.onAuthDone) {
window.onAuthDone();
} else if (window.opener && window.opener.postMessage) {
window.opener.postMessage("authDone", "*");
}
这样客户端可在内嵌浏览器用 onAuthDone 回调,或用 HTML5 跨文档消息 API。
收到通知后客户端应以仅含 session ID 的 auth 字段重发请求:
{
"session": "<session ID>"
}
示例
网页客户端可用如下 JS 打开处理未知认证类型的弹窗:
/**
* 参数:
* homeserverUrl: 主服务器基础地址 (如 "https://matrix.org")
* apiEndpoint: 正在使用的 API 端点 (如 "/_matrix/client/v3/account/password")
* loginType: 正在认证的类型 (如 "m.login.recaptcha")
* sessionID: 主服务器颁发的会话标识符
* onComplete: 认证完成的回调,参数为请求响应内容
*/
function unknownLoginType(homeserverUrl, apiEndpoint, loginType, sessionID, onComplete) {
var popupWindow;
var eventListener = function(ev) {
// 检查消息和来源
if (ev.data !== "authDone" || ev.origin !== homeserverUrl) {
return;
}
// 关闭弹窗
popupWindow.close();
window.removeEventListener("message", eventListener);
// 重发请求
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)标识用户。用户标识对象含 type 字段指明类型,根据类型含有不同的必需字段。
本规范定义如下标识符类型:
m.id.userm.id.thirdpartym.id.phone
Matrix 用户 ID
| 类型 | 描述 |
|---|---|
m.id.user |
以 Matrix ID 标识用户。 |
通过 Matrix ID 标识用户,既可以是全限定 ID,也可以是本地用户名。
"identifier": {
"type": "m.id.user",
"user": "<user_id 或本地用户名>"
}
第三方 ID
| 类型 | 描述 |
|---|---|
m.id.thirdparty |
以规范化第三方标识符标识用户。 |
通过 3PID 标识用户,3PID 绑定见 /account/3pid,medium 列表见 3PID 类型。
"identifier": {
"type": "m.id.thirdparty",
"medium": "<第三方标识符类型>",
"address": "<用户第三方标识符地址>"
}
手机号
| 类型 | 描述 |
|---|---|
m.id.phone |
以手机号标识用户。 |
以绑定手机号标识用户,手机号可由用户原样输入,由主服务器规范化。如需客户端自规范,可用 m.id.thirdparty 类型并设置 medium: msisdn。
"identifier": {
"type": "m.id.phone",
"country": "<手机号归属国家>",
"phone": "<手机号>"
}
country 是两位大写 ISO-3166-1 alpha-2 国家码,phone 按该区域拨号规范解析。
登录
客户端可用 /login API 获取访问令牌。
该端点 当前不 使用 用户交互式认证 API。
用户名/密码登录示例:
{
"type": "m.login.password",
"identifier": {
"type": "m.id.user",
"user": "<user_id 或本地用户名>"
},
"password": "<password>"
}
也可用 /account/3pid 绑定的 3PID:
{
"type": "m.login.password",
"identifier": {
"medium": "<第三方标识符类型>",
"address": "<用户规范化的第三方标识符地址>"
},
"password": "<password>"
}
主服务器不识别 3PID 时返回 403 Forbidden。
用登录令牌登录的方法如下:
{
"type": "m.login.token",
"token": "<login token>"
}
token 必须编码用户 ID(因请求中无其它身份信息)。如令牌无效,则返回 403 Forbidden 和错误码 M_FORBIDDEN。
若主服务器声明支持 m.login.sso 流,且客户端支持,客户端应重定向用户至 通过 SSO 客户端登录 的 /redirect 端点。认证完成后,需要匹配 m.login.token 类型发送 /login 请求。
[Added in v1.7] 已认证客户端如主服务器支持可通过 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 |
| Name | Type | Description |
|---|---|---|
get_login_token |
boolean |
If Added in |
type |
string |
Required: The login type. This is supplied as the |
{
"flows": [
{
"type": "m.login.password"
},
{
"get_login_token": true,
"type": "m.login.token"
}
]
}
429 response
| 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 |
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 |
medium |
string |
When logging in using a third-party identifier, the medium of the identifier. Must be ’email’. Deprecated in favour of |
password |
string |
Required when |
refresh_token |
boolean |
If true, the client supports refresh tokens. Added in |
token |
string |
Required when |
type |
string |
Required: The login type being used. This must be a type returned in one of the flows of the
response of the |
user |
string |
The fully qualified user ID or just local part of the user ID, to log in. Deprecated in favour of |
| 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:
|
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 |
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
|
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
Added in |
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. |
| 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. |
| Name | Type | Description |
|---|---|---|
base_url |
URI |
Required: The base URL for the homeserver for client-server connections. |
| 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
| 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
| Name | Type | Description |
|---|---|---|
errcode |
string |
Required: An error code. |
error |
string |
A human-readable error message. |
{
"errcode": "M_FORBIDDEN"
}
429 response
| 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. |
| 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 |
| <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
|
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. |
login_token |
string |
Required: The login token for the |
{
"expires_in_ms": 120000,
"login_token": "<opaque string>"
}
400 response
| 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
| 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. |
| 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
| 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
| 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
| 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
{}
应用服务登录
[Added in v1.2]
应用服务可通过提供有效的应用服务令牌及其命名空间下的用户进行登录。
应用服务不一定需要以单独用户身份登录,可通过 身份断言 用应用服务令牌操作。但如需针对用户的令牌,可用下面 API。
该请求需通过应用服务 as_token认证(详见客户端认证令牌用法)。
使用方式如下:
{
"type": "m.login.application_service",
"identifier": {
"type": "m.id.user",
"user": "<user_id 或本地用户名>"
}
}
如访问令牌无效、不属于应用服务,或该用户未注册,则主服务器返回错误码 M_FORBIDDEN。
如 token 属于应用服务但用户 ID 不在其命名空间,返回错误码 M_EXCLUSIVE。
登录 Fallback
客户端若无法识别登录流程,可用以下 fallback 登录 API:
GET /_matrix/static/client/login/
该端点返回含 JS 的 HTML 页面,可完整执行登录流程。登录成功后页面会调用 window.matrixLogin.onLogin(response),参数为 POST /_matrix/client/v3/login 的 JSON 响应体解析所得的 JS 对象。
[Added in v1.1] 可将非认证参数作为查询字符串提供至本端点,会在登录流程转发至实际登录 API。例如:
GET /_matrix/static/client/login/?device_id=GHTYAJCE
账户注册与管理
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 Added in |
id_server |
string |
The identity server to unbind all of the user’s 3PIDs from.
If not provided, the homeserver MUST use the |
| 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 |
| <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). One of: |
{
"id_server_unbind_result": "success"
}
401 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. |
| 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
| 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 |
new_password |
string |
Required: The new password for the account. |
| 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 |
| <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
| 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. |
| 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
| 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
|
email |
Email Address |
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 |
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 |
next_link |
URI |
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 |
Request body example
{
"client_secret": "monkeys_are_GREAT",
"email": "alice@example.org",
"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
| Name | Type | Description |
|---|---|---|
sid |
string |
Required: The session ID. Session IDs are opaque strings that must consist entirely
of the characters |
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 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 |
{
"sid": "123abc",
"submit_url": "https://example.org/path/to/submitToken"
}
400 response
| 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
| 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
|
country |
string |
Required: The two-letter uppercase ISO-3166-1 alpha-2 country code that the
number in |
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 |
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 |
next_link |
URI |
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 |
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
| Name | Type | Description |
|---|---|---|
sid |
string |
Required: The session ID. Session IDs are opaque strings that must consist entirely
of the characters |
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 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 |
{
"sid": "123abc",
"submit_url": "https://example.org/path/to/submitToken"
}
400 response
| 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
| 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:
-
useraccounts. These accounts may use the full API described in this specification. -
guestaccounts. 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
| Name | Type | Description |
|---|---|---|
kind |
string |
The kind of account to register. Defaults to One of: |
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 |
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 |
initial_device_display_name |
string |
A display name to assign to the newly-created device. Ignored
if |
password |
string |
The desired password for the account. |
refresh_token |
boolean |
If true, the client supports refresh tokens. Added in |
username |
string |
The basis for the localpart of the desired Matrix ID. If omitted, the homeserver MUST generate a Matrix ID local part. |
| 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 |
| <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:
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 |
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 |
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 Added in |
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
|
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
Omitted if the Added in |
user_id |
User ID |
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
| 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
| 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. |
| 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
| 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
| 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
| 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:
|
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 |
{
"available": true
}
400 response
| 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
| 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
|
email |
Email Address |
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 |
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 |
next_link |
URI |
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 |
Request body example
{
"client_secret": "monkeys_are_GREAT",
"email": "alice@example.org",
"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:
|
403 |
The homeserver does not permit the address to be bound. |
200 response
| Name | Type | Description |
|---|---|---|
sid |
string |
Required: The session ID. Session IDs are opaque strings that must consist entirely
of the characters |
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 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 |
{
"sid": "123abc",
"submit_url": "https://example.org/path/to/submitToken"
}
400 response
| 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
| 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
|
country |
string |
Required: The two-letter uppercase ISO-3166-1 alpha-2 country code that the
number in |
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 |
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 |
next_link |
URI |
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 |
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:
|
403 |
The homeserver does not permit the address to be bound. |
200 response
| Name | Type | Description |
|---|---|---|
sid |
string |
Required: The session ID. Session IDs are opaque strings that must consist entirely
of the characters |
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 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 |
{
"sid": "123abc",
"submit_url": "https://example.org/path/to/submitToken"
}
400 response
| 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
| 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"
}
密码管理注意事项
客户端应强制密码的足够复杂性,建议至少包含小写字母、大写字母、数字和符号,最少 8 位。服务器可拒绝弱密码,并返回 M_WEAK_PASSWORD 错误码。
账户锁定
[Added in v1.12]
服务器管理员可对帐号加锁(如出于安全原因),锁定行为非破坏性,可后续解锁。下述定义了客户端与服务器的行为,具体加锁/解锁方式为实现细节。
账户锁定时,服务器必须对除下列 API 外的所有客户端-服务器 API 返回 401 错误、错误码 M_USER_LOCKED 并设置 soft_logout:
服务器可在 error 字段告知锁定原因。
HTTP/1.1 401 Unauthorized
Content-Type: application/json
{
"errcode": "M_USER_LOCKED",
"error": "本账户已被锁定",
"soft_logout": true
}
服务器不应在账户锁定时立即吊销访问令牌,除非客户端主动调用上述登出端点。这样可以确保账户解锁后用户无需重新登录。
收到 M_USER_LOCKED 错误时,客户端应保留会话加密等信息,并提示用户帐户已被锁定。锁定期间应隐藏正常 UI 禁止使用账户,但应以限流方式持续轮询 /sync 等接口以检测解锁。
如需申诉可使用服务器联系方式发现。
账户停用
[Added in v1.13]
服务器管理员可停用用户账户以防止进一步操作。其效果类似锁定,但不会导致客户端丢失会话状态。停用可逆,不同于注销账号。
可用操作范围为服务器实现细节,但建议至少允许:
- 登录、新建会话(即使也处于停用态)
- 查看和接收消息,特别是通过
/sync与/messages - 验证其它设备与写交叉签名数据
- 上传密钥备份
- 离开房间与拒绝邀请
- 撤回自己发送的消息
- 注销或删除自己任意设备
- 停用账户,可设置延时防止频繁新注册
- 添加或更改管理员联系信息,但不允许移除(建议服务器记录变更日志)
一般请求如 /send/{eventType} 可根据请求参数决定是否允许,如允许发送撤回事件但不允许普通消息。
如房间用作管理员与被停用用户沟通通道,建议允许用户在该房间中发言。不想让其接收通知的管理员可考虑锁定账户。
其它被禁止的建议操作:
- 加入或敲门进入房间
- 接受或发送邀请
- 向房间发送消息
- 修改资料
- 撤回他人消息(如房间权限允许)
如客户端试图在停用时操作,服务器必须以 403 Forbidden、错误码 M_USER_SUSPENDED 响应:
HTTP/1.1 403 Forbidden
Content-Type: application/json
{
"errcode": "M_USER_SUSPENDED",
"error": "您在账号停用期间无法执行此操作。"
}
有关停用/取消停用 API 暂未纳入本规范,属实现细节。
添加账户管理员联系信息
主服务器可独立保存一些用于管理的联系信息。此信息独立于身份服务器,可以绑定至身份服务器。
本节涉及“添加”“绑定”概念。添加(或移除)指未绑定至身份服务器的标识符;绑定(或解绑)指服务端绑定于身份服务器。取决于上下文,标识符可同时添加和绑定。
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] |
| 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: |
validated_at |
integer |
Required: The timestamp, in milliseconds, when the identifier was validated by the identity server. |
{
"threepids": [
{
"added_at": 1535336848756,
"address": "monkey@banana.island",
"medium": "email",
"validated_at": 1535176800000
}
]
}
POST
/_matrix/client/v3/account/3pid
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. |
| 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 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 |
{
"submit_url": "https://example.org/path/to/submitToken"
}
403 response
| 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. |
| 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 |
| <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
| 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. |
| 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
| 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
| 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 |
medium |
string |
Required: The medium of the third-party identifier being removed. One of: |
Request body example
{
"address": "example@example.org",
"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. One of: |
{
"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
|
email |
Email Address |
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 |
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 |
next_link |
URI |
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 |
Request body example
{
"client_secret": "monkeys_are_GREAT",
"email": "alice@example.org",
"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:
|
403 |
The homeserver does not allow the third-party identifier as a contact option. |
200 response
| Name | Type | Description |
|---|---|---|
sid |
string |
Required: The session ID. Session IDs are opaque strings that must consist entirely
of the characters |
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 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 |
{
"sid": "123abc",
"submit_url": "https://example.org/path/to/submitToken"
}
400 response
| 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
| 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
|
country |
string |
Required: The two-letter uppercase ISO-3166-1 alpha-2 country code that the
number in |
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 |
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 |
next_link |
URI |
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 |
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:
|
403 |
The homeserver does not allow the third-party identifier as a contact option. |
200 response
| Name | Type | Description |
|---|---|---|
sid |
string |
Required: The session ID. Session IDs are opaque strings that must consist entirely
of the characters |
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 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 |
{
"sid": "123abc",
"submit_url": "https://example.org/path/to/submitToken"
}
400 response
| 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
| 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 |
medium |
string |
Required: The medium of the third-party identifier being removed. One of: |
Request body example
{
"address": "example@example.org",
"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. One of: |
{
"id_server_unbind_result": "success"
}
当前账户信息
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 |
is_guest |
boolean |
When Added in |
user_id |
User ID |
Required: The user ID that owns the access token. |
{
"device_id": "ABC1234",
"user_id": "@joe:example.org"
}
401 response
| 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
| 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
| 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
}
关于身份服务器的说明
Matrix 身份服务器存储用户第三方标识符(如邮箱、手机)与用户 ID 的绑定。用户确定身份服务器后,所有客户端都应使用该身份服务器。
客户端可通过账户数据事件 m.identity_server 得知用户所选身份服务器(见本章下文)。客户端应在检测到该数据(已/未出现)前不要请求身份服务器,如有 base_url 字段则使用该身份服务器,否则用默认值,且不应在为用户设置身份服务器前将默认值写入账户数据。
客户端应监听 m.identity_server 事件变更,并同步其使用的身份服务器。
如客户端允许用户设置身份服务器,必须更新 m.identity_server。base_url 为 null 时应视为不希望启用身份服务器,相关功能禁用。
客户端不应为缺少该事件的用户迁移补默认身份服务器,除非用户主动设置。即,用户未设置相关数据时,客户端不应主动写入默认身份服务器。
m.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 |
URI|null |
The URL of the identity server the user prefers to use, or |
Examples
{
"content": {
"base_url": "https://example.org"
},
"type": "m.identity_server"
}
能力协商
主服务器可能不支持部分功能,客户端需有查询主服务器能力的机制。例如主服务器采用外部认证,无法支持密码变更。
通过此机制声明的能力旨在描述 API 内可选功能,或与用户/服务器状态相关功能。不用于实验性功能,实验性或不稳定功能应由 /versions 公布。
合理能力如:
- 是否支持用户在线状态
- 是否支持可选功能如用户目录或房间目录
- 服务器对客户端速率限制、文件类型限制
不应声明为能力的有:
- 是否支持规范中处于
unstable状态的功能 - 媒体文件大小(已由
/configAPI 管理) - 可选编码/传输方式
能力以 m. 为前缀为 Matrix 规范保留,其它命名建议采用 Java 包命名风格。具体支持能力见下。
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. |
| 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. |
| Name | Type | Description |
|---|---|---|
enabled |
boolean |
Required: True if the user can perform the action, false otherwise. |
| 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
| 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 能力
本能力含 enabled 字段,表示用户能否调用 /account/password API 修改密码。不出现时默认允许修改密码。出现且 enabled 为假时,客户端应提示用户无法修改密码。
示例:
{
"capabilities": {
"m.change_password": {
"enabled": false
}
}
}
m.room_versions 能力
描述服务器支持的默认及可用房间版本与稳定性级别。客户端可用以判定房间是否需升级。
示例:
{
"capabilities": {
"m.room_versions": {
"default": "1",
"available": {
"1": "stable",
"2": "stable",
"3": "unstable",
"custom-version": "unstable"
}
}
}
}
本能力参考房间版本。未被 available 标为 stable 的统统视为 unstable。例如 future-stable 也算 unstable。
default 为服务器新建房间采用的版本。客户端应引导有权限用户将unstable房间升级为 default 版本。
如无本能力,客户端应以 "1" 作为默认和唯一可用稳定房间版本。
m.set_displayname 能力
本能力含 enabled 字段,标识用户能否通过资料接口修改昵称。若禁用,常为目录服务(如 LDAP)映射帐号。
此能力常与 m.set_avatar_url 成对出现。
如不出现,默认允许修改昵称。
示例:
{
"capabilities": {
"m.set_displayname": {
"enabled": false
}
}
}
m.set_avatar_url 能力
本能力唯一 enabled 字段,标识用户可否通过资料接口修改头像。典型禁用场景为外部服务(如 LDAP)账号同步。
与 m.set_displayname 能力常成对。
缺省时默认可修改头像。
示例:
{
"capabilities": {
"m.set_avatar_url": {
"enabled": false
}
}
}
m.3pid_changes 能力
本能力唯一字段 enabled,标识用户是否能添加、删除或修改自己的三方账号。注意仅作用于管理员联系信息接口,不影响身份服务端点。禁用通常为目录服务类(如 LDAP)账号的自动同步。
缺省时默认允许修改。
示例:
{
"capabilities": {
"m.3pid_changes": {
"enabled": false
}
}
}
m.get_login_token 能力
本能力唯一字段 enabled,标识用户是否可通过 POST /login/get_token 生成一次性登陆令牌,用于免认证客户端登陆。
缺省时,客户端应认为用户不能生成此类令牌。
示例:
{
"capabilities": {
"m.get_login_token": {
"enabled": false
}
}
}
过滤
过滤器可在服务器端创建,作为参数传递到支持事件返回的 API 里,这会影响 API 返回的数据内容。只有部分 API 支持过滤器。
房间成员延迟加载
成员事件数量巨大时占用资源较大。为节省资源,客户端可启用“延迟加载”,服务器仅发送与客户端相关的成员事件。
延迟加载作为优化不是绝对精确的,服务器可以为简化实现而多发部分事件,但应尽量减少冗余。
在过滤器层面,通过设定
RoomEventFilter 的 lazy_load_members 开启延迟加载。启用后,支持该特性的端点仅返回事件发送方的成员事件。例如带延迟加载的 /sync 仅返回时间线内发生事件相关的发送者成员事件。
处理事件序列(如循环调用 /sync 或分页 /messages)时,事件块发送用户可能重叠。服务器可以假设客户端会保留已接收成员事件,对没变化的用户成员事件不必重发(称为“冗余成员事件”)。客户端如需总是收到全部冗余成员事件,可设 include_redundant_members 为 true。
推荐用法如下:
- 客户端初次同步
/sync启用延迟加载,仅获取当前事件的发送人成员事件。 - 如需展示房间成员列表,可调用
/members,参数?at设为/sync的 from token。房间成员列表随后跟随增量/sync维护。 - 不支持成员补全可通过查询房间状态或
/profile按需获取。
支持延迟加载的端点有:
API 端点
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
| 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
| 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: |
presence |
EventFilter |
The presence updates to include. |
room |
RoomFilter |
Filters to be applied to room data. |
| 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 |
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 |
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 |
| 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 |
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 |
[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 |
state |
RoomEventFilter |
The state events to include for rooms. |
timeline |
RoomEventFilter |
The message and state update events to include for rooms. |
| Name | Type | Description |
|---|---|---|
contains_url |
boolean |
If |
include_redundant_members |
boolean |
If |
lazy_load_members |
boolean |
If |
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 |
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 |
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 |
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 |
unread_thread_notifications |
boolean |
If Added in |
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 |
{
"filter_id": "66696p746572"
}
GET
/_matrix/client/v3/user/{userId}/filter/{filterId}
| Rate-limited: | No |
|---|---|
| Requires authentication: | Yes |
Request
Request 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
| 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: |
presence |
EventFilter |
The presence updates to include. |
room |
RoomFilter |
Filters to be applied to room data. |
| 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 |
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 |
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 |
| 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 |
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 |
[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 |
state |
RoomEventFilter |
The state events to include for rooms. |
timeline |
RoomEventFilter |
The message and state update events to include for rooms. |
| Name | Type | Description |
|---|---|---|
contains_url |
boolean |
If |
include_redundant_members |
boolean |
If |
lazy_load_members |
boolean |
If |
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 |
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 |
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 |
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 |
unread_thread_notifications |
boolean |
If Added in |
{
"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"
]
}
}
}
事件
客户端-服务器 API 披露的会话历史为事件列表。服务器将最终一致的事件图(event graph)线性化为事件流(event stream):
[E0]->[E1]->[E2]->[E3]->[E4]->[E5]
房间事件类型
房间事件分为两类:
- 状态事件:更新房间元数据(如话题、成员等),状态由事件
type与state_key组成唯一键,重复键的状态会被覆盖。 - 消息事件:描述房间内临时性操作,如发送消息、设置 VoIP 通话等。
规范定义了若干 m. 前缀事件(详见 Room Events)。应用可自定义事件类型,建议采用 Java 包命名规范如 com.example.myapp.event。
事件类型不限于本规范定义,可随意采用 Java 包命名进行自定义,只要客户端可访问所用命名空间。例如 com.example.game.score。
房间事件格式
“联邦”层事件格式依房间使用的房间版本而定,详见 版本1 与 版本3。
但一般客户端不会直接遇到此格式,主服务器会将事件转换为如下便于客户端解析的格式。
ClientEvent
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.
| 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 |
type |
string |
Required: The type of the event. |
unsigned |
UnsignedData |
Contains optional extra information about the event. |
| 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 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 |
prev_content |
EventContent |
The previous Changed in |
redacted_because |
ClientEvent |
The event that redacted this event, if any. |
transaction_id |
string |
The client-supplied transaction ID, for example, provided via
|
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)状态
精简状态为房间的简化状态视图,助用户了解房间基本信息,仅含部分精简后的状态事件。
精简状态事件仅有 sender、type、state_key、content。
使用场景:邀请、敲门及用户可能有加入权利的房间(如 restricted 房间)。
客户端仅于无房间实际状态时应使用精简状态,有数据后弃用。如客户端带房间存档而收到房间精简状态(如被踢后看到新邀请),应以精简状态为准,直到重新进房获取最新状态。
精简状态一般包含下列事件,尽量以精简状态形式呈现:
m.room.createm.room.namem.room.avatarm.room.topicm.room.join_rulesm.room.canonical_aliasm.room.encryption
客户端应遍历精简状态,不要假设某事件必然存在。服务器亦可包含文档未列出的事件。
房间名、头像、话题、别名便于用户做出加入与否。 加入规则提醒客户端为何具备加入权限,比如不同图标辅助解释。 创建事件可判定房间类型(如是否为空间等),客户端可据此在 UI 不同区域展示。 加密信息则用于图标或相关解释。
尽管精简状态由服务器生成与下发,但接收方亦可能不准确。精简状态事件未签名,有被篡改或因延迟变更未达等可能。
Stripped state event
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 |
sender |
string |
Required: The |
state_key |
string |
Required: The |
type |
string |
Required: The |
大小限制
事件经联邦层格式、规范化 JSON 编码后,整体不得超过 65536 字节。
字段单独限制如下:
sender不得超过用户 ID 最大长度。room_id不得超过房间ID最大长度。state_key最多255字节。type最多255字节。event_id不得超过事件ID最大长度。
部分事件类型还有其他约束,详见各事件说明。其余键仅受全局 64KiB 限制。
房间事件
本节内容尚在完善中。
规范现有标准事件均以 m. 开头。
m.room.canonical_alias
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 |
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
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 |
m.federate |
boolean |
Whether users on other servers can join this room. Defaults to |
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 |
type |
string |
Optional room type to denote a room’s intended function outside of traditional conversation. Unspecified room types are possible using Namespaced Identifiers. |
| 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
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 theknockjoin rule, or can attempt to join having satisfied an allow condition per therestrictedjoin 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 Added in |
join_rule |
string |
Required: The type of rules used for users wishing to join this room. One of: |
| Name | Type | Description |
|---|---|---|
room_id |
string |
Required if |
type |
string |
Required: The type of condition:
One of: |
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
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 thanban).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
| 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 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 Added in |
membership |
string |
Required: The membership state of the user. One of: |
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 |
third_party_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. |
| 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
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 |
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 |
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 |
users |
{User ID: integer} |
The power levels for specific users. This is a mapping from |
users_default |
integer |
The power level for users in the room whose Note: When there is no |
| Name | Type | Description |
|---|---|---|
room |
integer |
The level required to trigger an |
| <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"
}
}
历史性事件
部分 m. 命名空间下的事件或见于房间,但本规范暂无业务含义,如:
m.room.aliases
早期版本有详细定义。
同步
读取事件的推荐流程为:客户端首次调用 /sync(无 since 参数),获取每间房最新消息及房间在返回时间线开头时的状态。响应含 next_batch 字段,后续以此为 since 参数获取新消息。每房间附带 prev_batch 字段,可用作 /rooms/<room_id>/messages 的 start 参数查询更早记录。
举例,/sync 返回房间事件 E2~E5,略去 E0/E1:
[E0]->[E1]->[E2]->[E3]->[E4]->[E5]
^ ^
| |
prev_batch: '1-2-3' next_batch: 'a-b-c'
客户端后续带 next_batch 做 timeout 长轮询。服务器会保持连接,及时有新事件随时返回,仅 /sync(及已弃用的 /events)支持该机制。
假如下一次同步返回增量事件 E6:
[E0]->[E1]->[E2]->[E3]->[E4]->[E5]->[E6]
^ ^
| |
| next_batch: 'x-y-z'
prev_batch: 'a-b-c'
通常全部新事件都会返回。若某段时间事件过多,则大部分事件会省略,仅返回最新事件,同时返回起止区间状态差量(delta),方便客户端补全,防断层。客户端如想回补,可按返回 prev_batch 查询丢失区间事件。
例,跟进一次 /sync 带 since=x-y-z,服务器发现新事件 E7E10,但只返回 E8E10 及前置状态差量:
| 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'
客户端据此以 from=x-y-z、to=d-e-f 查询 /rooms/<room_id>/messages 补历史。
此 API 按事件到达主服务器时间排序,部分旧 API 按事件图偏序排序,二者可能出现同事件多次返回。客户端需基于事件 ID 去重。
/sync 返回 state 列表与 timeline 分离。全量同步 state 为时间线开始时刻完整状态(如新房间状态只占满时间线 state 或为零)。增量同步 state 为自 since 以来至时间线起点的变更(如时间线 limited 时)。注意 state 不一定为时间线前一刻状态,客户端不应对时间线混排该列表。
本规范早期让 state 表示时间线末端状态,不理想:一是事件重复,二是客户端难以还原。例如返回时间线 [M0, S1, M2],M0/M2 同一用户发,S1 为其改昵称事件;若 state 表示末端,客户端需手动倒回 S1 状态计算 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
| 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 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 If By default, this is |
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: |
since |
string |
A point in time to continue a sync from. This should be the
|
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 |
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 |
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. |
| Name | Type | Description |
|---|---|---|
events |
[Event] |
List of events. |
| 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’ |
| Name | Type | Description |
|---|---|---|
events |
[Event] |
List of events. |
| 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. |
| Name | Type | Description |
|---|---|---|
invite_state |
InviteState |
The stripped state of a room that the user has been invited to. |
| Name | Type | Description |
|---|---|---|
events |
[StrippedStateEvent] |
The stripped state events that form the invite state. |
| Name | Type | Description |
|---|---|---|
content |
EventContent |
Required: The |
sender |
string |
Required: The |
state_key |
string |
Required: The |
type |
string |
Required: The |
| 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 N.B. state updates for |
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 Changed in |
unread_thread_notifications |
{Event ID: ThreadNotificationCounts} |
If 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 |
| Name | Type | Description |
|---|---|---|
events |
[Event] |
List of events. |
| Name | Type | Description |
|---|---|---|
events |
[ClientEventWithoutRoomID] |
List of events. |
| 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 |
type |
string |
Required: The type of the event. |
unsigned |
UnsignedData |
Contains optional extra information about the event. |
| 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 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 |
prev_content |
EventContent |
The previous Changed in |
redacted_because |
ClientEventWithoutRoomID |
The event that redacted this event, if any. |
transaction_id |
string |
The client-supplied transaction ID, for example, provided via
|
| 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
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 |
m.invited_member_count |
integer |
The number of users with |
m.joined_member_count |
integer |
The number of users with |
| Name | Type | Description |
|---|---|---|
events |
[ClientEventWithoutRoomID] |
Required: List of events. |
limited |
boolean |
True if the number of events returned was limited by the |
prev_batch |
string |
A token that can be supplied to the |
| 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. |
| 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. |
| Name | Type | Description |
|---|---|---|
knock_state |
KnockState |
The stripped state of a room that the user has knocked upon. |
| Name | Type | Description |
|---|---|---|
events |
[StrippedStateEvent] |
The stripped state events that form the knock state. |
| 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 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
| 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 |
start |
string |
A token which correlates to the start of |
| 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 |
type |
string |
Required: The type of the event. |
unsigned |
UnsignedData |
Contains optional extra information about the event. |
| 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 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 |
prev_content |
EventContent |
The previous Changed in |
redacted_because |
ClientEvent |
The event that redacted this event, if any. |
transaction_id |
string |
The client-supplied transaction ID, for example, provided via
|
{
"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}
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
| 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
| 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 |
type |
string |
Required: The type of the event. |
unsigned |
UnsignedData |
Contains optional extra information about the event. |
| 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 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 |
prev_content |
EventContent |
The previous Changed in |
redacted_because |
ClientEvent |
The event that redacted this event, if any. |
transaction_id |
string |
The client-supplied transaction ID, for example, provided via
|
{
"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 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
| Name | Type | Description |
|---|---|---|
archived |
boolean |
Whether to include rooms that the user has left. If |
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 |
presence |
[Event] |
Required: A list of presence events. |
rooms |
[RoomInfo] |
Required: |
| 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’ |
| Name | Type | Description |
|---|---|---|
account_data |
[Event] |
The private data that this user has attached to this room. |
invite |
InviteEvent |
The invite event if |
membership |
string |
Required: The user’s membership state in this room. One of: |
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 One of: |
| 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 |
type |
string |
Required: The type of the event. |
unsigned |
UnsignedData |
Contains optional extra information about the event. |
| 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 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 |
prev_content |
EventContent |
The previous Changed in |
redacted_because |
ClientEvent |
The event that redacted this event, if any. |
transaction_id |
string |
The client-supplied transaction ID, for example, provided via
|
| 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 |
end |
string |
Required: A token which correlates to the end of |
start |
string |
A token which correlates to the start of If no earlier events are available, this property may be omitted from the response. |
| 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 |
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"
}
]
}
获取房间事件
房间事件读取相关 API 如下:
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
| 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
| 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 |
type |
string |
Required: The type of the event. |
unsigned |
UnsignedData |
Contains optional extra information about the event. |
| 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 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 |
prev_content |
EventContent |
The previous Changed in |
redacted_because |
ClientEvent |
The event that redacted this event, if any. |
transaction_id |
string |
The client-supplied transaction ID, for example, provided via
|
{
"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
| 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
| 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. |
| Name | Type | Description |
|---|---|---|
avatar_url |
URI |
The avatar of the user this object is representing, as an |
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
| Name | Type | Description |
|---|---|---|
roomId |
string |
Required: The room to get the member events for. |
| 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 |
membership |
string |
The kind of membership to filter for. Defaults to no filtering if
unspecified. When specified alongside One of: |
not_membership |
string |
The kind of membership to exclude from the results. Defaults to no filtering if unspecified. One of: |
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] |
| 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 |
type |
string |
Required: The type of the event. |
unsigned |
UnsignedData |
Contains optional extra information about the event. |
| 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 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 |
prev_content |
EventContent |
The previous Changed in |
redacted_because |
ClientEvent |
The event that redacted this event, if any. |
transaction_id |
string |
The client-supplied transaction ID, for example, provided via
|
{
"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
| 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.
| 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 |
type |
string |
Required: The type of the event. |
unsigned |
UnsignedData |
Contains optional extra information about the event. |
| 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 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 |
prev_content |
EventContent |
The previous Changed in |
redacted_because |
ClientEvent |
The event that redacted this event, if any. |
transaction_id |
string |
The client-supplied transaction ID, for example, provided via
|
[
{
"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
| 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
| Name | Type | Description |
|---|---|---|
roomId |
string |
Required: The room to get events from. |
| Name | Type | Description |
|---|---|---|
dir |
string |
Required: The direction to return events from. If this is set to One of: |
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 This endpoint can also accept a value returned as a If it is not provided, the homeserver shall return a list of messages
from the first or last (per the value of the Changed in |
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 |
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 Note that an empty |
end |
string |
A token corresponding to the end of 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 |
state |
[ClientEvent] |
A list of state events relevant to showing the Unless |
| 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 |
type |
string |
Required: The type of the event. |
unsigned |
UnsignedData |
Contains optional extra information about the event. |
| 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 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 |
prev_content |
EventContent |
The previous Changed in |
redacted_because |
ClientEvent |
The event that redacted this event, if any. |
transaction_id |
string |
The client-supplied transaction ID, for example, provided via
|
{
"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
| Name | Type | Description |
|---|---|---|
roomId |
string |
Required: The ID of the room to search |
| Name | Type | Description |
|---|---|---|
dir |
string |
Required: The direction in which to search. One of: |
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": "$143273582443PhrSn:example.org",
"origin_server_ts": 1432735824653
}
404 response
| 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
| 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
Get a copy of the current state and the most recent messages in a room.
This endpoint should now exclusively be used for “peeking” into viewable rooms
you’re not a member of (Room Previews).
Using this endpoint for something other than peeking was deprecated in r0 of this specification.
The relevant information for syncing is now returned by the
/sync API. See the
migration guide.
| Rate-limited: | No |
|---|---|
| Requires authentication: | Yes |
Request
Request 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
| 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: |
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 One of: |
| 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’ |
| 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 |
end |
string |
Required: A token which correlates to the end of |
start |
string |
A token which correlates to the start of If no earlier events are available, this property may be omitted from the response. |
| 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 |
type |
string |
Required: The type of the event. |
unsigned |
UnsignedData |
Contains optional extra information about the event. |
| 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 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 |
prev_content |
EventContent |
The previous Changed in |
redacted_because |
ClientEvent |
The event that redacted this event, if any. |
transaction_id |
string |
The client-supplied transaction ID, for example, provided via
|
{
"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"
}
向房间发送事件
[Added in v1.3]
如事件 关联 其它事件,服务器可能做后处理。关联类型(rel_type)决定了可用性(如用户对同一目标事件只能发送一次)。
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
| 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:
|
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
| 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
| 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."
}
示例
合法写法:
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" }
非法请求:
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" }
注意区分 state key 与 txnId:
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" }
可按用户 ID 作为 state_key 存储用户相关状态:
PUT /rooms/!roomid:domain/state/m.favorite.animal.event/%40my_user%3Aexample.org
{ "animal" : "cat", "reason": "fluffy" }
也可无 state_key:
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
| 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
| Name | Type | Description |
|---|---|---|
errcode |
string |
Required: An error code. |
error |
string |
A human-readable error message. |
{
"errcode": "M_UNKNOWN",
"error": "An unknown error occurred"
}
撤回(Reductions)
因事件可扩展,恶意用户/服务器或添违法内容。部分事件无法直接删除(如成员事件),因此采用撤回手段,将事件只保留协议必需字段。撤回后的事件在客户端/服务器侧皆只返回精简内容。客户端访问已被撤回事件可在 unsigned 红包 redacted_because 带原撤回事件。
具体算法见房间版本说明。事件撤回后不可恢复。远程主服务器撤回有效性判据同理。
客户端接收到 m.room.redaction 事件时,须同步调整被撤回事件内容。
撤回事件仍能影响房间状态。撤回后,状态事件表现为属性未指定(除撤回算法保护外)。如撤回的 join 事件,用户仍为已加入;如撤回话题,则话题移除而非回滚至先前。
事件
m.room.redaction
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"
}
}
客户端行为
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 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
| 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"
}
形成事件关系
[Changed in v1.3]
部分场景需将一个事件逻辑关联至另一个,如回复消息、编辑、提供上下文等。
关联为父子结构,任意事件可为父。父不显式列子关系,由子通过 content 内 m.relates_to(见下)描述。可相互嵌套形成树。
子事件可指向其他子事件,形成链甚至树型结构。
为便于服务器聚合查找,m.relates_to 必须出现在加密事件的明文部分,不能仅内嵌在密文负载中。
加密事件内 payload 的 m.relates_to 应被忽略,只以明文副本为准(包括无副本时)。以确保客户端与服务器关于关系的处理一致。
不规范、非法、跨房间等关联均忽略。客户端应平等显示无效也无关联事件,可加以错误提示。
m.relates_to 格式定义如下:
m.relates_to
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.
| 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 |
Examples
{
"m.relates_to": {
"event_id": "$an_event",
"rel_type": "org.example.relationship"
}
}
关联类型
本规范描述如下关联类型:
- 富回复(注:未用
rel_type) - 事件替换
- 事件标注(注释、reaction)
- 线程
- 引用
子事件聚合
[Added in v1.3]
部分关系可由服务器按 rel_type 聚合,让客户端无需单独获取所有子事件。例如可统计有多少用户用某 reaction key。
聚合详情因关联类型而异。
下述端点提供聚合:
GET /rooms/{roomId}/messagesGET /rooms/{roomId}/context/{eventId}GET /rooms/{roomId}/event/{eventId}GET /rooms/{roomId}/relations/{eventId}GET /rooms/{roomId}/relations/{eventId}/{relType}GET /rooms/{roomId}/relations/{eventId}/{relType}/{eventType}GET /sync限流区间POST /search匹配 room_events- [Added in
v1.4]GET /rooms/{roomId}/threads
当前状态事件不支持聚合。欢迎补充相关 MSC 以提升支持。
客户端应本地追加聚合。例如收到新 reaction,更新计数。若事件本地未能找到或发生变化,需自行整合。
被撤回的父事件,其子事件(如 reaction)依然存在,但被撤回的子事件与父失去关联,需要解除聚合关系。客户端本地聚合同理。
建议客户端对聚合实现本地回显,如优化用户体验。但需注意不可二次聚合。建议为尚未获取事件 ID 的发出事件也能维护聚合信息,可用事务 ID 临时识别。
若历史可见性受限,部分事件不可见,聚合也缺失。关键信息(如投票)应充分考虑可见性。
服务器如有历史断层,聚合也会不准确。
关系 API
[Added in v1.3]
客户端可通过下述端点取父事件的子事件,如需递归聚合则需自行处理。
通过 event_type 或 rel_type 过滤条件,服务器仅返回匹配的直接/间接关联事件,所有非匹配类型的间接关联将被排除,如想获取完整线程建议客户端本地遍历而不是用 rel_type。
由于回复未使用 rel_type,无法通过本 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
| 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. |
| Name | Type | Description |
|---|---|---|
dir |
string |
Optional (default One of: Added in |
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 |
limit |
integer |
The maximum number of results to return in a single 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 If set to The default value is Added in |
to |
string |
The pagination token to stop returning results at. If not supplied, results
continue up to Like |
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 |
| 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 |
type |
string |
Required: The type of the event. |
unsigned |
UnsignedData |
Contains optional extra information about the event. |
| 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 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 |
prev_content |
EventContent |
The previous Changed in |
redacted_because |
ClientEvent |
The event that redacted this event, if any. |
transaction_id |
string |
The client-supplied transaction ID, for example, provided via
|
{
"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
| 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
| 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. |
| Name | Type | Description |
|---|---|---|
dir |
string |
Optional (default One of: Added in |
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 |
limit |
integer |
The maximum number of results to return in a single 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 If set to The default value is Added in |
to |
string |
The pagination token to stop returning results at. If not supplied, results
continue up to Like |
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 |
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 |
| 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 |
type |
string |
Required: The type of the event. |
unsigned |
UnsignedData |
Contains optional extra information about the event. |
| 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 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 |
prev_content |
EventContent |
The previous Changed in |
redacted_because |
ClientEvent |
The event that redacted this event, if any. |
transaction_id |
string |
The client-supplied transaction ID, for example, provided via
|
{
"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
| 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
| 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 |
relType |
string |
Required: The relationship type to search for. |
roomId |
string |
Required: The ID of the room containing the parent event. |
| Name | Type | Description |
|---|---|---|
dir |
string |
Optional (default One of: Added in |
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 |
limit |
integer |
The maximum number of results to return in a single 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 If set to The default value is Added in |
to |
string |
The pagination token to stop returning results at. If not supplied, results
continue up to Like |
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 |
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 |
| 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 |
type |
string |
Required: The type of the event. |
unsigned |
UnsignedData |
Contains optional extra information about the event. |
| 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 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 |
prev_content |
EventContent |
The previous Changed in |
redacted_because |
ClientEvent |
The event that redacted this event, if any. |
transaction_id |
string |
The client-supplied transaction ID, for example, provided via
|
{
"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
| Name | Type | Description |
|---|---|---|
errcode |
string |
Required: An error code. |
error |
string |
A human-readable error message. |
{
"errcode": "M_NOT_FOUND",
"error": "Event not found."
}
房间
类型
[Added in v1.2]
房间可选带类型,区分功能。无类型的房间一般为会话用途。
类型有助于客户端区分功能(会话/数据型),房间类型记录于 m.room.create 的 type 字段,建房时以 creation_content 提供。
当前规范房间类型有:
类型可扩展,依 命名空间标识符。
创建
主服务器建房时会生成 m.room.create 事件,作为事件树根;并自动设置权限等其它事件,如:
m.room.power_levels:设置用户及动作权限等m.room.join_rules:设置加入规则
详细参见 Room Events。创建房间接口如下。
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:
-
The
m.room.createevent itself. Must be the first event in the room. -
An
m.room.memberevent for the creator to join the room. This is needed so the remaining events can be sent. -
A default
m.room.power_levelsevent, giving the room creator (and not other members) permission to send state events. Overridden by thepower_level_content_overrideparameter. -
An
m.room.canonical_aliasevent ifroom_alias_nameis given. -
Events set by the
preset. Currently these are them.room.join_rules,m.room.history_visibility, andm.room.guest_accessstate events. -
Events listed in
initial_state, in the order that they are listed. -
Events implied by
nameandtopic(m.room.nameandm.room.topicstate events). -
Invite events implied by
inviteandinvite_3pid(m.room.memberwithmembership: inviteandm.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 |
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 |
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 |
name |
string |
If this is included, an |
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
|
preset |
string |
Convenience parameter for setting various default state events based on a preset. If unspecified, the server should use the One of: |
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 The complete room alias will become the canonical alias for
the room and an |
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 |
topic |
string |
If this is included, an |
visibility |
string |
A One of: |
| 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. |
| 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 |
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
|
200 response
| Name | Type | Description |
|---|---|---|
room_id |
string |
Required: The created room’s ID. |
{
"room_id": "!sefiuhWgwghwWgh:example.com"
}
400 response
| Name | Type | Description |
|---|---|---|
errcode |
string |
Required: An error code. |
error |
string |
A human-readable error message. |
{
"errcode": "M_UNKNOWN",
"error": "An unknown error occurred"
}
房间别名
服务器可托管人类友好的房间别名(如 #name:server.name)。
别名以主服务器域名作用域为限,服务器一般拒绝跨域维护,因此协议不提供给外部服务器发送更新的机制。但服务器必须处理其它服务器的别名解析请求。
房间不存储自身的全部别名,权限用户可通过 m.room.canonical_alias 公布别名。别名实践中常发生漂移,请客户端校验别名链接的实际房间 ID,再做分享或使用。
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
| 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
| 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
| 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
| 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
| 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
| 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
| 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
| 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
| 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
| 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
| 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
| 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
}
权限
本节尚在完善中。
房间权限通过 Power Level 实现。任一操作需满足所需 power level。m.room.power_levels 以状态事件记录房间内用户、各操作所需权限,默认用户 0,房主 100。用户可提升他人 power level,但不得超过自身。权限设定跨房间,有些操作如踢人、封禁、发送状态事件都需对应值。详情见 m.room.power_levels。
建议 power level 对应含义如下:
- 0 普通用户
- 50 协管
- 100 管理员
成员关系
必须为成员才能发送、接收事件。房间成员状态:
- 无关:不可发送接收
- 敲门:请求加入,等待批准
- 邀请:已被邀请,尚未参与
- 已加入:可正常操作
- 被封禁:不可加入
例外情形:
- 拒绝邀请:发送
m.room.member带leave内容,前提已被邀请 - 敲门加入:
m.room.member带knock内容,表示意图加入 - 撤销敲门:发送
leave相当于拒绝邀请
部分房间需先邀请才可加入,由 m.room.join_rules 控制:
public
自由加入
invite
需邀请
knock
要先敲门,若允许可由房间成员打招呼,请求准许
[Added in v1.2] restricted
满足指定房间成员条件或受邀请
[Added in v1.3] knock_restricted
结合 restricted 与 knock,即既可凭邀请/房间条件直接加入,也可敲门
状态转换流程:
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_rooms": [
"!foo:example.com"
]
}
加入房间
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
| 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 Added in |
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
|
403 |
You do not have permission to invite the user to the room. A meaningful
|
429 |
This request was rate-limited. |
200 response
{}
400 response
| 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
| 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
| 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
| Name | Type | Description |
|---|---|---|
roomIdOrAlias |
string |
Required: The room identifier or alias to join. |
| Name | Type | Description |
|---|---|---|
via |
[string] |
The servers to attempt to join the room through. One of the servers must be participating in the room. Added in |
Request body
| Name | Type | Description |
|---|---|---|
reason |
string |
Optional reason to be included as the Added in |
third_party_signed |
Third-party Signed |
If a |
| 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 |
403 |
You do not have permission to join the room. A meaningful
|
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
| 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
| 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
| 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 Added in |
third_party_signed |
Third-party Signed |
If supplied, the homeserver must verify that it matches a pending
|
| 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 |
403 |
You do not have permission to join the room. A meaningful
|
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
| 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
| 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
}
敲门进入房间
[Added in v1.1]
[Changed in v1.3]
自 v1.3 支持敲门进入 restricted 房间。
如加入规则允许,外部用户可 /knock 敲门,成员可邀请(/invite)或拒绝(/kick、/ban、设置 leave)。用户已在房间、已邀请、被封禁不可敲门。
同意敲门需有邀请权限,拒绝需有踢/封禁权限。
主服务器收到邀请即视为敲门被接受,无需关联至原敲门事件。服务器可自动接受邀请。
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
| Name | Type | Description |
|---|---|---|
roomIdOrAlias |
string |
Required: The room identifier or alias to knock upon. |
| Name | Type | Description |
|---|---|---|
via |
[string] |
The servers to attempt to knock on the room through. One of the servers must be participating in the room. Added in |
Request body
| Name | Type | Description |
|---|---|---|
reason |
string |
Optional reason to be included as the |
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 |
403 |
You do not have permission to knock on the room. A meaningful
|
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
| 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
| 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
| 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
}
限制型房间
[Added in v1.2]
[Changed in v1.3]
v1.3 起,knock_restricted 房可敲门,同理需房间支持。
限制型房间 (restricted) 在加入规则中指定 “allow conditions”。若已被邀请,规则不影响其;否则服务器判断用户是否满足任一 condition。
目前唯一可用 condition 是 m.room_membership:满足情况即已加入指定 room_id 的成员。例如:
{
"join_rule": "restricted",
"allow": [
{
"room_id": "!other:example.org",
"type": "m.room_membership"
}
]
}
离开房间
成员可离开房间(含拒绝邀请、撤回敲门),操作完成后对应房间将不会再出现在 /sync 返回中(除非用带 include_leave 的过滤器)。
如是 invite-only 房间,用户需被重新邀请才能再入。
用户也可“忘记房间”(forget),忘记后只有重新加入/被邀请才可恢复。
可被踢出(kick),需要操作者有足够权限。被踢视同主动离开,且只有非 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
| 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
| 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
| 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
| 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 Added in |
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
| 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
| 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
|
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
|
200 response
{}
403 response
| 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."
}
房间封禁
可封禁成员,操作是将 member 调为 ban,禁止加入、收发事件,踢出本房。必须有足够 power level,接口如下:
{
"user_id": "<待封禁成员ID>",
"reason": "string: 封禁原因"
}
或直接设置 membership:
{
"membership": "ban"
}
解封需显式发送 /rooms/<room_id>/unban。
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
| 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 Added in |
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
|
200 response
{}
403 response
| 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
| 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 Added in |
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
|
200 response
{}
403 response
| 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."
}
列出房间
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
| 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: |
{
"visibility": "public"
}
404 response
| 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
| 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: |
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
| 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
| 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. |
| 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 |
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 Added in |
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
| 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 This is the |
| 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 Added in |
Request body example
{
"filter": {
"generic_search_term": "foo",
"room_types": [
null,
"m.space"
]
},
"include_all_networks": false,
"limit": 10,
"third_party_instance_id": "irc-freenode"
}
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. |
| 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 |
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 Added in |
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/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. |
| Name | Type | Description |
|---|---|---|
avatar_url |
URI |
The avatar url, as an |
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
| 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}
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
| 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
| 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
| 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
| 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
| 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
| 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
| 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
| 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
| 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
| 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
| 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
}
服务器行为
主服务器必须接受以下情况下的用户资料查询:
- 与请求方共享房间的用户
- 公开房间的成员(主服务器已知)
其它情况主服务器可直接拒绝(403 + M_FORBIDDEN)。
远程用户资料查询成功时主服务器应向远端请求。远端可拒绝联邦查询。
不存在的用户,主服务器可选返回 403 或 404,建议 403。
资料变更通知
因 display name 和 avatar 处处用到,信息修改时会自动广播变更:
- 发送 join 状态
m.room.member事件至其所在所有房间更新 displayname / avatar_url - 发送
m.presence含新 displayname / avatar_url / presence
主服务器应自动完成上述通知。
此外,主服务器自有用户房间成员事件应含 displayname、avatar_url 以便客户端直接展示。
模块
模块是客户端-服务器 API 的组成部分,但并非适用于所有端点。模块定义严格区分于实验扩展或可选特性。合规服务器必须支持全部模块及相关规范(如仅面向特定客户端类型,可仅支持所需模块)。合规模块的客户端必须支持其目标特性的所有模块及规范,详见特性配置文件。
特性配置文件
Matrix 支持多种客户端形态:嵌入式 IoT 至桌面客户端。并非所有设备都能实现全部功能(如无屏幕)。客户端可归入如下类型,每类包含一组必须实现的功能。必须整体实现整类配置文件所含特性。
简要表
| 模块 / 配置文件 | 网页端 | 移动端 | 桌面端 | 命令行 | 嵌入式 |
|---|---|---|---|---|---|
| 内容仓库 | 必须 | 必须 | 必须 | 可选 | 可选 |
| 直接消息 | 必须 | 必须 | 必须 | 必须 | 可选 |
| 忽略用户 | 必须 | 必须 | 必须 | 可选 | 可选 |
| 即时消息 | 必须 | 必须 | 必须 | 必须 | 可选 |
| 在线状态 | 必须 | 必须 | 必须 | 必须 | 可选 |
| 推送通知 | 可选 | 必须 | 可选 | 可选 | 可选 |
| 消息回执 | 必须 | 必须 | 必须 | 必须 | 可选 |
| 房间历史可见性 | 必须 | 必须 | 必须 | 必须 | 可选 |
| 房间升级 | 必须 | 必须 | 必须 | 必须 | 可选 |
| 第三方邀请 | 可选 | 必须 | 可选 | 可选 | 可选 |
| 输入状态通知 | 必须 | 必须 | 必须 | 必须 | 可选 |
| 用户与房间提醒 | 必须 | 必须 | 必须 | 可选 | 可选 |
| VoIP | 必须 | 必须 | 必须 | 可选 | 可选 |
| 客户端配置 | 可选 | 可选 | 可选 | 可选 | 可选 |
| 设备管理 | 可选 | 可选 | 可选 | 可选 | 可选 |
| 端到端加密 | 可选 | 可选 | 可选 | 可选 | 可选 |
| 事件注释与回应 | 可选 | 可选 | 可选 | 可选 | 可选 |
| 事件上下文 | 可选 | 可选 | 可选 | 可选 | 可选 |
| 事件替换 | 可选 | 可选 | 可选 | 可选 | 可选 |
| 已读未读标记 | 可选 | 可选 | 可选 | 可选 | 可选 |
| 访客接入 | 可选 | 可选 | 可选 | 可选 | 可选 |
| 内容管理策略列表 | 可选 | 可选 | 可选 | 可选 | 可选 |
| OpenID | 可选 | 可选 | 可选 | 可选 | 可选 |
| 引用关系 | 可选 | 可选 | 可选 | 可选 | 可选 |
| 内容举报 | 可选 | 可选 | 可选 | 可选 | 可选 |
| 富回复 | 可选 | 可选 | 可选 | 可选 | 可选 |
| 房间预览 | 可选 | 可选 | 可选 | 可选 | 可选 |
| 标签功能 | 可选 | 可选 | 可选 | 可选 | 可选 |
| SSO 客户端登录/认证 | 可选 | 可选 | 可选 | 可选 | 可选 |
| Secrets | 可选 | 可选 | 可选 | 可选 | 可选 |
| 点对点消息 | 可选 | 可选 | 可选 | 可选 | 可选 |
| 服务器访问控制列表 | 可选 | 可选 | 可选 | 可选 | 可选 |
| 服务器管理 | 可选 | 可选 | 可选 | 可选 | 可选 |
| 服务器通知 | 可选 | 可选 | 可选 | 可选 | 可选 |
| 服务器端搜索 | 可选 | 可选 | 可选 | 可选 | 可选 |
| 空间 | 可选 | 可选 | 可选 | 可选 | 可选 |
| 贴纸消息 | 可选 | 可选 | 可选 | 可选 | 可选 |
| 第三方网络 | 可选 | 可选 | 可选 | 可选 | 可选 |
| 线程 | 可选 | 可选 | 可选 | 可选 | 可选 |
各模块具体需实现内容详见相应章节。
客户端种类
独立网页端(Web)
即以 Matrix 为主的 Web App,SPA、MPA 等。
移动端(Mobile)
专门为移动设备开发的 Matrix 客户端,通常为 App。
桌面端(Desktop)
原生桌面 GUI 客户端。
命令行(CLI)
文本终端使用的客户端。
嵌入式(Embedded)
嵌入至其他网站(如 iframe)或嵌入设备,如家用电器、汽车等,仅做少量操作,无需完整通信功能。
Application
指嵌入网页,供特定网站专用的 Matrix 客户端,通常为单一用途。
Device
指嵌入设备(如水壶、冰箱、汽车)的小型客户端,仅执行部分操作,环境受限。
即时消息
该模块增加了向房间发送易于理解的信息的支持,同时也支持为房间本身关联可读性强的信息,如房间名称和话题。
事件
m.room.message
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. |
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
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
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
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 |
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. |
| 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. |
size |
integer |
Size of the image in bytes. |
thumbnail_info |
ThumbnailInfo |
Metadata about the image referred to in |
thumbnail_url |
string |
The URL (typically |
w |
integer |
The intended display width of the image in pixels. This may differ from the intrinsic dimensions of the image file. |
| 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. |
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
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 消息类型(msgtype)
每个 m.room.message 必须包含一个 msgtype 键,用于标识发送消息的类型。不同类型的消息有各自必须和可选的键,具体如下。如果客户端无法显示给定的 msgtype,那么应当显示备用的纯文本 body 字段。
某些消息类型支持事件内容中的 HTML,客户端应优先显示可用的 HTML。目前,m.text、m.emote、m.notice、m.image、m.file、m.audio、m.video 和 m.key.verification.request 支持额外的 format 参数 org.matrix.custom.html。当提供该字段时,必须同时提供携带 HTML 的 formatted_body。HTML 的纯文本版本则应存于 body 字段。
[Changed in v1.10]
在以往的规范版本中,format 和 formatted 字段仅限于 m.text、m.emote、m.notice 以及 m.key.verification.request。现在该列表扩展至 m.image、m.file、m.audio、m.video 以支持媒体标题。
为防止跨站脚本攻击(XSS)、HTML 注入及类似攻击,客户端应限制渲染的 HTML 范围。强烈建议仅允许以下 HTML 标签,其余标签应拒绝使用与渲染: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 功能在 WHATWG HTML Living Standard 标准中被弃用时,可以无需提交 规范变更提案而弃用并用其现代等价替换之。
[Changed in v1.10]
在以往规范中,建议使用 font 标签及其 data-mx-bg-color、data-mx-color 和 color 属性。该标签现已弃用,新的消息推荐使用带有 data-mx-bg-color 和 data-mx-color 属性的 span 标签替代。
上述标签的所有属性均不应被允许,因为部分属性可能带来其他干扰性风险,比如添加 onclick 事件或设置过大的文本。客户端仅应允许下表中为各标签列出的属性。其中,data-mx-bg-color 和 data-mx-color 为列表项时,客户端应将其值(即 # 开头的 6 位十六进制颜色代码)转换为该标签相应的 CSS/属性。
| 标签 | 允许的属性 |
|---|---|
span |
data-mx-bg-color、data-mx-color、data-mx-spoiler(参见剧透消息)、data-mx-maths(参见数学消息) |
a |
target、href(前提是值不是相对路径,且 scheme 为 https、http、ftp、mailto、magnet 中之一) |
img |
width、height、alt、title、src(前提是来源为 Matrix 内容 (mxc://) URI) |
ol |
start |
code |
class(仅允许以 language- 开头的 class,以便语法高亮) |
div |
data-mx-maths(参见数学消息) |
除此之外,Web 客户端应确保所有 a 标签获得 rel="noopener" 属性,以防目标页面获取当前客户端标签页/窗口的引用。
标签嵌套不得超过 100 层。客户端仅应支持其能够渲染的子集标签,对无法渲染的标签采用其他表现方式显示。例如,若客户端无法正确渲染表格,可回退为制表符分隔文本。
除了不渲染不安全的 HTML 外,客户端也不应在事件中生成不安全的 HTML。客户端同样不应生成不必要的 HTML,比如由于富文本编辑导致的多余的段落标签。事件中的 HTML 应为有效 HTML,例如有适当的闭合标签、正确的属性(结合本文档自定义说明),且整体结构合法。
[Changed in v1.13]
在更早的规范版本中,富回复 可以使用特殊标签 mx-reply。现在不再需要这样做。客户端应去除该标签及其内容。详情请参见“富回复”章节。
未来的规范会支持更强大且可扩展的消息格式化选项,例如提案 MSC1767。
m.text
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 |
string |
The formatted version of the |
msgtype |
string |
Required: One of: |
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
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 |
string |
The formatted version of the |
msgtype |
string |
Required: One of: |
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
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 |
string |
The formatted version of the |
msgtype |
string |
Required: One of: |
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
m.image
This message represents a single image and an optional thumbnail.
| Event type: | Message event |
|---|
Content
| Name | Type | Description |
|---|---|---|
body |
string |
Required: If Changed in |
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 |
format |
string |
The format used in the Added in |
formatted_body |
string |
The formatted version of the Added in |
info |
ImageInfo |
Metadata about the image referred to in |
msgtype |
string |
Required: One of: |
url |
string |
Required if the file is unencrypted. The URL (typically |
| 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. |
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 |
string |
The URL (typically |
w |
integer |
The intended display width of the image in pixels. This may differ from the intrinsic dimensions of the image file. |
| 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. |
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
m.file
This message represents a generic file.
| Event type: | Message event |
|---|
Content
| Name | Type | Description |
|---|---|---|
body |
string |
Required: If Changed in |
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 Added in |
formatted_body |
string |
The formatted version of the Added in |
info |
FileInfo |
Information about the file referred to in |
msgtype |
string |
Required: One of: |
url |
string |
Required if the file is unencrypted. The URL (typically |
| Name | Type | Description |
|---|---|---|
mimetype |
string |
The mimetype of the file e.g. |
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 |
string |
The URL to the thumbnail of the file. Only present if the thumbnail is unencrypted. |
| 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. |
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
m.audio
This message represents a single audio clip.
| Event type: | Message event |
|---|
Content
| Name | Type | Description |
|---|---|---|
body |
string |
Required: If Changed in |
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 |
format |
string |
The format used in the Added in |
formatted_body |
string |
The formatted version of the Added in |
info |
AudioInfo |
Metadata for the audio clip referred to in |
msgtype |
string |
Required: One of: |
url |
string |
Required if the file is unencrypted. The URL (typically |
| Name | Type | Description |
|---|---|---|
duration |
integer |
The duration of the audio in milliseconds. |
mimetype |
string |
The mimetype of the audio e.g. |
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
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: |
| 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 |
string |
The URL to a thumbnail of the location being represented. Only present if the thumbnail is unencrypted. |
| 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. |
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
m.video
This message represents a single video clip.
| Event type: | Message event |
|---|
Content
| Name | Type | Description |
|---|---|---|
body |
string |
Required: If Changed in |
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 |
format |
string |
The format used in the Added in |
formatted_body |
string |
The formatted version of the Added in |
info |
VideoInfo |
Metadata about the video clip referred to in |
msgtype |
string |
Required: One of: |
url |
string |
Required if the file is unencrypted. The URL (typically |
| 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. |
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 |
string |
The URL (typically |
w |
integer |
The width of the video in pixels. |
| 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. |
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"
}
}
客户端行为
客户端应验证收到事件的结构,确保所需字段存在且类型正确。对于格式错误的事件,可以选择丢弃或向用户显示占位提示消息。被修订(redacted)的 m.room.message 事件必须从客户端删除,可以用占位文本(如“[REDACTED]”)替换,或直接从消息视图移除。
带有附件的事件(如 m.image、m.file)应使用内容仓库模块上传(如可用)。所得的 mxc:// URI 可用于 url 字段。
客户端可通过 info.thumbnail_url 字段为附件带上客户端生成的缩略图。该缩略图也应为 mxc:// URI。呈现附带附件的事件时,客户端可直接使用缩略图,或者通过内容仓库模块请求 homeserver 基于原始附件生成缩略图。
发送消息时的推荐做法
在发送失败时,客户端应使用指数退避算法重试请求,重试时间 T 为一段时间,建议不超过 5 分钟。超时后客户端应停止重试,并将消息标记为“未发送”。用户应能够手动重新发送未发送消息。
用户可能会一次输入并快速发送多条消息。客户端应保持用户发送消息的顺序,这意味着应等待上一请求响应后再发送下一个请求。这可能导致“队头阻塞”。为减轻此影响,应按房间分别使用队列而非全局队列,因为顺序仅在单一房间内有意义,房间间无需严格顺序。
本地回显(Local Echo)
用户点击“发送”按钮时,消息应立即在消息视图中显示,哪怕消息正在发送中。这一过程称为“本地回显”。客户端应实现本地消息回显。客户端可采用不同展示方式显示尚未被服务器处理的消息。当服务器响应后应移除该特殊格式。
客户端需要能将其发送的消息和从事件流中收到的同一消息进行匹配。从事件流收到的同一消息的回显称为“远程回显”。本地回显和远程回显都要能被识别为相同消息,以防止重复显示。理想情况下,这一过程对用户透明:UI 从本地回显切换为远程回显时不会闪烁。通过使用用于发送事件的事务 ID,可减少切换时的闪烁。事务 ID 会作为收到事件时 unsigned 数据中的 transaction_id 字段返回。
如果客户端无法使用事务 ID,那么当远程回显在消息发送请求完成之前到达事件流时,很可能会出现闪烁。在这种情况下,事件在消息发送请求完成、客户端获得事件 ID 之前就到了,导致无法将其识别为远程回显。这样客户端在一段时间内(取决于服务器响应速度)会同时显示两条消息。请求完成后,客户端可通过查找重复事件 ID 移除多余事件。
计算用户的显示名
客户端可能希望在成员列表或消息发送时展示房间成员的可读型显示名。然而,不同成员可能出现显示名冲突。显示名在展示给用户前必须唯一化处理,以防止冒充其他用户。
为确保客户端间一致处理,推荐使用如下算法为指定用户计算唯一显示名:
- 检查相关用户的
m.room.member状态事件。 - 若该状态事件无
displayname字段或该字段为null,则用其原始用户 ID 作为显示名。否则: - 若
m.room.member事件中的displayname在房间中所有membership: join或membership: invite成员里是唯一的,则用该displayname作为可见显示名。否则: - 若
displayname不唯一,应结合用户 ID 做唯一化处理,例如“显示名 (@id:homeserver.org)”。
开发者在实现该算法时需注意:
- 一名成员的显示名有可能因其他成员状态变化而变化。例如,若
@user1:matrix.org在房间中显示为Alice,当@user2:example.com也以Alice加入该房间时,两名用户都必须使用唯一化后的显示名。相反,若其中一名用户更改显示名致不再冲突,两者又可拥有自己原先的显示名。客户端需注意并确保对受影响成员正确重命名。 - 房间显示名也可能因成员名单变化而受影响。因为房间名有时基于用户显示名派生(见计算房间显示名)。
- 若全量遍历成员列表以查重显示名,则会导致 O(N^2) 复杂度,该实现对房间成员众多时很低效。建议客户端维护一个从
displayname到使用该名成员列表的哈希表,以高效判断是否需唯一化。
随消息同步展示成员信息
客户端可能希望显示发送消息成员的显示名与头像 URL。可通过检查该用户 ID 的 m.room.member 状态事件获取(参见计算用户显示名)。
在用户分页浏览历史记录时,客户端可能希望展示成员的历史显示名与头像 URL。由于分页时会返回旧的 m.room.member 事件,因此可以实现该功能。一般做法是同时维护两组房间状态:旧状态和当前状态。随着新事件到达和/或用户回溯浏览,这两组状态会逐渐分化:新事件更新当前状态,分页事件更新旧状态。当分页事件顺序处理时,旧状态即为消息发送时的房间状态。历史显示名和头像 URL 可由此设置。
计算房间显示名
客户端可能希望显示房间的可读型名称。命名方式有多种选择。为保持不同客户端之间房间命名一致,推荐按照如下算法选择房间名:
- 若房间具有 m.room.name 状态事件且其
name字段非空,则采用该字段给出的名称。 - 若房间有 m.room.canonical_alias 状态事件且该
alias字段有效,则使用之。请注意,客户端在计算房间名时应避免使用alt_aliases。 - 如果以上条件都不满足,应根据房间成员组合房间名。客户端应考虑除当前用户外的 m.room.member 事件(定义如下)。
- 若房间
m.heroes数量大于等于m.joined_member_count + m.invited_member_count - 1,则可利用英雄成员的事件计算用户显示名(必要时唯一化)并拼接。比如,客户端可选择展示“Alice, Bob,以及 Charlie (@charlie:example.org)”作为房间名。客户端可根据用户体验选择限制用于生成房间名的成员数量。 - 若英雄成员数少于
m.joined_member_count + m.invited_member_count - 1,且总成员数大于 1,则应用英雄成员计算显示名(必要时唯一化),拼接后加上剩余成员人数。例如,“Alice、Bob 及其他 1234 位成员”。 - 若成员总数(加入和被邀请之和)小于等于 1(表明该成员为唯一成员),则依据上述规则显示房间为空。例如,“空房间(曾为 Alice)”、“空房间(曾为 Alice 及 1234 位成员)”或无成员时显示“空房间”。
- 若房间
客户端用 m.heroes 计算房间名时应对各国语言进行国际化处理。生成房间名时,客户端应尽量使用不少于 5 名英雄成员,但可根据实际需求调整数量以配合用户体验。
剧透消息
[Added in v1.1]
消息中的部分内容可通过剧透形式在视觉上对用户隐藏。这不影响服务器对事件内容的存储,仅是在视觉上提示用户相关内容可能会暴露重要信息,导致“剧透”。
发送剧透消息时,客户端必须使用 formatted_body,即上文描述的 org.matrix.custom.html 格式。因此,支持剧透的任意 msgtype 都须支持该格式。
剧透内容包裹在 span 标签中,原因(可选)放在 data-mx-spoiler 属性里。若无原因,属性值可留空或未定义,但该属性不能省略。
一个剧透消息示例:
{
"msgtype": "m.text",
"format": "org.matrix.custom.html",
"body": "Alice [剧透](mxc://example.org/abc123) 在电影里。",
"formatted_body": "Alice <span data-mx-spoiler>最终幸福地生活下去</span> 在电影里。"
}
若提供原因,则如下:
{
"msgtype": "m.text",
"format": "org.matrix.custom.html",
"body": "Alice [健康剧透](mxc://example.org/abc123) 在电影里。",
"formatted_body": "Alice <span data-mx-spoiler='健康'>最终幸福地生活下去</span> 在电影里。"
}
发送剧透时,客户端应如上示例在 body 字段提供包含原因的备用内容。备用 body 字段不应包含剧透正文,因为 body 可能被文本类客户端或通知直接显示。为防止剧透内容被泄露,强烈推荐客户端首先将剧透正文上传至媒体仓库,然后以 markdown 链接形式引用对应 mxc:// URI,如上述示例。
客户端应区别渲染剧透内容,并以某种显式交互提示。例如,可将剧透文本模糊化,提示用户点击后显示。
媒体标题
[Added in v1.10]
媒体消息(包括 m.image、m.file、m.audio、m.video)可包含题注,以补充说明媒体内容。
发送标题时,客户端必须同时使用 filename 和 body 字段,formatted_body 及 org.matrix.custom.html 格式为可选。
如存在 filename 字段,且其与 body 不同,则将 body 视为题注,否则 body 视为文件名。format 和 formatted_body 仅用于题注。
在旧规范中,body 字段通常用于上传文件名,而 filename 字段仅出现在 m.file 上且用法一致。
媒体消息附带题注示例:
{
"msgtype": "m.image",
"url": "mxc://example.org/abc123",
"filename": "dog.jpg",
"body": "这是一张~~猫咪~~照片 :3",
"format": "org.matrix.custom.html",
"formatted_body": "这是一张 <s>猫咪</s> 照片 :3",
"info": {
"w": 479,
"h": 640,
"mimetype": "image/jpeg",
"size": 27253
},
"m.mentions": {}
}
客户端必须与媒体一起渲染标题,并应优先渲染其格式化形式。
数学消息
[Added in v1.11]
用户可能希望在消息中发送数学符号或公式。
发送数学公式时,客户端必须使用 formatted_body,即采用上述 org.matrix.custom.html 格式。任何可用该格式的 msgtype 均可支持数学形式。
数学内容根据是否需要行内显示,使用 span 或 div 标签。用 data-mx-maths 属性书写 LaTeX 格式的公式。
标签内容为不能渲染 LaTeX 的客户端备用显示。可用图片、HTML 近似表示或原始 LaTeX 源文本作为备用。若用图片作为备用,发送方应注意接收端可能背景色不同所带来的显示问题。body 字段应包含文本表示的公式。
数学消息示例:
{
"msgtype": "m.text",
"format": "org.matrix.custom.html",
"body": "这是一个方程:sin(x)=a/b。",
"formatted_body": "这是一个方程:<span data-mx-maths=\"\\sin(x)=\\frac{a}{b}\">sin(<i>x</i>)=<sup><i>a</i></sup>/<sub><i>b</i></sub></span>"
}
LaTeX 语法定义不完整且有多种扩展,若客户端遇到无法渲染的语法,应优先显示备用内容。但客户端最低应支持LaTeX2e 的数学命令及 TeX 数学命令(部分命令因安全风险可例外)。
总的说来,LaTeX 给客户端带来了安全处理压力。部分命令(如可创建宏的命令)具潜在风险。客户端应拒绝处理此类命令,或确保安全处理(如限制递归)。客户端应以白名单方式只允许已知安全命令,而非黑名单拒绝已知不安全命令。
因此,客户端在未安全隔离环境下,不应直接调用 LaTeX 编译器渲染数学表达式,因为相关可执行文件并未设计处理不可信输入。有些 LaTeX 渲染库适合,仅允许部分 LaTeX 并限制递归深度。
服务器行为
HomeServer 在收到不包含 msgtype 键,或无文本型 body 键的 m.room.message 事件时,应拒绝请求并返回 400 HTTP 状态码。
安全注意事项
使用本模块发送的消息不会加密,端到端加密(E2E)仍在开发中(详见 E2E 模块)。
客户端应对所有显示的键进行不安全 HTML 的过滤,以防止跨站脚本(XSS)攻击。这包括房间名称和话题。
富回复(Rich replies)
富回复是一种特殊类型的关系,它有效地引用了被引用事件,供客户端以其所希望的方式进行渲染或处理。富回复通常与 m.room.message 事件一起使用。
[Changed in v1.3]
在规范 v1.3 之前,富回复仅限于表示为 HTML 格式正文的 m.room.message 事件。从 v1.3 开始,这一限制被解除,富回复现在可以应用于所有事件类型,无需再强制要求包含 HTML 格式正文。
此外,从 v1.3 起,富回复可以引用任何其他事件类型。此前,富回复只能引用另一条 m.room.message 事件。
[Changed in v1.13]
在规范早期版本中,富回复可在 body(通过前缀序列)和 formatted_body(通过自定义 HTML 元素)中包含原始消息的回退表示,供不支持富回复的客户端使用。目前已不再要求如此,但客户端仍应在渲染事件前移除这些回退内容。
要去除 body 中的回退内容,客户端应逐行遍历字符串,移除以回退前缀序列(> ,包括尾随空格)开始的所有行,遇到不含该前缀的行时停止处理。
要去除 format 为 org.matrix.custom.html 的 m.room.message 事件的 formatted_body 回退内容:如果 formatted_body 以 <mx-reply> 起始标签开头,客户端应移除整个 <mx-reply> 元素。
虽然富回复与另一个事件形成关系,但它们并不使用 rel_type 来建立这种关系。相反,采用名为 m.in_reply_to 的子键来描述回复关系,从而使 m.relates_to 的其它属性可被用于描述该事件的主关系。这意味着,如果一个事件只是单纯回复另一事件而无其它关系,m.relates_to 中的 rel_type 和 event_id 属性变为可选。
一个回复示例:
{
"content": {
"m.relates_to": {
"m.in_reply_to": {
"event_id": "$another_event"
}
},
"body": "That sounds like a great idea!"
},
// 事件所需的其他字段
}
请注意,m.in_reply_to 对象中的 event_id 具有与其直接位于 m.relates_to 下时相同的要求。
提及被回复用户
为了通知用户被回复,建议在回复中包括被回复事件的 sender 以及该事件中提及的所有用户。更多信息请参见用户和房间提及。
包含原始发送者及其他用户提及的示例:
{
"content": {
"m.relates_to": {
"m.in_reply_to": {
"event_id": "$another_event"
}
},
"body": "That sounds like a great idea!",
"m.mentions": {
"user_ids": [
// $another_event 的发送者
"@alice:example.org",
// 从 $another_event 的 m.mentions 属性中复制的另一个 Matrix ID
"@bob:example.org"
]
}
},
// 事件所需的其他字段
}
语音通信(VoIP)
本模块描述了房间内两位用户如何建立语音通信(VoIP)通话。语音和视频通话均基于 WebRTC 1.0 标准构建。通话信令通过向房间发送消息事件来实现。在本规范版本中,仅支持双方通信(如两点对两点,或点对多点会议设备)。虽然通话可在包含多位成员的房间中发起,但同一时间仅允许两台设备参与通话。
所有 VoIP 事件均包含一个 version 字段。该字段用于判断设备是否支持此新版本的协议。例如,客户端可利用该字段判断是否应期待来自对方的 m.call.select_answer 事件。如果客户端接收到的事件 version 字段不是 0 或 "1"(包括如数值型 1),则应视同其 version == "1" 处理。
需注意,这意味着未来任何版本的 VoIP 事件均应保持向后兼容性。如有必要引入非兼容性新规范,则将采用一套独立的事件类型。
通话方标识符
每当客户端首次参与新通话时,应为自身生成一个 party_id,在通话期间一直使用。此标识符应足够长,确保即使多设备同时生成应答,其间发生冲突的概率极低:建议使用 8 个大小写字母+数字字符。通话方通过 (user_id, party_id) 元组进行识别。
客户端将包含该 party_id 字段,并放置于所有 VoIP 事件内容的顶层,包括 m.call.invite。客户端用此字段识别自身事件的远端回显:由于用户可能与自己通话,无法简单地忽略来自自身用户的事件。此外,该字段还能区分不同客户端对同一邀请发出的不同应答,并将 m.call.candidates 事件与相应的应答/邀请进行匹配。
客户端实现可选择使用端到端加密中使用的设备 ID,用作此目的;或者可为每次通话生成不同 ID,以避免在未加密房间中泄露使用设备信息,或隐藏单一设备(即 Access Token)被用于多个通话方信令发送等信息。
party_id 的语法定义见下文。
礼让规则
根据 WebRTC 完美协商示例,在重新协商过程中存在礼让(politeness)规则。被叫方始终为礼让方。在碰撞(glare)情况下,通话方的礼让状态由是采纳呼入通话还是主动呼叫决定:如果客户端舍弃本地呼出而采用呼入通话,则其为礼让方。
通话事件存活性
m.call.invite 中包含 lifetime 字段,指示邀约有效的时长。收到邀请后,客户端应结合事件同步响应中的 age 字段与自接收到该事件以来的时间,判断邀约是否依然有效。使用 age 字段可确保客户端设备错误时钟不会导致通话异常。
若邀约有效且在用户可接受通话期间保持有效,应提示有来电。用户可接受通话的时长可由不同客户端自行决定,例如在锁定的移动设备上,可以比在解锁的桌面设备上更长。
在处理完整个同步响应(sync response)并(对于加密房间)尝试解密该房间全部加密事件后,客户端才应提示来电。这样可以避免同步响应中含有随后指示通话已挂断、被拒绝或已被他处接听的事件时,重复提示来电。
若客户端启动后,在处理本地已存储事件后,发现仍有有效的邀约,应在与 homeserver 完成一次同步后再予以提示。
建议的最小有效时长为 90 秒——这样可确保用户有充足时间接听来电。
ICE 候选(Candidate)批量发送
客户端应设法只发送少量候选事件,具体指引如下:
- 在邀请/应答事件本身中,应立即或几乎立即发现的 ICE 候选(例如 host 类型 candidate)。如能在短时间内收集到服务器反射或中继 candidate,应一并发送。建议初始延迟约 200ms。
- 之后,客户端应等待一段时间以收集更多候选,实现批量发送,而非每获一个立刻发送。建议在发送邀请后等待 2 秒,或发送应答后等待 500ms(因发送邀请后,客户端本就等待用户接听,可利用此延迟)。
结束候选通知(End-of-candidates)
值为空字符串的 ICE 候选表示不会再发送新的 ICE 候选。客户端必须在 m.call.candidates 消息中发送此类候选。虽然 WebRTC 规范要求浏览器生成此候选,但截止目前,并非所有浏览器都实现(Chrome 不生成,但会产生 icegatheringstatechange 事件)。候选生成结束时,客户端应立即发送全部候选,而不应再等待上文时间间隔。这可方便桥接到不支持逐步 ICE(trickle ICE)协议时对候选的批量处理。
DTMF
Matrix 客户端可按 WebRTC 规范发送 DTMF。截至 2020 年 8 月,WebRTC 标准尚不支持接收 DTMF,但 Matrix 客户端可接收并解析 RTP 负载中的 DTMF 信号。
VoIP 标识符的语法
call_id 和 party_id 必须遵循不透明标识符语法。
离开房间时的行为
若客户端检测到正在通话的用户离开房间,应将其视作所有正在进行中的通话的挂断事件。对于发送邀约而被邀请方离开房间的情形,规范未做硬性规定,但若房间内已无可接听用户,客户端可选择将其视作被拒绝(如仅剩发送方本人,或邀约的 invitee 字段被设置后未被接听)。
历史通话回溯时亦应如此处理。
支持的编解码器
Matrix 规范未强制指定特定音视频编解码器,完全遵循 WebRTC 规范。兼容的 Matrix VoIP 客户端将像被支持的“浏览器”一样,根据所支持的编解码器及其变体运作。需遵循最新的 WebRTC 规范版本,因此客户端应及时跟进 WebRTC 规范的新版本,无论 Matrix 规范是否变更。
事件
通用字段
Call event
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 |
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. |
事件
m.call.invite
m.call.invite
This event is sent by the caller when they wish to establish a call.
| Event type: | Message event |
|---|
Content
| 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 |
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 |
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 |
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. |
| Name | Type | Description |
|---|---|---|
sdp |
string |
Required: The SDP text of the session description. |
type |
string |
Required: The type of session description. One of: |
| Name | Type | Description |
|---|---|---|
audio_muted |
boolean |
Whether the audio track in the stream is muted. Defaults to Added in |
purpose |
string |
Required: The purpose of the stream. The possible values are:
One of: |
video_muted |
boolean |
Whether the video track in the stream is muted. Defaults to Added in |
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
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
| 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 |
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. |
| 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 |
string |
The SDP media type this candidate is intended for. At least one of |
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
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
| 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 |
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
m.call.answer
This event is sent by the callee when they wish to answer the call.
| Event type: | Message event |
|---|
Content
| 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 |
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 |
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. |
| Name | Type | Description |
|---|---|---|
sdp |
string |
Required: The SDP text of the session description. |
type |
string |
Required: The type of session description. One of: |
| Name | Type | Description |
|---|---|---|
audio_muted |
boolean |
Whether the audio track in the stream is muted. Defaults to Added in |
purpose |
string |
Required: The purpose of the stream. The possible values are:
One of: |
video_muted |
boolean |
Whether the video track in the stream is muted. Defaults to Added in |
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
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
| 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 |
selected_party_id |
string |
Required: The |
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
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
| 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 |
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 |
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. |
| Name | Type | Description |
|---|---|---|
sdp |
string |
Required: The SDP text of the session description. |
type |
string |
Required: The type of session description. One of: |
| Name | Type | Description |
|---|---|---|
audio_muted |
boolean |
Whether the audio track in the stream is muted. Defaults to Added in |
purpose |
string |
Required: The purpose of the stream. The possible values are:
One of: |
video_muted |
boolean |
Whether the video track in the stream is muted. Defaults to Added in |
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
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
| 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 |
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 |
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. |
| Name | Type | Description |
|---|---|---|
audio_muted |
boolean |
Whether the audio track in the stream is muted. Defaults to Added in |
purpose |
string |
Required: The purpose of the stream. The possible values are:
One of: |
video_muted |
boolean |
Whether the video track in the stream is muted. Defaults to Added in |
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
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 toice_failedwhich means no media connection could be established). Note that, in the case of an ICE renegotiation, a client should be sure to sendice_timeoutrather thanice_failedif 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 thereasonfield asuser_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
| 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 |
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 One of: Changed in |
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"
}
}
客户端行为
通话建立流程如下,双方通过消息事件进行通信:
呼叫方 被叫方
[发起呼叫]
m.call.invite ----------->
m.call.candidate -------->
[..candidates..] -------->
[接听来电]
<--------------- m.call.answer
m.call.select_answer ----------->
[通话正在进行中]
<--------------- m.call.hangup
或通话被拒绝时:
呼叫方 被叫方
m.call.invite ------------>
m.call.candidate --------->
[..candidates..] --------->
[拒绝通话]
<-------------- m.call.hangup
通话协商遵循 WebRTC 规范进行。
面对来电,客户端可采取多种操作:
- 发送
m.call.answer,尝试接听通话。 - 主动在所有设备上拒绝通话:如上图,发送
m.call.reject,则所有用户设备均停止响铃,并通知呼叫方其来电被拒。 - 忽略通话:不发送任何事件,仅本地停止通话提示。用户的其他设备仍会继续响铃,呼叫方设备继续显示响铃,若无设备响应,通话将自动超时。
多流(Streams)
客户端可在一次 VoIP 通话中发送多路流。应通过在 m.call.invite、m.call.answer 以及 m.call.negotiate 事件中加入 sdp_stream_metadata 属性来区分流。当元数据发生变更但无需重新协商时,可发送 m.call.sdp_stream_metadata_changed 事件。
推荐客户端对于带 audio_muted 字段且值设为 true 的来流,不要本地关闭 WebRTC 音轨。这是因为当对方取消静音后,客户端发送音频与 m.call.sdp_stream_metadata_changed 事件到达之间可能略有延迟,这段音频会被错过。对方静音后将停止发送音频,无需担心无意识的音频发送。
对于 video_muted,建议仍应本地关闭视频流,避免接收端看到黑屏。
如 sdp_stream_metadata 存在,但来流未被列在其中,应直接忽略。若某流用途未知(purpose 类型未知)也应忽略。
为保证兼容性,若对方首次发来的 m.call.invite 或 m.call.answer 缺少 sdp_stream_metadata 属性,客户端应假定对方不支持此属性,即无法区分多流,客户端仅应使用第一条来流,并勿发送多于一条。
实现本规范的客户端应忽略无流的轨道(streamless tracks)。
被邀请方
若通话仅面向特定用户,invitee 字段应被加入,并设置为该用户的 Matrix ID。不含 invitee 字段的邀请,默认为面向房间内除发送者以外任何成员。
客户端在接收到未过期的邀请,invitee 字段缺失或等于本用户 Matrix ID 时,应视为有效来电,但是否响铃需根据与呼叫方关系和来电地点判定。建议客户端默认忽略来自公共房间的呼叫邀请。强烈建议即便未为来电响铃,客户端也应在房间中展示来电并标记其被忽略。
通话碰撞(Glare)
“通话碰撞”指两位用户几乎在同一时间互相呼叫对方,导致已有呼入/呼出通话而无法建立。可使用碰撞解决算法决定应挂断哪个通话,应接听哪个通话。如双方客户端实用相同算法,将会选择同一个通话,通话得以正常建立。
因通话目标为房间而非具体用户,以下碰撞解决算法仅适用于同一房间的通话:
- 若客户端在准备发送
m.call.invite至某房间时,收到了同一房间的m.call.invite:- 客户端应取消本地外呼,转而自动为用户接听来电。
- 若客户端已向某房间发送
m.call.invite并在等待响应时收到同房间的m.call.invite:- 客户端应对两个通话的 call_id 按字典序比较,保留较小者,挂断较大者;如来电为较小者,客户端应代表用户接听之。
对用户而言,通话建立过程应如同直接接通,无需察觉背后切换。任何初始化媒体流应当平滑迁移至被采纳的通话。
服务器行为
Homeserver 可以(可选)向客户端提供 TURN 服务器信息,便于客户端通过 TURN 实现点对点通信。客户端可通过下述 HTTP API 获取 TURN 服务器信息。
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
| 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.typing
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"
}
客户端行为
当客户端接收到 m.typing 事件时,必须使用该事件中的用户 ID 列表来替换其当前所知的正在输入的所有用户。其原因在于服务器不会记住那些当前未在输入的用户,因为该列表增长得很快。客户端应将所有未在此列表中的用户 ID 标记为未输入状态。
建议客户端存储一个表示用户是否正在输入的 boolean 值。当该值为 true 时,应每隔 N 秒定时触发一次定时器,发送一次输入状态的 HTTP 请求。N 推荐值为 20-30 秒以内。客户端应不断重新发送此请求,以持续告知服务器用户仍在输入中。由于后续请求会取代旧的请求,建议在预期超时前留有 5 秒的安全余量。当用户停止输入时,将 boolean 状态变为 false 的变化应触发另一次 HTTP 请求,以告知服务器用户已停止输入。
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
| 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 |
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
| 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
}
安全性注意事项
客户端可能不希望通知房间内的所有人他们正在输入,而只通知房间内特定的用户。
回执
[Changed in v1.4] 新增了私有已读回执。
本模块增加了对回执的支持。回执是一种对事件的确认方式。该模块定义了用于表示用户已读至某一事件的 m.read 回执,以及用于相同目的但不会被其他用户察觉的 m.read.private 回执。主要来说,m.read.private 旨在清除通知,而不向他人公开已读状态。
为每个事件发送回执可能导致向主服务器发送大量流量。为了防止这一问题,回执采用“已读至”标记的方式实现。此标记表示确认适用于“至(含)”指定事件的所有事件。例如,将某事件标记为“已读”就意味着用户已经阅读了至此的所有事件。关于已读回执如何影响通知计数,请参见接收通知章节。
[Added in v1.4] 已读回执主要有三种形式:
- 非线程化:表示已读至某事件的回执,与线程无关。这等同于线程功能引入前的已读回执。
- 线程化,主时间线:表示对非特定线程事件的已读回执。用线程 ID
main标识。 - 线程化,特定线程:表示在指定线程内的已读回执。用线程根事件的事件 ID 标识。
有关线程化回执的更多详细信息,请参阅下文。
事件
[Changed in v1.4] 每个 user_id、receipt_type 和类别(非线程化,或 thread_id)三元组必须只关联一个 event_id。
m.receipt
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. |
| Name | Type | Description |
|---|---|---|
m.read |
{User ID: Receipt} |
A collection of users who have sent |
m.read.private |
{User ID: Receipt} |
Similar to |
| Name | Type | Description |
|---|---|---|
thread_id |
string |
The root thread event’s ID (or Added in |
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"
}
客户端行为
[Changed in v1.4] 修订以支持线程化已读回执。
在 /sync 接口中,回执列在指定房间的 ephemeral 事件数组下。新收到的回执是增量信息,用于更新已有的映射。客户端应依据 user_id、receipt_type 和(如有)thread_id 替换旧的已读回执。例如:
客户端收到 m.receipt:
user = @alice:example.com
receipt_type = m.read
event_id = $aaa:example.com
thread_id = undefined
客户端收到另一个 m.receipt:
user = @alice:example.com
receipt_type = m.read
event_id = $bbb:example.com
thread_id = main
此时客户端尚未替换任何确认。
客户端再次收到 m.receipt:
user = @alice:example.com
receipt_type = m.read
event_id = $ccc:example.com
thread_id = undefined
客户端用新回执 $ccc:example.com 替换之前 $aaa:example.com 的确认,但不会替换 $bbb:example.com,因为它属于线程。
客户端再次收到 m.receipt:
user = @alice:example.com
receipt_type = m.read
event_id = $ddd:example.com
thread_id = main
现在客户端用新 $ddd:example.com 的确认替换旧的 $bbb:example.com。客户端不会替换 $ccc:example.com 的旧回执,因为它是未线程化的。
客户端应在确定事件已显示给用户时才发送已读回执。仅仅收到事件并不能确保用户已经看到。用户应当执行某些操作,如查看事件所处房间或关闭通知,事件方可计为“已读”。客户端不应为自己的用户发送已读回执。
与发送回执的规则类似,线程化回执应出现在线程上下文中。如果线程被折叠,客户端尚未向用户展示该事件(或任何相关已读回执)。一旦用户展开线程,就应发送线程化已读回执,并显示来自其他用户的每线程回执。
客户端可通过以下 HTTP API 与其用户相关的回执状态进行更新。
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
| 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 Note that One of: Changed in |
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 Added in |
Request body example
{
"thread_id": "main"
}
Responses
| Status | Description |
|---|---|
200 |
The receipt was sent. |
400 |
The
|
429 |
This request was rate-limited. |
200 response
{}
400 response
| 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
| 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
}
私有已读回执
[Added in v1.4]
部分用户希望标记房间为已读,以清除其通知计数,但又不希望暴露自己已经阅读了特定消息。为此,客户端可以发送 m.read.private 回执,作用同 m.read,但不会向其他用户广播回执,从而实现只清除通知不公开已读状态。
服务器不得将 m.read.private 回执发送给除最初发送用户外的任何其他用户。
在 m.read 和 m.read.private 两者间,决定最高已读至标记时会采用“更靠前”或“更近期”的回执。关于这对通知计数影响的更多信息,请参见通知章节。
如果客户端发送的 m.read 回执“落后”于 m.read.private 回执,其他用户会看到该变化,但发送用户的通知计数不会回退到那一时间点。尽管不常见,出现 m.read(公开)回执比 m.read.private 回执滞后几条消息的情况也是合法的。
线程化已读回执
[Added in v1.4]
如果客户端未使用线程功能,则只会发送“非线程化”已读回执,无论线程如何都影响整个房间。
线程化回执指的是带有 thread_id 的回执,其目标为线程根事件的事件 ID 或主时间线用 main。
线程化引入了在同一房间中进行多次独立会话的概念,因此也对应有独立的已读回执和通知计数。某事件被认为“属于线程”,需满足以下任一条件:
- 其
rel_type为m.thread,或 - 在事件关系链上,其父事件通过
rel_type为m.thread的方式被关联到线程根。实现时不应无限级递归,建议最多递归 3 级以覆盖间接关系。
房间内未归属于某线程的事件视为主时间线中的事件。当用作线程引用(如回执和通知计数中),主时间线采用特殊线程 ID main。
线程根本身被视作主时间线事件,通过非线程关系与线程根相关的事件也被视为主时间线事件。
以下是一个房间的 DAG 示例,虚线表示事件间关系,实线表示拓扑排序。
该 DAG 可分解为 3 条线程化时间线,其中 A 和 B 为线程根:
据此可说明:
- 在
I上的线程化已读回执会标记A、B和I为已读。 - 在
E上的线程化已读回执会标记C和E为已读。 - 在
D上的非线程化已读回执会标记A、B、C和D为已读。
注意,仅用线程化回执将 A 标记为已读,并不会让 C、E、G 或 H 也被标记为已读。线程 A 的时间线需在 H 上设置属于该线程的线程化回执才能做到。
上述 3 个例子的回执示例如下:
{
"$I": {
"m.read": {
"@user:example.org": {
"ts": 1661384801651,
"thread_id": "main" // 因为 `I` 不在任何线程中,但回执为线程化回执
}
}
},
"$E": {
"m.read": {
"@user:example.org": {
"ts": 1661384801651,
"thread_id": "$A" // 因为 `E` 属于线程 `A`
}
}
},
"$D": {
"m.read": {
"@user:example.org": {
"ts": 1661384801651
// 无 `thread_id`,因为这是*非线程化*回执
}
}
}
}
发送已读回执的条件在线程化与非线程化场景下适用方式一致。例如,当用户展开某线程时,客户端可能会为该线程事件发送私有已读回执。
服务器行为
出于高效性考虑,应将回执合并打包为按房间和线程分组的事件后再发送给客户端。
部分回执会作为类型为 m.receipt 的 EDU 跨联邦发送。该 EDU 格式为:
{
<room_id>: {
<receipt_type>: {
<user_id>: { <内容(ts & thread_id, 当前支持)> }
},
...
},
...
}
这些均以增量方式相较此前已发送的回执推送。目前仅应使用一个 <receipt_type> :m.read。m.read.private 不得出现在联邦 m.receipt EDU 内。
安全性注意事项
回执是在事件图之外发送的,因此 m.receipt 事件内容不会进行完整性校验。
已读与未读标记
完全已读标记
某个房间的消息历史可以被划分为三个部分:用户已读(或表示对其不感兴趣)的消息、用户可能只读了一部分的消息,以及用户尚未见过的消息。“完全已读标记”(也称为“已读标记”)标记了第一部分的最后一个事件,而用户的已读回执则标记了第二部分的最后一个事件。
事件
用户的完全已读标记作为房间账户数据中的一个事件进行保存。可以通过读取该事件来判断用户当前的完全已读标记在房间中的位置,并且和其他账户数据事件一样,该事件在更新时将通过事件流推送下发。
完全已读标记以 m.fully_read 事件的形式保存。如果该事件在用户账户数据中不存在,应将完全已读标记视为用户的已读回执位置。
m.fully_read
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"
}
客户端行为
客户端无法通过直接修改 m.fully_read 账户数据事件来更新完全已读标记。相反,客户端必须使用已读标记 API 来更改该值。
[Changed in v1.4] 现在可以通过 /read_markers 发送 m.read.private 回执。
已读标记 API 还可以在设置完全已读标记位置的同时,更新用户的已读回执(m.read 或 m.read.private)的位置。因为已读回执和已读标记通常会被同时更新,因此客户端可能希望节省一次额外的 HTTP 调用。提供 m.read 和/或 m.read.private 的效果与对
/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
| 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 |
m.read |
string |
The event ID to set the read receipt location at. This is
equivalent to calling |
m.read.private |
string |
The event ID to set the private read receipt location at. This
equivalent to calling Added in |
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
| 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.fully_read。此外,服务器必须确保其对 /read_markers 请求中存在的 m.read 和 m.read.private 的处理方式与对
/receipt/{receiptType}/{eventId} 的请求完全相同。
当由于 /read_markers 请求导致 m.fully_read 事件被更新时,服务器必须通过事件流(例如 /sync),并在满足任何适用过滤器的前提下,将更新后的账户数据事件发送到客户端。
未读标记
[Added in v1.12]
客户端可以使用“未读标记”允许用户独立于已读回执或完全已读标记对房间进行后续关注的标记。
事件
用户在某房间中的未读标记通过房间账户数据中的 m.marked_unread 事件进行保存。可通过该事件来确定用户在房间中的当前未读标记状态。和其他账户数据事件一样,该事件在更新时会通过事件流下发。
m.marked_unread
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"
}
客户端行为
客户端必须通过直接修改 m.marked_unread 房间账户数据事件来更新未读标记。当标记房间为未读时,客户端不应更改 m.fully_read 标记,以便保留用户在该房间的已读位置。
当 unread 字段为 true 时,客户端应以视觉方式标注房间为未读。具体如何实现为实现细节。推荐客户端使用与未读通知房间相似的表现方式。
当打开某房间以展示其时间线时,客户端应通过将 unread 设为 false 来重置未读标记。
如果客户端提供通过为最新事件发送已读回执将房间标记为“已读”的功能,应同时重置未读标记。
如果 m.marked_unread 事件在用户账户数据中不存在,客户端必须视为 unread 为 false 进行处理。
服务器行为
此子模块对服务器没有额外要求。
在线状态
每个用户都有在线状态信息的概念。该信息包含以下内容:
- 用户当前是否在线
- 用户最近一次活跃的时间(由服务器检测)
- 某客户端是否认为用户当前处于空闲状态
- 关于用户当前状态的任意信息(例如“正在开会”)
这些信息既包括设备级(online、idle、last_active),也包括用户级(status)数据,由用户的宿主服务器聚合后,通过 m.presence 事件传输。在线状态事件会发送给有房间成员关系的相关用户。
用户的在线状态通过 presence 键表示,该键枚举以下几种状态之一:
online:用户连接到事件流时的默认状态。unavailable:此时用户不可达,例如用户处于空闲状态。offline:用户未连接到事件流,或有意禁止发送其个人信息。
事件
m.presence
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: |
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"
}
客户端行为
客户端可以通过下述 HTTP API 手动设置/获取自身的在线状态。
GET
/_matrix/client/v3/presence/{userId}/status
Get the given user’s presence state.
| Rate-limited: | No |
|---|---|
| Requires authentication: | Yes |
Request
Request 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: |
status_msg |
string|null |
The state message for this user if one was set. |
{
"last_active_ago": 420845,
"presence": "unavailable"
}
403 response
| 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
| 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
| 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: |
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
| 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
}
距离上一次活跃时长
服务器会维护一次记录“最后一次检测到用户主动事件”的时间戳。主动事件可能是向房间发送消息,或将在线状态更改为 online。这个时间戳通过名为 last_active_ago 的键呈现,表示自上次主动事件以来的相对毫秒数。
为减少服务器向客户端发送的在线状态更新数量,当在线状态为 online 时,服务器可能会包含一个布尔字段 currently_active。如果该字段为 true,服务器将不会继续发送活跃时间的更新,直到向客户端发送的更新中 a) currently_active 变为 false,或 b) 状态变为非 online。在此期间,客户端应始终认为该用户处于活跃状态,而不考虑 last_active_ago 的具体值。
每当服务器向客户端推送在线状态事件时,最新的活跃时间必须是当前的。currently_active 机制仅用于服务器停止持续推送在线状态更新,而不应禁用活跃时间的追踪。因此,客户端可以通过显式请求某用户的在线状态来获取最新的活跃持续时长。
空闲超时
如果用户的最后一次活跃时间超过了设定的阈值(比如 5 分钟),服务器会自动将用户状态设置为 unavailable。客户端也可以手动将用户状态设置为 unavailable。只要用户任意一个客户端有新的活动导致活跃时间刷新,服务器会自动将其在线状态设置为 online。
安全性注意事项
在线状态信息会与所有目标用户的房间成员共享。在大型公共房间中,这种共享可能并不理想。
内容库
内容库(或称“媒体库”)允许用户将文件上传到其本地通信服务器(homeserver)以供日后使用。例如,用户想要发送到房间的文件会上传至此,用户想要使用的头像也同样如此。
上传文件时,客户端会向用户本地通信服务器的特定资源发送 POST 请求,该资源会返回一个 mxc:// URI,供日后通过 GET 请求进行下载。内容的下载则是从接收方的本地通信服务器完成,如果内容不是本地的,必须先通过同样的 API 从源通信服务器转存内容(除非源通信服务器和目的通信服务器为同一台服务器)。
在提供内容服务时,服务器应当提供 Content-Security-Policy 头。推荐的安全策略为:
sandbox; default-src 'none'; script-src 'none'; plugin-types application/pdf; style-src 'unsafe-inline'; object-src 'self';。
[Added in v1.4] 当服务器向客户端提供内容时,还应当额外提供 Cross-Origin-Resource-Policy: cross-origin 头,以便(Web)客户端能够访问如 SharedArrayBuffer 等受限 API,在与媒体库进行交互时使用。
[Changed in v1.11] 未认证的下载端点已被弃用,建议使用新的、需要认证的端点。此更改包括将所有媒体端点的路径从 /_matrix/media/* 更新为 /_matrix/client/{version}/media/*,但 /upload 和 /create 端点除外。这两个上传/创建端点预计将在之后的版本中进行类似的迁移。
Matrix 内容(mxc://)URI
内容的位置以 Matrix 内容(mxc://)URI 形式表示。格式如下:
mxc://<server-name>/<media-id>
<server-name> :内容最初上传所在的通信服务器名称,例如 matrix.org
<media-id> :用以标识内容的不透明 ID。
客户端行为
客户端可通过以下端点访问内容库。
[Changed in v1.11] / _matrix/media 层级下的多个端点已弃用并被新的、需要认证的端点取代。已弃用端点在下文中有注明。
在 Matrix 1.12 版本,服务器应当“冻结”已弃用的非认证端点,以防止新上传的媒体被下载。这意味着冻结前上传的媒体仍可通过已弃用端点访问,而冻结后(或期间)上传的媒体应当只能通过新的认证端点访问。对于远程媒体,“新上传”由缓存填充时间决定。这意味着媒体本身可能早于冻结,但因服务器需重新下载,所以被视为“新媒体”。
客户端应当在服务器冻结非认证访问之前,更新以支持已认证的端点。
服务器在冻结前应充分考虑其本地生态影响。例如,确保用户常用客户端支持新端点,或者更新桥接程序以使用媒体代理等。
此外,用于 m.login.sso 流程的 IdP 图标 应从冻结中排除。详情请参阅 m.login.sso 流程的架构说明。
服务器示例时间表如下:
- Matrix 1.11 发行:客户端开始支持认证媒体访问。
- Matrix 1.12 发行:服务器冻结非认证媒体访问。
- 此前上传的媒体仍可通过弃用端点访问。
- 新上传(或缓存)的媒体仅能通过认证端点访问。
预计 Matrix 1.12 将于 2024 年 7~9 月正式发布。
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
| 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
| Name | Type | Description |
|---|---|---|
mediaId |
string |
Required: The media ID from the |
serverName |
Server Name |
Required: The server name from the |
| 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 |
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
| Name | Type | Description |
|---|---|---|
Content-Disposition |
string |
Required: The disposition
of the returned content. MUST be one of If the If the upload was made with a Changed in |
Content-Type |
string |
Required: The content type of the file that was previously uploaded. The server MUST return a
Actions not in the spirit of the above are not considered “reasonable”. Changed in |
| Content-Type | Description |
|---|---|
application/octet-stream |
Required. The bytes for the uploaded file. |
429 response
| 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
| 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
| 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
| Name | Type | Description |
|---|---|---|
fileName |
string |
Required: A filename to give in the |
mediaId |
string |
Required: The media ID from the |
serverName |
Server Name |
Required: The server name from the |
| 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 |
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
| Name | Type | Description |
|---|---|---|
Content-Disposition |
string |
Required: The disposition
of the returned content. MUST be one of If the Changed in |
Content-Type |
string |
Required: The content type of the file that was previously uploaded. The server MUST return a
Actions not in the spirit of the above are not considered “reasonable”. Changed in |
| Content-Type | Description |
|---|---|
application/octet-stream |
Required. The bytes for the uploaded file. |
429 response
| 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
| 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
| 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
| 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 |
{
"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
| 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
| Name | Type | Description |
|---|---|---|
mediaId |
string |
Required: The media ID from the |
serverName |
Server Name |
Required: The server name from the |
| Name | Type | Description |
|---|---|---|
animated |
boolean |
Indicates preference for an animated thumbnail from the server, if possible. Animated
thumbnails typically use the content types When Servers SHOULD prefer to return When Added in |
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: |
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 |
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
| Name | Type | Description |
|---|---|---|
Content-Disposition |
string |
Required: The disposition
of the returned content. MUST be 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 |
Content-Type |
string |
Required: The content type of the thumbnail. One of: Changed in |
| Content-Type | Description |
|---|---|
image/apng |
Required. The bytes for the animated thumbnail. Added in |
image/gif |
Required. The bytes for the animated thumbnail. Added in |
image/jpeg |
Required. The bytes for the thumbnail. |
image/png |
Required. The bytes for the thumbnail. The thumbnail MAY use an animated
format if Changed in |
image/webp |
Required. The bytes for the animated thumbnail. Added in |
400 response
| 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
| 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
| 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
| 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
| 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 |
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
| 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
| 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
Replaced by GET /_matrix/client/v1/media/config.
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
| 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}
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
| Name | Type | Description |
|---|---|---|
mediaId |
string |
Required: The media ID from the |
serverName |
Server Name |
Required: The server name from the |
| 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 Added in |
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 |
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 |
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
| Name | Type | Description |
|---|---|---|
Content-Disposition |
string |
Required: The disposition
of the returned content. MUST be one of If the If the upload was made with a Changed in |
Content-Type |
string |
Required: The content type of the file that was previously uploaded. The server MUST return a
Actions not in the spirit of the above are not considered “reasonable”. Changed in |
| Content-Type | Description |
|---|---|
application/octet-stream |
Required. The bytes for the uploaded file. |
429 response
| 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
| 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
| 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}
Replaced by GET /_matrix/client/v1/media/download/{serverName}/{mediaId}/{fileName}
(requires authentication).
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
| Name | Type | Description |
|---|---|---|
fileName |
string |
Required: A filename to give in the |
mediaId |
string |
Required: The media ID from the |
serverName |
Server Name |
Required: The server name from the |
| 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 Added in |
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 |
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 |
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
| Name | Type | Description |
|---|---|---|
Content-Disposition |
string |
Required: The disposition
of the returned content. MUST be one of If the Changed in |
Content-Type |
string |
Required: The content type of the file that was previously uploaded. The server MUST return a
Actions not in the spirit of the above are not considered “reasonable”. Changed in |
| Content-Type | Description |
|---|---|
application/octet-stream |
Required. The bytes for the uploaded file. |
429 response
| 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
| 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
| 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
Replaced by GET /_matrix/client/v1/media/preview_url.
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
| 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 |
{
"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
| 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}
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
| Name | Type | Description |
|---|---|---|
mediaId |
string |
Required: The media ID from the |
serverName |
Server Name |
Required: The server name from the |
| 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 Added in |
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 |
animated |
boolean |
Indicates preference for an animated thumbnail from the server, if possible. Animated
thumbnails typically use the content types When Servers SHOULD prefer to return When Added in |
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: |
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 |
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
| Name | Type | Description |
|---|---|---|
Content-Disposition |
string |
Required: The disposition
of the returned content. MUST be 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 |
Content-Type |
string |
Required: The content type of the thumbnail. One of: Changed in |
| Content-Type | Description |
|---|---|
image/apng |
Required. The bytes for the animated thumbnail. Added in |
image/gif |
Required. The bytes for the animated thumbnail. Added in |
image/jpeg |
Required. The bytes for the thumbnail. |
image/png |
Required. The bytes for the thumbnail. The thumbnail MAY use an animated
format if Changed in |
image/webp |
Required. The bytes for the animated thumbnail. Added in |
400 response
| 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
| 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
| 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
| 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
| 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
| Name | Type | Description |
|---|---|---|
Content-Type |
string |
Optional. The content type of the file being uploaded. Clients SHOULD always supply this header. Defaults to Changed in |
| 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:
|
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 |
{
"content_uri": "mxc://example.com/AQwafuaFswefuhsfAFAgsw"
}
403 response
| 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
| 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
| 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
| Name | Type | Description |
|---|---|---|
Content-Type |
string |
Optional. The content type of the file being uploaded. Clients SHOULD always supply this header. Defaults to Changed in |
| Name | Type | Description |
|---|---|---|
mediaId |
string |
Required: The media ID from the |
serverName |
Server Name |
Required: The server name from the |
| 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:
A standard error response
will be returned with the |
404 |
The user has provided an invalid MXC ID. Some reasons for this error include:
A standard error response
will be returned with the |
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
| 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
| 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
| 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
| 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
| 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
}
缩略图
通信服务器应能够为上传的图片和视频生成缩略图。当前尚未规定支持生成缩略图的确切文件类型,详见 Issue #1938。
缩略图方法分为“裁剪(crop)”和“缩放(scale)”。“scale”方法会返回宽度或高度小于请求尺寸的图片。客户端若需适配特定矩形尺寸,则需进一步缩放和留白补齐。“crop”方法则尽量返回与请求尺寸相近、并且保持请求尺寸宽高比的图片。客户端如需适配,也应进一步缩放图片以适配目标矩形。
传给缩略图 API 的尺寸为客户端期望的最小尺寸。服务器不得返回比客户端请求尺寸更小的缩略图,除非原始内容本身尺寸就小于请求尺寸。若原始内容尺寸较小,服务器应直接返回原始内容,而非额外生成缩略图。
服务器应生成以下尺寸、方法的缩略图:
- 32x32,裁剪
- 96x96,裁剪
- 320x240,缩放
- 640x480,缩放
- 800x600,缩放
简要总结:
- “缩放”会保持原始图片的宽高比
- “裁剪”会输出与请求尺寸宽高比一致的图片
- 服务器会尽量返回尺寸大于等于请求值的图片
服务器在任何情况下都不得对缩略图进行放大。除非原始内容尺寸不足,否则服务器也不得返回小于请求尺寸的缩略图。
安全性考量
HTTP GET 端点无需认证。只要知道内容的 URL,无论实体是否在房间内都可获取内容。
mxc:// URI 易遭目录遍历攻击,如 mxc://127.0.0.1/../../../some_service/etc/passwd。这可能导致目标通信服务器试图访问并返回该文件。因此,通信服务器必须对 mxc:// URI 做合法性检查,仅允许在 server-name 和 media-id 中出现字母(A-Za-z)、数字(0-9)、下划线 _ 和短横线 -。该白名单字符集兼容 RFC 4648 规范的 URL 安全 base64 编码。采用白名单优于黑名单(如黑名单排除 . 和 /),因为黑名单可能被漏洞(如百分号编码、UTF-8 编码遍历等)绕过。
通信服务器的内容服务还有额外的内容安全注意事项:
- 客户端可能上传超大型文件。通信服务器应拒绝保存过大的文件,并返回 HTTP 413 且错误码为
M_TOO_LARGE。 - 客户端可能上传超大图片。通信服务器应拒绝为超大图片生成缩略图,返回 HTTP 413 并附带
M_TOO_LARGE。 - 远程通信服务器可能托管超大文件或图片。通信服务器应拒绝中转或生成超大远程文件/图片的缩略图,返回 HTTP 502 并附带
M_TOO_LARGE。 - 客户端可能尝试上传过多文件。通信服务器应限制单个客户端上传的文件数量与总大小,超过限制时返回 HTTP 403 并附带
M_FORBIDDEN。 - 客户端可能通过通信服务器访问大量远程文件。通信服务器应限制缓存的远程媒体的数量与总大小。
- 客户端或远程通信服务器可能上传包含针对通信服务器缩略图机制或客户端解码器漏洞的恶意文件。
内联内容服务
若客户端配置不安全,在以 Content-Disposition: inline 方式分发媒体时,可能会受到跨站脚本攻击。为降低大部分风险,客户端应避免与通信服务器的媒体端点部署在同一域名下。服务器应当在内联内容服务时,将 Content-Type 限定为以下值之一:
text/csstext/plaintext/csvapplication/jsonapplication/ld+jsonimage/jpegimage/gifimage/pngimage/apngimage/webpimage/avifvideo/mp4video/webmvideo/oggvideo/quicktimeaudio/mp4audio/webmaudio/aacaudio/mpegaudio/oggaudio/waveaudio/wavaudio/x-wavaudio/x-pn-wavaudio/flacaudio/x-flac
在指定 Content-Type 时,上述类型不太可能引发跨站脚本风险,因为客户端只会按此类型渲染数据。例如上传 HTML 文件但指定类型为 image/png 时,客户端只会认为图片损坏,并不会作为 HTML 页面渲染。因此,只要 Content-Disposition 根据该类型正确计算,服务器即可信任用户定义的内容类型。
客户端不应依赖于服务器必定在 /download 接口返回 inline 而非 attachment。服务器实现可能出于安全考量将所有下载均返回 attachment,无论内容类型为何——客户端不应对此举感到意外。
设备间消息发送(Send-to-Device messaging)
本模块为客户端提供了一种交换信令消息的方式,这些消息不会作为共享通信历史的一部分被永久存储。每条消息将精确地送达每个客户端设备一次。
该 API 的主要动机是用于交换在房间有向无环图(DAG)中无意义或不希望持久保存的数据——例如,一次性身份验证令牌或密钥数据。它并不适用于会话内容,会话内容应使用常规的 /rooms/<room_id>/send API 发送,以保持 Matrix 协议的一致性。
客户端行为
要向其他设备发送消息,客户端应调用 /sendToDevice。每个事务中,每台设备只能发送一条消息,且所有消息的事件类型必须一致。请求体中的设备ID可以设置为 *,以请求将消息发送到所有已知设备。
如果有等待客户端接收的设备间消息,这些消息将通过 /sync 返回,详见对 /sync 的扩展。客户端应检查每个返回事件的 type 字段,并忽略其无法识别的事件。
服务器行为
服务器应为本地用户存储待发送消息,直到它们成功送达目标设备。当客户端使用与有待发送消息的设备相关联的访问令牌调用 /sync 时,服务器应按到达顺序在响应体中列出这些待发送消息。
当客户端再次使用第一次响应中的 next_batch 令牌调用 /sync 时,服务器应推断该响应中的所有设备间消息已成功送达,并从存储中删除它们。
如果有大量待发送的设备间消息,服务器应限制在每次 /sync 响应中发送的消息数量。建议合理的上限为 100 条消息。
如果客户端向远程域的用户发送消息,这些消息应通过联邦协议(federation)发送至远程服务器。
协议定义
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
| 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
| 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 |
Request body example
{
"messages": {
"@alice:example.com": {
"TLLBEANAAG": {
"example_content_key": "value"
}
}
}
}
Responses
| Status | Description |
|---|---|
200 |
The message was successfully sent. |
200 response
{}
对 /sync 的扩展
本模块在 /sync 响应中增加了以下属性:
| 参数 | 类型 | 说明 |
|---|---|---|
| to_device | ToDevice | 可选。关于本客户端设备的设备间消息(send-to-device)信息。 |
ToDevice
| 参数 | 类型 | 说明 |
|---|---|---|
| events | [Event] | 设备间消息列表。 |
Event
| 参数 | 类型 | 说明 |
|---|---|---|
| content | EventContent | 本事件的内容。此对象中的字段将根据事件类型有所不同。 |
| sender | string | 发送本事件的 Matrix 用户ID。 |
| type | string | 事件类型。 |
示例响应:
{
"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"]
}
}
]
}
}
设备管理
本模块为用户提供了管理其设备的方式。
客户端行为
实现此模块的客户端应为用户提供已注册设备的列表,并允许用户更新设备的显示名称。客户端还应允许用户删除不再使用的设备。
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. |
| 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 |
| <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
| 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. |
| 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. |
| 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
| 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
| 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
| 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
| 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. |
| 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 |
| <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
| 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. |
| 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"
}
安全性注意事项
删除设备具有安全风险:该操作会使分配给该设备的 access_token 失效,因此攻击者可能利用这一点注销真实用户(并且每当真实用户尝试重新登录时,攻击者都可以重复进行此操作,从而阻止用户登录)。服务器在删除设备时应要求超出 access_token 的额外身份验证措施(例如,要求用户重新输入密码)。
设备的显示名称为公开可见内容。客户端应考虑提醒用户这一点。
端到端加密
Matrix 可选支持端到端加密,允许创建房间,其对话内容无法被任何参与的主服务器解密或截获。
密钥分发
Matrix 中的加密与身份验证基于公钥密码学。Matrix 协议提供了一个基础的公钥交换机制,但需要通过带外渠道在用户之间交换指纹,以建立信任网络。
概述
1)Bob 发布其设备的公钥和支持的算法。这可能包括长期身份密钥和/或一次性密钥。
+----------+ +--------------+
| Bob 的主服务器 | | Bob 的设备 |
+----------+ +--------------+
| |
|<=============|
/keys/upload
2)Alice 请求 Bob 的公用身份密钥和支持的算法。
+----------------+ +------------+ +----------+
| Alice 的设备 | | Alice 的主服务器 | | Bob 的主服务器 |
+----------------+ +------------+ +----------+
| | |
|=================>|==============>|
/keys/query <federation>
3)Alice 选择一种算法并领取所需的一次性密钥。
+----------------+ +------------+ +----------+
| Alice 的设备 | | Alice 的主服务器 | | Bob 的主服务器 |
+----------------+ +------------+ +----------+
| | |
|=================>|==============>|
/keys/claim <federation>
密钥算法
不同的密钥算法用于不同的用途。每种密钥算法通过名称标识,并有特定的表示方式。
名称 ed25519 对应于 Ed25519 签名算法。密钥为一个 32 字节的 Ed25519 公钥,使用 unpadded Base64 编码。例如:
"SogYyrkTldLz0BXP+GYWs0qaYacUI0RleEqNT8J3riQ"
名称 curve25519 对应于 Curve25519 ECDH 算法。密钥为一个 32 字节的 Curve25519 公钥,使用 unpadded Base64 编码。例如:
"JGLn/yafz74HB2AbPLYJWIVGnKAtqECOBf11yyXac2Y"
名称 signed_curve25519 也对应于 Curve25519 ECDH 算法,但该密钥经过签名,可以进行身份认证。使用此算法的密钥以包含以下属性的对象表示:
KeyObject
| 参数 | 类型 | 描述 |
|---|---|---|
| key | string | 必选。 未填充的 Base64 编码的 32 字节 Curve25519 公钥。 |
| signatures | Signatures | 必选。 密钥对象的签名。签名过程详见 Signing JSON。 |
| fallback | boolean | 标记此密钥是否为 备用密钥。默认为 false。 |
示例:
{
"key":"06UzBknVHFMwgi7AVloY7ylC+xhOhEX4PkNge14Grl8",
"signatures": {
"@user:example.com": {
"ed25519:EGURVBUNJP": "YbJva03ihSj5mPk+CHMJKUKlCXCPFXjXOK6VqBnN9nA2evksQcTGn6hwQfrgRHIDDXO2le49x7jnWJHMJrJoBQ"
}
}
}
ed25519 和 curve25519 密钥用于 设备密钥。此外,ed25519 密钥也用于 跨签名密钥。
signed_curve25519 密钥用于 一次性及备用密钥。
设备密钥
每台设备应拥有一个 Ed25519 签名密钥。此密钥应通过设备上的加密安全源生成,且其私钥部分绝不可导出。其他客户端通过该密钥作为设备指纹,并由该密钥签署设备的其他密钥。
设备通常还需要生成若干附加密钥,具体取决于使用的消息算法。
在 Olm 1.0 版本中,每台设备还需有一个 Curve25519 身份密钥。
一次性与备用密钥
除了设备密钥(属于长期密钥),某些加密算法还要求设备拥有多个一次性密钥,这些密钥仅使用一次,用后即弃。在 Olm 1.0 版本中,设备使用由设备 Ed25519 密钥签名的 signed_curve25519 一次性密钥。
设备会生成一次性密钥上传至服务器。其他用户之后会通过 claim 领取。服务器必须确保每个一次性密钥只被领取一次:主服务器在将该一次性密钥分配给其他用户后即应删除。
[Added in v1.2] 备用密钥与一次性密钥类似,但使用后不会被消耗。如果上传了备用密钥,当设备用尽一次性密钥、用户请求密钥时,服务器会返回备用密钥。备用密钥一旦被使用,应尽快用新密钥替换。
备用密钥用于在设备离线或无法上传新密钥时防止一次性密钥耗尽。但使用备用密钥建立的会话可能易受重放攻击。
设备将通过 /sync 得知尚可被领取的一次性密钥数量,以及备用密钥是否已被使用。这样设备即可保障在线期间有充足一次性密钥,并在备用密钥被用后及时更换。
密钥上传
设备通过 /keys/upload API 将身份密钥的公钥部分以签名 JSON 对象形式上传至主服务器。JSON 对象必须包含设备 Ed25519 密钥的公钥部分,且必须由该密钥签名,详见 Signing JSON。
一次性密钥和备用密钥也通过 /keys/upload API 上传。如需上传新的一次性或备用密钥则再上传。备用密钥(格式为签名 JSON 对象的密钥算法)应包含名为 fallback 且值为 true 的属性。
设备必须保存他们上传的每个密钥的私钥部分。当收到使用该密钥加密的信息后,可以删除一次性密钥的私钥部分。但有可能主服务器发出的某个一次性密钥从未被用过,因此生成密钥的设备永远也不会知道可以删除该密钥。因此,设备最终可能需要保管过多私钥。若存储私钥过多,设备可以从最早的密钥开始丢弃。
客户端不应无限期保存备用密钥的私钥,以免攻击者能解密此前用该备用密钥加密的消息。
客户端最多应该只保存 2 个备用密钥的私钥:当前未用的备用密钥及其前一个。一旦客户端确信所有用旧备用密钥加密的消息都已收到(如自第一条消息后经过一小时),应删除该备用密钥。
跟踪用户的设备列表
在 Alice 向 Bob 发送加密消息之前,需要获得 Bob 每台设备及相关身份密钥的列表,以建立这些设备的加密会话。此列表可通过 /keys/query 并在 device_keys 参数中传入 Bob 用户 ID 获得。
Bob 可能会不时添加新设备,Alice 需要及时获知并在今后的加密消息中加上新的设备。最简单做法是每发一次消息都执行一次 /keys/query,但用户数和设备数可能庞大,如此会极度低效。
因此,通常每个客户端会维护一些用户(实践中通常是与之共享加密房间的用户)的设备列表。该列表必须在客户端应用程序多次启动间持久化存储(以保留设备验证数据并在 Bob 突然添加新设备时警告 Alice)。
Alice 的客户端可按以下流程维护 Bob 的设备列表:
- 首先设置标记,记录现在正在跟踪 Bob 的设备列表,并有单独标记表示本地 Bob 设备列表已过期。两者都必须保存在客户端重启后依然存在的存储中。
- 向
/keys/query发送请求,device_keys参数传入 Bob 用户 ID。请求完成后,将获取到的设备列表持久化保存,并清除“已过期”标记。 - 正常处理
/sync响应时,解析device_lists字段的changed属性。如果在跟踪于列出的用户之一的设备列表,则将该用户的设备列表标记为过期,并重发/keys/query请求。 - 定期将
/sync结果中的next_batch字段保存持久化。如果 Alice 后续重启客户端,可通过/keys/changes并将记录的next_batch字段作为from参数查询在离线期间哪些用户的设备列表已变更。如果在跟踪这些用户的设备列表,则将其标记为过期。将此列表与之前已标记过期的列表并集,然后对这些用户重新执行/keys/query。
Bob 可能在 Alice 的 /keys/query 请求仍在进行时更新设备。Alice 的客户端可能会在第一次请求未结束时,在 /sync 响应的 device_lists 字段中看到 Bob 的用户 ID,并发起第二次 /keys/query 请求。这可能导致两类相关问题。
第一个问题:当第一次请求响应时,客户端会清除记录的 Bob 设备已过期标记。如果第二次请求失败或在完成前客户端关闭,可能导致 Alice 继续使用已过期的 Bob 设备列表。
第二个问题:在某些条件下,第二次请求可能早于第一次请求完成。此时第一次请求的结果会覆盖第二次的结果。
客户端必须防范上述情况。例如,可以确保每个用户至多只有一个 /keys/query 请求在运行,对于新的请求排队等待第一个完成。或立即发起新请求,但确保忽略第一次请求的结果(例如通过取消第一次请求)。
当 Bob 和 Alice 共享房间并跟踪 Alice 设备时,Alice 离开房间后新增设备,Bob 不会知晓。当他们再次共享房间时,Bob 就有一个过时的 Alice 设备列表。为了解决该问题,Bob 的主服务器会把 Alice 的用户 ID 加入 device_lists 字段的 changed 属性中,因此 Bob 会按常规流程更新 Alice 的设备列表。另外,Bob 也可通过检查 device_lists 字段的 left 属性获知双方已不再共享任何房间,并相应地将 Alice 从跟踪列表移除。
发送加密附件
当房间启用加密时,文件应在上传到主服务器前进行加密。
操作方式为,客户端生成一个只用一次的 256 位 AES 密钥并使用 AES-CTR 模式加密该文件。计数器(Counter)应为 64 位,从 0 开始,且以随机 64 位初始化向量(IV)为前缀,两者一起组成 128 位唯一计数器块。
IV 绝不可与同一密钥重复使用。这意味着如需加密同一消息中的多个文件(如图片和缩略图),绝不可共用密钥及 IV。
加密后,文件即可上传到主服务器。必须在房间事件中包含密钥和 IV 以及上传得到的 mxc://,以便接收方可以解密。由于承载这些密钥和 IV 的事件会用 Megolm 加密,服务器无法解密文件。
还必须包含密文的哈希值,以防主服务器篡改文件内容。
客户端应以加密后的 m.room.message 事件形式发送数据,msgtype 可用 m.file,或其他对应文件类型的 msgtype。密钥采用 JSON Web Key 格式,带 W3C 扩展。
对 m.room.message 的扩展
本模块为引用文件的 m.room.message 消息类型添加了 file 和 thumbnail_file 属性,类型为 EncryptedFile,用于替代 url 和 thumbnail_url 属性,如 m.file 和 m.image 等。
EncryptedFile
| 参数 | 类型 | 描述 |
|---|---|---|
| url | string | 必选。 文件的 URL。 |
| key | JWK | 必选。 一个 JSON Web Key 对象。 |
| iv | string | 必选。 AES-CTR 使用的 128 位唯一计数块,未填充 base64 编码。 |
| hashes | {string: string} | 必选。 算法名称与密文哈希的映射,未填充 base64 编码。客户端应至少支持 sha256 哈希。 |
| v | string | 必选。 加密附件协议版本,必须为 v2。 |
JWK
| 参数 | 类型 | 描述 |
|---|---|---|
| kty | string | 必选。 密钥类型,必须为 oct。 |
| key_ops | [string] | 必选。 密钥操作。至少包含 encrypt 和 decrypt。 |
| alg | string | 必选。 算法,必须为 A256CTR。 |
| k | string | 必选。 密钥,以 urlsafe 未填充 base64 编码。 |
| ext | boolean | 必选。 可提取性。必须为 true。此为 W3C 扩展。 |
示例:
{
"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
}
}
设备验证
在 Alice 向 Bob 发送加密数据、或信任从 Bob 收到的数据之前,或许想先验证自己真正与 Bob 本人通信,而非与中间人。此验证过程需要带外渠道:仅依赖 Matrix 自身无法做到完全安全验证(否则就只能信任主服务器管理员了)。
在 Matrix 中,验证流程通常是 Alice 与 Bob 线下见面或通过其他可靠方式交流,采用下文中定义的任一种互动验证方式校验 Bob 的设备。Alice 和 Bob 也可以当面逐字读出各自的未填充 base64 编码 Ed25519 公钥(如 /keys/query 返回值)。
设备验证可能有多种结论。例如:
- Alice 可以“接受”设备,表明她确信设备属于 Bob,今后可以为该设备加密敏感内容,并确认接收到的消息确实由该设备发出。
- Alice 可以“拒绝”设备,如果她知道或怀疑该设备非 Bob 控制(如也不信任 Bob),则不会发送敏感内容、不再信任该设备发来的消息。
- Alice 可以跳过设备验证流程。这种情况下虽然无法确信设备属于 Bob,但也没有证据怀疑,且加密协议依然能够防止被动窃听。
一旦签名密钥通过验证,就由加密协议去验证具体消息是否真由持有该 Ed25519 私钥的设备发出,或仅允许该设备解密。Olm 协议的具体文档见 https://matrix.org/docs/olm_signing.html。
密钥验证框架
人工比对 Ed25519 密钥并不友好,易出错。为降低错误概率、提升用户体验,本规范支持一些验证方式,并允许通过用户设备间互发消息辅助验证。这些方法共用一套验证协商框架。
验证消息可以发在由双方共享的房间(应为 定向消息),也可通过 to-device 消息点对点直发两个设备。两种场景下报文结构类似,细微差别详见下文。验证不同用户应用房间消息,同一用户多个设备验证则用 to-device 消息。
一个密钥验证会话以首条消息中的 ID 标识。房间消息用初始 message 的 event ID,to-device 消息用首条消息的 transaction_id 字段,其余同会话消息继承。
一般来说,验证流程如下:
- Alice 通过发送密钥验证请求事件,向 Bob 发起验证请求。如果在房间,事件类型为
m.room.message且msgtype: m.key.verification.request,如用 to-device 则类型为m.key.verification.request。事件指明 Alice 客户端支持的验证方式。(注意“Alice”与“Bob”在自验证场景其实可以是同一用户)。 - Bob 客户端弹窗提示 Bob 接受密钥验证。Bob 接受后,其客户端发送
m.key.verification.ready事件,载明其支持的验证方式。 - Alice 或 Bob 选择双方都支持的一个验证方式后,主设备发送
m.key.verification.start事件,标明所选验证方式。如果双方仅有唯一通用方式,则可自动选择。 - Alice 和 Bob 按所选验证方式完成验证,可能涉及客户端间消息交互,双方在带外渠道核对信息,或与各自设备交互。
- Alice 和 Bob 客户端各自发送
m.key.verification.done通知验证成功。
任一设备可随时通过发送 m.key.verification.cancel 事件取消验证,请求体里的 code 字段指明原因。
to-device 场景下,Alice 若无法确定要验证 Bob 的哪台设备或不想指定特定设备,可以向 Bob 所有设备发 m.key.verification.request,事务 ID 相同。当 Bob 某台设备接受或拒绝(发 m.key.verification.ready 或 m.key.verification.cancel)时,Alice 要向 Bob 其他设备发送 m.key.verification.cancel(若 Bob 接受则 code 为 m.accepted,拒绝则为 m.user)。假定 Alice、Bob 各有两台设备,Bob 第一台设备接受 Alice 第二台设备的请求,协议流程如下(注意,Alice 第一个设备未参与验证,消息次序 Bob、Alice 谁先发 m.key.verification.start 都可以;最终双方互发 done 通知):
+---------------+ +---------------+ +-------------+ +-------------+
| 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 | |
| |---------------------------------->| |
| | | |
房间消息下,Alice 只需向房间发送一次请求事件(类型为 m.room.message 且 msgtype: m.key.verification.request),而非向 Bob 各设备分别发送 m.key.verification.request。一旦 Bob 某设备发出 m.key.verification.ready,其他 Bob 设备即可知有设备已接受,自动忽略该请求,无须 Alice 主动通知。
房间加密模式下,验证流程不得因加密受阻。例如:若验证消息本身用加密,务必确保收件人所有未经验证的设备都能获得解密该消息的密钥(即使通常不会给他们解密密钥)。或者,也可将验证消息设为明文(但并不推荐)。
Bob 某设备收到 Alice 的 m.key.verification.request 且所有方法都不支持时,不应直接取消请求,以免其他设备支持。应提示 Bob 无支持方式,允许其手动拒绝。
提示 Bob 接受/拒绝 Alice 请求(或“不支持”提示)应在 timestamp(to-device)或 origin_ts(房间消息)后 10 分钟,或收到消息后 2 分钟(以先到者为准)自动消失,期间若收到 m.key.verification.cancel 则自动隐藏。
Bob 若拒绝请求,客户端需发送 code 为 m.user 的 m.key.verification.cancel,Alice 应知晓被拒绝,并若是 to-device 消息类型,还要通知 Bob 的所有设备该请求已被拒绝。
Alice/Bob 若同时发出 m.key.verification.start 且指定同一验证方式,用户 ID 字典序较大的设备消息被忽略,视为只有较小字典序一端发起。若用户 ID 相同(即自验证),则比对设备 ID。若指定验证方式不一致,则以 code: m.unexpected_message 取消。
to-device 验证也可无请求直接发 m.key.verification.start,该行为已废弃,建议新客户端不要这样做,但应能兼容老客户端。
具体验证方式可能会添加额外步骤、事件及消息字段。定义于本规范下的方法事件类型须以 m.key.verification 命名空间为前缀,其他自定义事件按 Java 包命名原则命名。
m.room.message with msgtype: m.key.verification.request
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 |
string |
The formatted version of the |
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: |
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 |
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
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
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
|
methods |
[string] |
Required: The verification methods supported by the sender, corresponding to
the verification methods indicated in the
|
transaction_id |
string |
Required when sent as a to-device message. The transaction ID of the
verification request, as given in the |
| Name | Type | Description |
|---|---|---|
event_id |
string |
The event ID of the |
rel_type |
string |
The relationship type. Currently, this can only be an
One of: |
Examples
{
"content": {
"from_device": "BobDevice1",
"methods": [
"m.sas.v1"
],
"transaction_id": "S0meUniqueAndOpaqueString"
},
"type": "m.key.verification.ready"
}
m.key.verification.start
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
|
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 |
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 |
| Name | Type | Description |
|---|---|---|
event_id |
string |
The event ID of the |
rel_type |
string |
The relationship type. Currently, this can only be an
One of: |
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
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
|
transaction_id |
string |
Required when sent as a to-device message. The opaque identifier for the verification process/request. |
| Name | Type | Description |
|---|---|---|
event_id |
string |
The event ID of the |
rel_type |
string |
The relationship type. Currently, this can only be an
One of: |
Examples
{
"content": {
"transaction_id": "S0meUniqueAndOpaqueString"
},
"type": "m.key.verification.done"
}
m.key.verification.cancel
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:
Clients should be careful to avoid error loops. For example, if a device sends
an incorrect message and the client returns |
m.relates_to |
VerificationRelatesTo |
Required when sent as an in-room message. Indicates the
|
reason |
string |
Required: A human readable description of the |
transaction_id |
string |
Required when sent as a to-device message. The opaque identifier for the verification process/request. |
| Name | Type | Description |
|---|---|---|
event_id |
string |
The event ID of the |
rel_type |
string |
The relationship type. Currently, this can only be an
One of: |
Examples
{
"content": {
"code": "m.user",
"reason": "User rejected the key verification request",
"transaction_id": "S0meUniqueAndOpaqueString"
},
"type": "m.key.verification.cancel"
}
短认证字符串(SAS)验证
SAS 验证是一种基于上述通用框架的用户友好密钥验证流程。其高度交互化,使用户容易参与其中。
验证过程受 Phil Zimmermann 的 ZRTP 密钥协商启发。其关键在于哈希承诺(commitment):发起 Diffie-Hellman 的一方先发送自己部分的哈希,只有收到对方部分才发明文。这样,攻击者只会有一次机会破解 DH,实际上即便只校验 n 位也能保证高安全性:若认证 n 位,攻击者成功概率为 1/2ⁿ。如验证 40 位,攻击概率小于十万亿分之一,若失败,两端的短认证字符串会不一致,从而提醒用户遭遇攻击。
要支持该方式,客户端应在 m.key.verification.request 和 m.key.verification.ready 的 methods 字段中声明名称 m.sas.v1。
SAS 验证主要两步:
- 密钥协商(类似 ZRTP 协商)。
- 密钥认证(基于 HMAC)。
Alice 和 Bob 互验证流程如下:
-
Alice 和 Bob 建立安全的带外连接,如线下、视频通话等。(“安全”即无可被冒充,而非完全机密。)
-
双方启动密钥验证流程。
-
Alice 设备向 Bob 设备发
m.key.verification.start,确保 Bob 的设备密钥已本地存储。 -
Bob 设备收到后,选定双方均支持的密钥协商、哈希、MAC、SAS 算法。
-
Bob 设备确保 Alice 设备密钥也齊。
-
Bob 设备生成临时 Curve25519 密钥对 (K_B^private, K_B^public),计算公钥哈希。
-
Bob 回复
m.key.verification.accept,传递承诺。 -
Alice 保存承诺哈希备用。
-
Alice 设备生成临时 Curve25519 密钥对 (K_A^private, K_A^public), 以
m.key.verification.key只发送公钥。 -
Bob 回复
m.key.verification.key,含其公钥。 -
Alice 校验 Bob 设备发来的密钥哈希与早先的承诺及 Alice 启动消息内容一致。
-
双方用自己的私有临时密钥与对方的公钥做 ECDH,得共享密钥。
-
双方用选定算法从共享密钥派生 SAS(短认证字符串)。如有多种方式,客户端应允许用户选择。
-
两端人工比对 SAS,手动告知设备是否一致。
-
若认证一致,双方各自针对下述密钥做 MAC 认证:
- 希望对方验证的所有密钥(通常是各自设备 ed25519 密钥及主跨签名密钥)。
- 希望对方验证密钥的 key ID 列表。
MAC 算法见下文。
-
双方并行发送
m.key.verification.mac,各自附上密钥及 ID 的 MAC 值。 -
对端收到
m.key.verification.mac后,本地核算 MAC 进行比对,一致则设备密钥通过认证。 -
双方各自发送
m.key.verification.done完成验证。
Alice/Bob 设备间协议交互如下:
+-------------+ +-----------+
| 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 |
|<--------------------------------|
| |
错误与异常处理
过程中出现错误处理如下:
- Alice 或 Bob 任何时刻都可取消验证。须发送
m.key.verification.cancel。 - 超时。验证流程超时设为 10 分钟,事务 ID 长时间(10 分钟未收发)未用也需过期,用户应看到超时提示,同时通知对方取消。
- 一台设备参与多次验证,接收端需取消与该设备的所有未完成尝试。
- 若收到未知事务 ID,须给对方发送说明原因的
m.key.verification.cancel(首次收到m.key.verification.start或m.key.verification.cancel除外)。 - 若双方无共同密钥、哈希、HMAC 或 SAS 算法,则发錯誤
m.key.verification.cancel。 - 用户主观判定 SAS 不一致时,需发送
m.key.verification.cancel。 - 若收到意外次序的消息,应通知对方并取消。
SAS 验证专用消息
通用验证框架基础上,SAS 用到如下事件。
m.key.verification.cancel 事件无需变更,但新增以下错误码:
m.unknown_method:双方无法协商出共同算法。m.mismatched_commitment:哈希承诺校验失败。m.mismatched_sas:SAS 不一致。
m.key.verification.start with method: m.sas.v1
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
|
key_agreement_protocols |
[string] |
Required: The key agreement protocols the sending device understands. Should
include at least |
m.relates_to |
VerificationRelatesTo |
Required when sent as an in-room message. Indicates the
|
message_authentication_codes |
[string] |
Required: The message authentication code methods that the sending device understands.
Must include at least |
method |
string |
Required: The verification method to use. One of: |
short_authentication_string |
[string] |
Required: The SAS methods the sending device (and the sending device’s user)
understands. Must include at least |
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 |
| Name | Type | Description |
|---|---|---|
event_id |
string |
The event ID of the |
rel_type |
string |
The relationship type. Currently, this can only be an
One of: |
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
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 |
hash |
string |
Required: The hash method the device is choosing to use, out of the options in
the |
key_agreement_protocol |
string |
Required: The key agreement protocol the device is choosing to use, out of the
options in the |
m.relates_to |
VerificationRelatesTo |
Required when sent as an in-room message. Indicates the
|
message_authentication_code |
string |
Required: The message authentication code method the device is choosing to use, out of
the options in the |
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 |
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
|
| Name | Type | Description |
|---|---|---|
event_id |
string |
The event ID of the |
rel_type |
string |
The relationship type. Currently, this can only be an
One of: |
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
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
|
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
|
| Name | Type | Description |
|---|---|---|
event_id |
string |
The event ID of the |
rel_type |
string |
The relationship type. Currently, this can only be an
One of: |
Examples
{
"content": {
"key": "fQpGIW1Snz+pwLZu6sTy2aHy/DYWWTspTJRPyNp0PKkymfIsNffysMl6ObMMFdIJhk6g6pwlIqZ54rxo8SLmAg",
"transaction_id": "S0meUniqueAndOpaqueString"
},
"type": "m.key.verification.key"
}
m.key.verification.mac
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 |
m.relates_to |
VerificationRelatesTo |
Required when sent as an in-room message. Indicates the
|
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
|
| Name | Type | Description |
|---|---|---|
event_id |
string |
The event ID of the |
rel_type |
string |
The relationship type. Currently, this can only be an
One of: |
Examples
{
"content": {
"keys": "2Wptgo4CwmLo/Y8B8qinxApKaCkBG2fjTWB7AbP5Uy+aIbygsSdLOFzvdDjww8zUVKCmI02eP9xtyJxc/cLiBA",
"mac": {
"ed25519:ABCDEF": "fQpGIW1Snz+pwLZu6sTy2aHy/DYWWTspTJRPyNp0PKkymfIsNffysMl6ObMMFdIJhk6g6pwlIqZ54rxo8SLmAg"
},
"transaction_id": "S0meUniqueAndOpaqueString"
},
"type": "m.key.verification.mac"
}
MAC 计算
验证期间,会对密钥以及密钥 ID 列表生成 MAC。
采用的 MAC 算法由 m.key.verification.accept 的 message_authentication_code 字段决定。当前要求使用 hkdf-hmac-sha256.v2,实现如下:
-
用 HKDF(RFC5869,哈希为 SHA-256)生成 HMAC 密钥。输入为共享密钥。无盐值,info 参数为下列组合:
- 字符串
MATRIX_KEY_VERIFICATION_MAC - 被 MAC 密钥所属用户的 Matrix ID
- 发送 MAC 的设备 ID
- 另一用户的 Matrix ID
- 接收 MAC 的设备 ID
- 正在使用中的
transaction_id - 被 MAC 密钥的 Key ID,若为密钥列表则为字符串
KEY_IDS
- 字符串
-
用上述密钥和 SHA-256 按 RFC2104 做 HMAC 得 MAC 值。
若为密钥,则对密钥的公钥编码作 MAC,如
ed25519算法下为未填充 base64 格式;若为密钥 ID 列表,则须字典序排序、以逗号分隔(无额外空格),每个格式如
{algorithm}:{keyId},如:ed25519:Cross+Signing+Key,ed25519:DEVICEID。此设计保证接收方比对 mac 字段名时即可还原全体 Key ID,无新增删漏。 -
MAC 最后以 base64 编码,并发送于
m.key.verification.mac事件。
曾用的 hkdf-hmac-sha256 MAC 方法的 base64 编码错误,因 libolm 最初实现缺陷。新版本 hkdf-hmac-sha256.v2 与之算法相同,但采用标准 base64 编码。hkdf-hmac-sha256 已废弃,日后将移除。若双方都支持 hkdf-hmac-sha256.v2,则绝不可使用 hkdf-hmac-sha256。
SAS HKDF 计算
所有 SAS 方法均按 RFC5869 HKDF 标准实现,哈希算法取决于之前协商,输入为协商得的共享密钥,无盐:
当 key_agreement_protocol 为 curve25519-hkdf-sha256 时,info 参数为:
- 字符串
MATRIX_KEY_VERIFICATION_SAS| - 启动
m.key.verification.start消息用户 Matrix ID,后跟| - 启动消息设备 ID,后跟
| - 该设备发出的
m.key.verification.key公钥,未填充 base64,后跟| - 接受
m.key.verification.accept消息用户 Matrix ID,后跟| - 该设备 ID,后跟
| - 该设备发出的
m.key.verification.key公钥,未填充 base64,后跟| - 当前用的
transaction_id
若为废弃的 curve25519,info 依次为:
- 字符串
MATRIX_KEY_VERIFICATION_SAS - 启动用户 Matrix ID
- 启动设备 ID
- 接受用户 Matrix ID
- 接受设备 ID
transaction_id
不建议新实现支持旧方法 curve25519。
HKDF 能让密钥协商更安全且易于处理。
SAS 方法:decimal
用 HKDF 生成 5 字节,按 13 位一组转换成十进制数(范围 0~8191),各加 1000,得到 3 组认证码。
具体为:
- 第一组:(B₀≪5|B₁≫3)+1000
- 第二组:( (B₁&0x7)≪10|B₂≪2|B₃≫6 )+1000
- 第三组:( (B₃&0x3F)≪7|B₄≫1 )+1000
用户两端显示时应加分隔符或分行。
SAS 方法:emoji
用 HKDF 生成 6 字节,将前 42 位分为 7 组(每组 6 位),参照 base64,每组 6 位得 0~63 数字,查下表转为表情:
| 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 |
上述 Emoji 选择标准:
- 无颜色即能辨识
- 小图依然清晰
- 各文化普遍识别
- 互不相似,不易误认
- 易用少量词描述
- 无负面含义、平台表现一致性高
客户端应以所表描述或译文显示 Emoji。各客户端宜协作维护多语言译文集。
已知译文在 https://github.com/matrix-org/matrix-doc/blob/master/data-definitions/,在线翻译由 https://translate.riot.im/projects/matrix-doc/sas-emoji-v1
跨签名
跨签名功能,允许用户只需一次验证,就能信任对方今后新添加的设备(而无需依次手工验证对方全部设备)。每个用户有一套跨签名密钥:\
- 主密钥(MSK):作为跨签名身份密钥,为用户用户主身份,对其他密钥签名;
- 用户签名密钥(USK):仅本用户可见,用于为其他用户主密钥签名;
- 自签名密钥(SSK):用于为本用户所有设备密钥签名。
主密钥还可为备份密钥签名。主密钥本身可用各自设备密钥签名,便于从设备验证迁移(如 Alice 先前已验证过 Bob 设备,Bob 设备已签主密钥,Alice 的设备可信任主密钥,并可用自己 USK 签之)。
用户通过 POST /_matrix/client/v3/keys/device_signing/upload API 上传跨签名密钥。Alice 若上传新密钥,其用户 ID 会自动出现在所有与她共享加密房间的用户 /sync 响应的 device_lists 字段的 changed 属性中。看到 Alice ID 后,Bob 应用 POST /_matrix/client/v3/keys/query 查询 Alice 的设备密钥与跨签名密钥。
如果 Alice 想给 Bob 发加密消息,则只要满足:
- Alice 设备正用主密钥且已签 USK,
- Alice USK 已签 Bob 主密钥,
- Bob 主密钥已签其 SSK,
- Bob 的 SSK 已签 Bob 的设备密钥,
即可信任 Bob 的设备。下图展示密钥签名关系:
+------------------+ .................. +----------------+
| +--------------+ | .................. : | +------------+ |
| | 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 | ...| | |
| | +---------+ -+ +---------+ -+ | |
| | | ... | | ... | | |
| +------+ | | +----+ |
+----------------+ +--------------+
其中,方框代表密钥,箭头指示签名方向,虚线代表仅用户本人可见的键或签名。
Alice 视角下隐藏她不可见的密钥/签名:
+------------------+ +----------------+ +----------------+
| +--------------+ | | | | +------------+ |
| | v v | v v v | |
| | +-----------+ | +-----------+ | |
| | | Alice MSK | | | Bob MSK | | |
| | +-----------+ | +-----------+ | |
| | | | | | | |
| | +--+ +--+ | +--+ | |
| | v v | v | |
| | +-----------+ +-----------+ | +-----------+ | |
| | | Alice SSK | | Alice USK | | | Bob SSK | | |
| | +-----------+ +-----------+ | +-----------+ | |
| | | ... | | | | ... | | |
| | V V +--------+ V V | |
| | +---------+ -+ +---------+ -+ | |
| | | Devices | ...| | Devices | ...| | |
| | +---------+ -+ +---------+ -+ | |
| | | ... | | ... | | |
| +------+ | | +----+ |
+----------------+ +--------------+
验证方法 也可直接用于验证主密钥:将其公钥(未填充 base64)做为“设备 ID”,按常规定义。例如,Alice 和 Bob 用 SAS 验证后,Alice 的 m.key.verification.mac 里可能有 "ed25519:alices+master+public+key": "alices+master+public+key"。因此服务器必须防止设备 ID 与跨签名公钥冲突。
私钥可借 Secrets 模块储存在服务器或分发给其他设备。分别用 m.cross_signing.master, m.cross_signing.user_signing, m.cross_signing.self_signing 名称标识,采用 base64 编码后加密。
密钥与签名安全
主密钥一旦泄露,攻击者即可冒充用户或让用户信任冒名顶替者。因此主密钥私钥必须严格安全储存。若客户端无安全存储能力(如无系统级密钥保护),严禁保存主密钥私钥。
看到其他用户更换主密钥,应提醒用户再决定是否继续通信。
由于设备 key ID (ed25519:DEVICE_ID) 与跨签名 key ID (ed25519:PUBLIC_KEY) 处于同一命名空间,客户端必须区分并用公钥实际内容定位验证目标。
服务器虽应禁止设备 ID 与跨签名密钥冲突,但仍须防范恶意服务器,所以客户端应注意:
- 在验证过程中用密钥公钥而不是仅用 key ID 识别密钥;
- 验证一开始就锁定目标密钥,并确保其在过程中未被更换;
- 如发现用户下设备 ID 与跨签名 key 相同,必须拒绝验证并弹警告。
用户签名与自签名密钥意在可被快速替换(如有泄露,仅需主密钥重签及适度重新验证)。但发现被攻破和重签都要耗用户精力,因此应优先安全存储私钥,否则干脆不存储,以权衡安全与便用。
为防社交网络泄露,服务器仅允许用户查阅:
- 自己主密钥/自签/用户签名密钥的签名,
- 自己设备对自己主密钥的签名,
- 他人自签密钥对其设备的签名,
- 他人主密钥对其自签密钥的签名,
- 他人设备对其主密钥的签名。
用户无法看到其他人 user-signing key 的签名。
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. |
| 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 |
| <Other properties> | |
Keys dependent on the login type |
| Name | Type | Description |
|---|---|---|
keys |
{string: string} |
Required: The public key. The object must have exactly one property, whose name is
in the form |
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:
|
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 |
{
"failures": {
"@alice:example.com": {
"HIJKLMN": {
"errcode": "M_INVALID_SIGNATURE",
"error": "Invalid signature"
}
}
}
}
二维码
[Added in v1.1]
二维码验证适用于一方设备支持扫码,可快速验证。二维码内容编码双方主签名密钥及一组随机共享密钥供单次扫描双向验证。
若支持显示二维码,客户端在 m.key.verification.request 和 m.key.verification.ready 的 methods 字段中声明 m.qr_code.show.v1 和 m.reciprocate.v1。若支持扫码,则用 m.qr_code.scan.v1 和 m.reciprocate.v1。如果既支持显示又支持扫码,则全部声明。
Alice、Bob 互验证流程:
-
Alice 和 Bob 面对面,欲验证钥匙。
-
启动验证流程。
-
Alice 客户端显示二维码,若 Bob 客户端能扫码则 Bob 可选扫码;Bob 客户端也可以显示二维码或选择扫码。二维码格式见下文。其他验证方式如 SAS Emoji 可并列提供。
-
Alice 扫描 Bob 的二维码。
-
Alice 设备校验扫码内容是否为期望的公钥。如失败则报错同时发送
m.key.verification.cancel给 Bob。否则
- 此时 Alice 已信任 Bob 密钥,
- 并知道 Bob 拥有 Alice 的正确密钥。 接下来 Alice 需告知 Bob 验证结果以使其信任 Alice 密钥。
-
Alice 设备显示验证成功;Bob 设备尚未通过验证(需等待 Alice 明确反馈),用户需人工看到 Alice 已同意。
-
Alice 设备发送
m.key.verification.start(method=m.reciprocate.v1)给 Bob,包含共享密钥。仅作信号用,无验证功效。 -
Bob 收到上述消息,校验共享密钥。如不符则报错(这不影响 Alice 对 Bob 的验证);如一致,则请求 Bob 手动确认已经被验证。
-
Bob 见 Alice 端确认密钥一致后,点按钮确认验证。
Bob 信任 Alice,依据于 Alice 现场告知已验证成功。恶意 Alice 欺骗只影响自己与 Bob 通信,Alice 没动力作假。因而只要通信媒介可靠(如真人言语),这一流程足以可信。
-
双方退回
m.key.verification.done。
二维码格式
二维码必须兼容 ISO/IEC 18004:2015,仅使用字节模式单分段。
纠错级别由展示端自行选择。
二进制内容结构如下:
- ASCII “MATRIX”,各字节编码分别为 0x4D, 0x41, 0x54, 0x52, 0x49, 0x58
- 1 字节,二维码版本(须为 0x02)
- 1 字节,二维码验证模式,值为:
- 0x00 验证其他用户(跨签名)
- 0x01 本端信任主密钥情况下自验证
- 0x02 本端尚未信任主密钥的自验证
- 关联验证事件的 event ID 或 transaction_id,方式为:
- 2 字节,网络字节序,长度
- 实际 UTF-8 字符串编码的 ID
- 第一个 32 字节密钥,含义:
- 若 mode==0x00 或 0x01,则本用户主跨签名公钥
- 若 mode==0x02,则本设备 Ed25519 签名密钥
- 第二个 32 字节密钥,含义:
- 若 mode==0x00,则为他人主跨签名公钥
- 若 mode==0x01,则为他人设备 Ed25519 公钥
- 若 mode==0x02,为本用户主跨签名公钥
- 一组随机共享密钥(推荐约 8 字节),为编码内容的剩余部分
实例:Alice 的二维码内容
"MATRIX" |ver|mode| len | event ID
4D 41 54 52 49 58 02 00 00 2D 21 41 42 43 44 ...
| 用户主密钥 | 对方主密钥 | 共享密钥
00 01 02 03 04 05 06 07 ... 10 11 12 13 14 15 16 17 ... 20 21 22 23 24 25 26 27
代表 Alice 验证 Bob,在事件 “$ABCD…” 下自认主密钥为 0001020304050607... (base64: “AAECAwQFBg…"),她认为 Bob 的主密钥为 1011121314151617... (base64: “EBESExQVFh…"),共享密钥为 2021222324252627 (base64: “ICEiIyQlJic”)。
二维码专用验证消息
m.key.verification.start with method: m.reciprocate.v1
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
|
method |
string |
Required: The verification method to use. One of: |
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 |
| Name | Type | Description |
|---|---|---|
event_id |
string |
The event ID of the |
rel_type |
string |
The relationship type. Currently, this can only be an
One of: |
Examples
设备间共享密钥
Bob 若在电脑端与 Alice 有加密对话,在手机端首次登入时,或希望能访问到历史消息。为此本协议支持若干跨设备密钥转移方式。
密钥请求
如果设备缺失解密信息所需的密钥,可向其他设备发 m.room_key_request to-device 消息,请求字段 action 设为 request。
其他设备同意分享密钥后,可通过加密的 m.forwarded_room_key to-device 消息发送给请求设备。请求设备收到后,需向所有原发出请求的设备发 request_cancellation 的 m.room_key_request 通知取消,接收端一旦收到 request_cancellation 就应忽视所有同一 request_id 和 requesting_device_id 的早前请求。
如设备决定不共享密钥,可以发 m.room_key.withheld 以反馈拒绝加密密钥分享,详见报告加密密钥被拒绝。
密钥分享本身是攻击点,必须谨慎对待。客户端应只向受信任同一用户的设备请求/发密钥,并只接受来自受信任同一用户设备的转发密钥。
服务器端密钥备份
设备可将加密密钥上传服务器做加密备份。需解密某消息时,设备可向服务器请求该密钥并解密。备份为每用户维护,用户可重置覆盖已有备份。
相比密钥请求,服务器备份无需在线设备即可获取密钥。但会话密钥在服务器以加密形式存储,客户端需有解密密钥 方可解密备份。
创建备份时,客户端调用 POST /_matrix/client/v3/room_keys/version,加密方式定义于 auth_data。其他客户端用 GET /_matrix/client/v3/room_keys/version 发现备份。密钥随后按备份的 auth_data 加密,并用 PUT /_matrix/client/v3/room_keys/keys 或其相关变体上传,可用 GET /_matrix/client/v3/room_keys/keys 或变体下载。一次仅能写入最新备份版本。也可用 DELETE /_matrix/client/v3/room_keys/version/{version} 或 DELETE /_matrix/client/v3/room_keys/keys 等方式删除备份或部分密钥。
客户端上传密钥前,须确保 auth_data 已被信任。可通过:
如果上传会话密钥已存在,服务器对比密钥元数据保留或覆盖,规则如下:
- 若有
is_verified字段,优先保留设为true的; - 若两者都同等,则选择
first_message_index更小者; - 最后由
forwarded_count更小者获保留。
解密密钥
通常,解密钥匙(即密钥的私有部分)会以 Secrets 模块方式存储服务器或发送给其他设备,采用 m.megolm_backup.v1 名称,且加密前为 base64 编码。
如用户直接复制密钥,则以通用加密密钥表示法为字符串展示。
本规范早前使用“恢复密钥”一词,但各客户端 UI 通常用该词指秘密存储密钥,为避免歧义,现不再使用该说法。
备份算法:m.megolm_backup.v1.curve25519-aes-sha2
若备份的 algorithm 为 m.megolm_backup.v1.curve25519-aes-sha2,则 auth_data 结构如下:
AuthData
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.
| 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 |
Examples
{
"public_key": "abcdefg",
"signatures": {
"something": {
"ed25519:something": "hijklmnop"
}
}
}
备份的 session_data 字段生成步骤:
-
把要备份的会话密钥编码为
BackedUpSessionData格式的 JSON 对象。 -
生成临时 curve25519 密钥,并用它和备份公钥做 ECDH 得共享密钥。临时公钥(未填充 base64)存为
ephemeral字段。 -
以共享密钥做 HKDF(哈希算法为 SHA-256,盐为 32 字节全 0,info 为空字符串),得 80 字节。前 32 字节作 AES 密钥,下 32 字节作 MAC 密钥,后 16 字节作 AES 初始化向量。
-
JSON 序列化,加密方式为 AES-CBC-256/PKCS#7,密文未填充 base64 编码存于
ciphertext。 -
用 MAC 密钥计算 HMAC-SHA-256(消息体为空),取前 8 字节转换为 base64 存为
mac字段。
第五步本意应对原始密文做 HMAC,但 libolm 实现失误导致实际传递空字符串。未来规范将修正此问题。参见 MSC4048。
BackedUpSessionData
BackedUpSessionData
The format of a backed-up session key, prior to encryption, when using the
m.megolm_backup.v1.curve25519-aes-sha2 algorithm.
| Name | Type | Description |
|---|---|---|
algorithm |
string |
Required: The end-to-end message encryption algorithm that the key is for. Must be |
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 ( |
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
| 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. |
| Name | Type | Description |
|---|---|---|
sessions |
{string: KeyBackupData} |
Required: A map of session IDs to key data. |
| 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
| 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
| 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
| 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. |
| Name | Type | Description |
|---|---|---|
sessions |
{string: KeyBackupData} |
Required: A map of session IDs to key data. |
| 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
| 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 |
{
"count": 10,
"etag": "abcdefg"
}
403 response
| 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
| 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
| 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
| 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
| 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 |
{
"count": 10,
"etag": "abcdefg"
}
404 response
| 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
| 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
| Name | Type | Description |
|---|---|---|
roomId |
string |
Required: The ID of the room that the requested key is for. |
| 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
| Name | Type | Description |
|---|---|---|
sessions |
{string: KeyBackupData} |
Required: A map of session IDs to key data. |
| 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
| 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
| 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
| Name | Type | Description |
|---|---|---|
roomId |
string |
Required: The ID of the room that the keys are for. |
| Name | Type | Description |
|---|---|---|
version |
string |
Required: The backup in which to store the keys. Must be the current backup. |
Request body
| Name | Type | Description |
|---|---|---|
sessions |
{string: KeyBackupData} |
Required: A map of session IDs to key data. |
| 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
| 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 |
{
"count": 10,
"etag": "abcdefg"
}
403 response
| 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
| 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
| 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
| Name | Type | Description |
|---|---|---|
roomId |
string |
Required: The ID of the room that the specified key is for. |
| 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
| 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 |
{
"count": 10,
"etag": "abcdefg"
}
404 response
| 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
| 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
| 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. |
| 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
| 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
| 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
| 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
| 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. |
| Name | Type | Description |
|---|---|---|
version |
string |
Required: The backup in which to store the key. Must be the current backup. |
Request body
| 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
| 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 |
{
"count": 10,
"etag": "abcdefg"
}
403 response
| 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
| 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
| 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. |
| 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
| 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 |
{
"count": 10,
"etag": "abcdefg"
}
404 response
| 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
| 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: |
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 |
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
| 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
| 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: |
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
| 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
| Name | Type | Description |
|---|---|---|
version |
string |
Required: The backup version to get, as returned in the |
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: |
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 |
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
| 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
| 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
| Name | Type | Description |
|---|---|---|
version |
string |
Required: The backup version to update, as returned in the |
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: |
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
| 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
| 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
| 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
| Name | Type | Description |
|---|---|---|
version |
string |
Required: The backup version to delete, as returned in the |
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
| 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
| 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 export format JSON 对象格式编码。
-
用户口令经 PBKDF2(HMAC-SHA-512, password, S, N, 512) 推导 512 位密钥。S 为 128 比特随机盐,N 迭代次数(至少 10 万)。K, K’ 为前后 256 比特,K 为 AES-256 密钥,K’ 供 HMAC-SHA-256。
-
JSON 字符串化后用 AES-CTR-256/K 加密,IV 预设为 128 比特安全随机数且 bit 63 为零(兼容实现差异)。
-
按顺序拼接如下:
| 长度(字节) | 描述 |
|---|---|
| 1 | 导出格式版本,须为 0x01。 |
| 16 | 盐 S。 |
| 16 | 初始化向量 IV。 |
| 4 | N,32 位大端整数。 |
| 变长 | 加密 JSON。 |
| 32 | 以上所有数据用 K’ 做 HMAC-SHA-256 后的结果。 |
-
整体 base64 编码。可适当换行。
-
最终整体包裹于
-----BEGIN MEGOLM SESSION DATA-----\n和\n-----END MEGOLM SESSION DATA-----\n。
密钥导出格式
导出会话为 ExportedSessionData JSON 数组,具体定义:
ExportedSessionData
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.
| Name | Type | Description |
|---|---|---|
algorithm |
string |
Required: The end-to-end message encryption algorithm that the key is for. Must be |
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 ( |
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..."
}
消息算法
消息算法命名
消息算法命名使用全规范内统一扩展格式。以 m. 开头的算法名保留为本规范定义。自定义算法应按 Java 包规范全局唯一命名。
算法名应简短清晰,并表明所用原始算法,以便判断安全性。例如:
m.olm.v1 太短,识别度低且难于扩展。
m.olm.v1.ecdh-curve25519-hdkfsha256.hmacsha256.hkdfsha256-aes256-cbc-hmac64sha256 太冗长且损耗带宽、可读性差。
m.olm.v1.curve25519-aes-sha2
此名代表 Olm 协议 v1,见 Olm 规范。其组成:
- Curve25519 初始密钥协商
- HKDF-SHA-256 密钥推进
- Curve25519 用作根密钥推进
- HMAC-SHA-256 推进主键
- HKDF-SHA-256,AES-256-CBC,8 字节 HMAC-SHA-256 验证加密
支持 Olm 的设备必须将 “m.olm.v1.curve25519-aes-sha2” 纳入支持算法列表,发布 Curve25519 设备密钥和一次性密钥。
一份 Olm 加密的事件:
{
"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 为设备 Curve25519 公钥到加密负载的映射。body 为 Base64 编码的 Olm 消息内容。type 整数:0 为初始 key,1 为后续普通消息。
Olm 会话发送消息前,均为类型 0;收到对端消息后,转为类型 1。
客户端收到类型 0 消息,需先查是否已有匹配会话;若没有则新建并尝试解密,解密成功前不得持久化会话或销毁一次性密钥。
类型 1 需已有会话才能解密,若无会话,则视为无效消息。
明文负载如下:
{
"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>"
}
}
type 与 content 对应具体消息类型与内容。
这些字段用于防止攻击者冒名发 curve25519 公钥假冒自己。sender 应为发送者用户,recipient 为本地用户,recipient_keys 标明本端 ed25519。
客户端需确认加密消息正文中 sender_key 和解密明文中的 keys.ed25519 与 /keys/query 查询到的密钥相符。还需核实密钥签名。否则无法确认发送设备拥有所声称 ed25519 私钥,这对经过验证设备尤为重要。
如与对方建立了多个会话,应选用最近收到且成功解密过消息的那个。若尚无消息则以建立时间论新旧。为避免膨胀,可为每个设备设一个最大会话数(至少 4),采用 LRU 策略淘汰旧会话。
恢复不可解密 Olm 消息
有时消息因各种原因无法解密。此时应假定 Olm 会话失效,需要新建会话。
Megolm 加密消息通常不会这样。未解密的消息一般密钥随后可获得。但 Olm 无此恢复机制,故必须新建会话。
新建会话时,需向对端发送 m.dummy 事件通知会话变更。
客户端应限速,不得一小时内反复新建会话。
可通过 m.room_key_request 请求帮助找回因失效而丢失的 Megolm 会话密钥。
对于未知 Megolm 会话的密钥请求,应广播给该用户的所有设备(不只当前 event 的 device_id 或 sender_key),因这些字段已废弃。见 m.megolm.v1.aes-sha2。
m.megolm.v1.aes-sha2
[Changed in v1.3]
m.megolm.v1.aes-sha2 代表 Megolm 算法 v1, 详见 Megolm 规范。详细:
- HMAC-SHA-256 连续哈希推进
- HKDF-SHA-256,AES-256-CBC,加 8 字节 HMAC-SHA-256 验证
- Ed25519 签名消息
支持 Megolm 的设备即需支持 Olm,且将本算法纳入支持算法列表。
加密事件格式:
{
"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>"
}
}
明文负载为:
{
"type": "<event_type>",
"content": "<event_content>",
"room_id": "<the room_id>"
}
room_id 写入明文防止服务器篡改房间。
客户端须防范重放攻击,记录 Megolm ratchet index,应拒绝重复 index 的消息(须避免误杀,如正常流程可能重复解密一条消息)。
同 Olm,客户端需确认消息发送人即明文 sender 字段,其房间与 session_id 已被本端信任记录。
自 v1.3 起,sender_key 与 device_id 字段已废弃。应继续发送,但不得用其校验消息来源。
客户端不得用这两字段存储/检索会话。
将来规范版本中,这些字段将完全移除。
移除此类字段,提升隐私和安全性,主设备不可见、降低对不受信数据(服务器可篡改、用户故意作假)的依赖。
session_id 本身全局唯一,无需额外 context 字段。
减少此依赖同时提升隐私与安全性。
要在房间启用端到端加密,客户端可发 m.room.encryption 状态事件,algorithm 为 m.megolm.v1.aes-sha2。
房间新建 Megolm 会话后,需用 Olm 私信分发 session key 给目标设备,以便其解密今后消息。密钥由 m.room_key 事件发送。收到他人密钥后,须存储以便解密消息。
收到会话密钥时,必须确保密钥由 Olm channel 安全获得,以确认消息真实性。
当客户端要更新 Megolm 会话数据时,必须确保新数据仅来自可信来源(如本用户已验证设备的 m.forwarded_room_key 或 m.room_key),且新密钥的 message index 必须小于已有密钥。
协议定义
事件
m.room.encryption
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: |
rotation_period_ms |
integer |
How long the session should be used before changing it. |
rotation_period_msgs |
integer |
How many messages should be sent before changing the session. |
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
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: |
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 Changed in |
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 Changed in |
session_id |
string |
The ID of the session used to encrypt the message. Required with Megolm. |
| 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
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: |
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
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: |
body |
RequestedKeyInfo |
Information about the requested key. Required when |
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. |
| 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 Changed in |
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
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 |
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
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"
}
密钥管理 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
fromandto.
| Rate-limited: | No |
|---|---|
| Requires authentication: | Yes |
Request
Request parameters
| Name | Type | Description |
|---|---|---|
from |
string |
Required: The desired start point of the list. Should be the |
to |
string |
Required: The desired end point of the list. Should be the |
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 |
{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 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. |
| 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
|
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 |
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
Added in |
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
Added in |
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
|
| 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 |
signatures |
{User ID: {string: string}} |
Required: Signatures for the device key object. A map from user ID, to a map from
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. |
| Name | Type | Description |
|---|---|---|
device_display_name |
string |
The display name which the user set on the device. |
| Name | Type | Description |
|---|---|---|
keys |
{string: string} |
Required: The public key. The object must have exactly one property, whose name is
in the form |
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
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 May be absent if a new fallback key is not required. Added in |
one_time_keys |
{string: string|KeyObject} |
One-time public keys for “pre-key” messages. The names of
the properties should be in the format
May be absent if no new one-time keys are required. |
| 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 |
signatures |
{User ID: {string: string}} |
Required: Signatures for the device key object. A map from user ID, to a map from
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. |
| 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
}
}
/sync 扩展
本模块为 /sync 响应新增可选 device_lists 字段(详见下文)。仅增量 /sync(指定 since 参数时)需返回。客户端应在初始同步后用 /keys/query 或 /keys/changes 跟进,见跟踪用户设备列表。
同时新增 device_one_time_keys_count 属性。注意拼写与 /keys/upload 响应的 one_time_key_counts 有区别。
[Added in v1.2] 最后,新增 device_unused_fallback_key_types 列举当前设备已上传但尚未被领用的备用密钥算法。若某算法此前上传的备用密钥不在列表,则应在必要时上传替换密钥。此属性为强制包含项,亦可用于判断服务器对备用密钥的支持(如 /versions 外的备用判断方式)。
| 参数 | 类型 | 描述 |
|---|---|---|
| device_lists | DeviceLists | 可选。e2e 设备变更信息,仅在增量 sync 响应中出现。 |
| device_one_time_keys_count | {string: integer} | 可选。按算法分列当前设备未被认领的一次性密钥数,未传视为 0。 |
| device_unused_fallback_key_types | [string] | 必选。 未被使用的备用密钥算法列表。 |
DeviceLists
| 参数 | 类型 | 描述 |
|---|---|---|
| changed | [string] | 设备身份或跨签名密钥有更新、或新近与本客户端共享加密房间的用户列表。 |
| left | [string] | 自上次 sync 后,本客户端不再与之共享任何加密房间的用户列表。 |
最优逻辑下,仅在 Alice 更新设备、密钥或与 Bob 新建共享房间时,在 Bob 的 sync 的 changed 字段添加 Alice。但为了简化,服务器也可在 Alice 与 Bob 每次新建共享房间(无论是否已共享)时添加。
例:
{
"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"]
}
报告密钥被拒绝
当客户端向房间内其他设备发送加密事件时,可选择通知那些因未提供密钥而无法解密事件的设备。如此接收方即可明确原因而非泛泛报错。
同理,一台设备通过密钥请求 向他设备请求密钥,对方亦可主动说明拒绝分享密钥。
如 Alice 一开始不愿向 Bob 分享部分消息的 Megolm 会话,后期决定给予 Bob 解密后续消息的权力,可发送只包含最新版会话密钥的消息,且在 Bob 的新设备请求密钥时,会获得被 ratchet(推进)过的最新版本。Bob 旧设备可在 m.forwarded_room_key 里以对象 withheld 字段附上最初被拒绝的原因代码。
m.room_key.withheld
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: |
code |
string |
Required: A machine-readable code for why the key was not sent. Codes beginning
with One of: |
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
|
room_id |
string |
Required if |
sender_key |
string |
Required: The unpadded base64-encoded device curve25519 key of the event's sender. |
session_id |
string |
Required if |
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"
}
机密信息
[Added in v1.1]
客户端可能拥有希望授权给其他客户端访问但不希望服务器知晓的机密信息,因此这些信息在通过服务器传递时必须进行加密。这可以通过异步方式(将加密后的数据存储在服务器供之后获取)或同步方式(客户端之间互发消息)来实现。
每个机密信息都有一个标识符,客户端在存储、获取、请求或共享机密信息时会通过该标识符进行引用。机密信息是普通字符串;如需存储结构化数据,可将其编码为字符串形式。
本节所描述的机制被称为“安全机密信息存储与共享”(secure secret storage and sharing)、简称“SSSS”或“4S”。
存储
当机密信息存储在服务器上时,会以账户数据的形式存储在用户的账户数据中,事件类型等于机密信息的标识符。用于加密机密信息的密钥,其描述也会存储在用户的账户数据中。用户可以拥有多个密钥,从而根据分配给客户端的密钥控制其可访问哪些机密信息。
密钥存储
每个密钥都有一个 ID,其描述以事件类型 m.secret_storage.key.[key ID] 存储在用户的账户数据中。密钥的账户数据内容包含一个 algorithm 属性,表示所用加密算法,以及一个 name 属性,为该密钥的可读名称。密钥描述中还可以包含一个 passphrase 属性,该属性用于根据用户输入的口令生成密钥,详见从口令派生密钥。
KeyDescription
| 参数 | 类型 | 说明 |
|---|---|---|
| name | string | 可选。密钥名称。如果未提供,客户端可使用“未命名密钥”之类的通用名称;若该密钥被标记为默认密钥(见下文),则可用“默认密钥”。 |
| algorithm | string | 必需。 此密钥使用的加密算法。目前仅支持 m.secret_storage.v1.aes-hmac-sha2。 |
| passphrase | string | 详见从口令派生密钥一节。 |
其他属性依赖于加密算法,详见下文。
如需将某个密钥标记为“默认”密钥,应在用户的账户数据中,设置事件类型为 m.secret_storage.default_key 的对象,其 key 属性为该密钥 ID。默认密钥将用于加密用户期望在所有客户端均可用的所有机密信息。除非用户另行指定,客户端将尝试使用默认密钥解密机密信息。
希望为用户提供简化界面的客户端可只支持默认密钥。若未指定默认密钥,客户端可视为不存在任何密钥。当此类客户端创建密钥时,应将其标为默认密钥。
DefaultKey
| 参数 | 类型 | 说明 |
|---|---|---|
| key | string | 必需。 默认密钥的ID。 |
m.secret_storage.v1.aes-hmac-sha2
为便于客户端检查用户输入密钥的正确性,m.secret_storage.v1.aes-hmac-sha2 算法使用的密钥会存储附加数据。
存储密钥时,客户端应:
-
以机密信息存储密钥为基础,使用 SHA-256 为哈希值,32字节0为salt,空字符串为info执行 HKDF,生成64字节数据。前32字节为AES密钥,后32字节为MAC密钥。
-
生成16字节随机数,将第63位设为0(为兼容不同 AES-CTR 实现),作为AES初始化向量(IV)。
-
用第1步所得的AES密钥和IV,以 AES-CTR-256 加密32字节的零组成的消息。
-
用第1步所得的MAC密钥,对第3步所得的原始加密数据执行 HMAC-SHA-256。
-
将第2步生成的IV与第4步生成的 MAC,使用无填充 base64 编码,并分别存储在
iv和mac属性中,属性位置为m.secret_storage.key.[key ID]账户数据。(第3步得到的密文仅用于MAC计算后即丢弃。)
客户端在检查密钥正确性时可重复此过程:若MAC值匹配,则密钥正确。但需注意,这些属性为可选。若不存在此类属性,客户端必须假定密钥有效。
还需注意,虽然建议客户端应当如上采用无填充base64进行编码,但部分现有实现使用标准RFC4648规范base64并含有填充,因此客户端必须同时兼容两种编码。
因此,面向该算法的 m.secret_storage.key.[key ID] 账户数据结构如下:
AesHmacSha2KeyDescription
| 参数 | 类型 | 说明 |
|---|---|---|
| name | string | 可选。密钥名称。 |
| algorithm | string | 必需。 此密钥使用的加密算法:m.secret_storage.v1.aes-hmac-sha2。 |
| passphrase | object | 详见从口令派生密钥一节。 |
| iv | string | 可选。校验用的16字节初始化向量,base64编码。 |
| mac | string | 可选。对32字节零加密结果的MAC,base64编码。 |
示例:
{
"name": "m.default",
"algorithm": "m.secret_storage.v1.aes-hmac-sha2",
"iv": "random+data",
"mac": "mac+of+encrypted+zeros"
}
机密信息存储
加密后的数据以具体功能定义的事件类型,存储在用户账户数据中。账户数据会有一个 encrypted 属性,为从密钥ID到对象的映射。特定密钥的 m.secret_storage.key.[key ID] 数据中的算法定义了解释其他属性的方式,但大多数加密方案应包含 ciphertext 和 mac 两个属性,其中 ciphertext 属性为无填充base64编码的密文,mac 用于保证数据完整性。
Secret
| 参数 | 类型 | 说明 |
|---|---|---|
| encrypted | {string: object} | 必需。 密钥ID到加密数据的映射。加密数据的确切格式取决于密钥算法。参见m.secret_storage.v1.aes-hmac-sha2 节中的 AesHmacSha2EncryptedData 定义。 |
示例:
某机密信息使用 ID 为 key_id_1 和 key_id_2 的密钥加密:
org.example.some.secret:
{
"encrypted": {
"key_id_1": {
"ciphertext": "base64+encoded+encrypted+data",
"mac": "base64+encoded+mac",
// ... 其他属性,见 m.secret_storage.key.key_id_1 的 algorithm 属性
},
"key_id_2": {
// ...
}
}
}
相应密钥的描述:
m.secret_storage.key.key_id_1:
{
"name": "Some key",
"algorithm": "m.secret_storage.v1.aes-hmac-sha2",
// ... 其他属性,见 algorithm
}
m.secret_storage.key.key_id_2:
{
"name": "Some other key",
"algorithm": "m.secret_storage.v1.aes-hmac-sha2",
// ... 其他属性,见 algorithm
}
若 key_id_1 为默认密钥,则还应有:
m.secret_storage.default_key:
{
"key": "key_id_1"
}
m.secret_storage.v1.aes-hmac-sha2
采用 m.secret_storage.v1.aes-hmac-sha2 算法加密的机密信息,使用 AES-CTR-256 进行加密,并用 HMAC-SHA-256 进行认证,加密过程如下:
-
以机密信息存储密钥为基础,使用 SHA-256 作为哈希,32字节0为salt,以机密名称为info,执行HKDF生成64字节。前32字节为AES密钥,后32字节为MAC密钥。
-
生成16字节随机数,将第63位设为0(为兼容不同AES-CTR实现),用作AES初始化向量(IV)。
-
用第1步获得的AES密钥和IV,采用 AES-CTR-256 加密数据。
-
用第1步获得的MAC密钥,对第3步获得的原始加密数据执行HMAC-SHA-256。
-
将第2步的 IV、第3步的密文和第4步的 MAC 用无填充 base64 编码,分别存为账户数据对象的
iv、ciphertext和mac属性。注意:部分现有实现使用标准RFC4648规范base64带填充进行编码,因此客户端必须两种编码均可接受。
采用该算法加密的账户数据对象的 encrypted 属性结构如下:
AesHmacSha2EncryptedData
| 参数 | 类型 | 说明 |
|---|---|---|
| iv | string | 必需。 16字节初始化向量,base64编码。 |
| ciphertext | string | 必需。 AES-CTR加密的数据,base64编码。 |
| mac | string | 必需。 MAC,base64编码。 |
示例,加密后数据形态如下:
{
"encrypted": {
"key_id": {
"iv": "16+bytes+base64",
"ciphertext": "base64+encoded+encrypted+data",
"mac": "base64+encoded+mac"
}
}
}
密钥表示
用户获得 m.secret_storage.v1.aes-hmac-sha2 的原始密钥时,应以通用加密密钥表示方法的字符串形式呈现密钥。
从口令派生密钥
用户可能希望使用自选口令而非随机生成的密钥。在这种情况下,如何从口令生成密钥的信息会保存在 m.secret_storage.key.[key ID] 账户数据的 passphrase 属性中。passphrase 属性包含一个 algorithm 属性,表示如何据口令生成密钥。passphrase 的其他属性由指定的算法定义。
当前只定义了 m.pbkdf2 算法。对于 m.pbkdf2,passphrase 属性包括:
| 参数 | 类型 | 说明 |
|---|---|---|
| algorithm | string | 必需。 必须为 m.pbkdf2 |
| salt | string | 必需。 PBKDF2 算法使用的 salt。 |
| iterations | integer | 必需。 PBKDF2 算法使用的迭代次数。 |
| bits | integer | 可选。生成密钥的位数,默认256。 |
密钥将使用 PBKDF2 算法,以 SHA-512 为哈希,使用 salt 属性作为盐值,iterations 属性指明迭代次数生成。
示例:
{
"passphrase": {
"algorithm": "m.pbkdf2",
"salt": "MmMsAlty",
"iterations": 100000,
"bits": 256
},
...
}
共享
客户端如需向其他设备请求机密信息,可发送 m.secret.request 设备事件,action 设为 request,name 设为机密信息标识符。有意愿分享机密信息的设备将用Olm加密,发送 m.secret.send 事件回复。当原始客户端获取到机密信息后,应向所有除获得机密信息那台设备外的其他设备发送 m.secret.request 事件,action 设置为 request_cancellation。客户端应忽略未针对本机发送 m.secret.request 事件的设备所收到的 m.secret.send 事件。
客户端必须确保仅将机密信息共享给被授权查看的其他设备。例如,客户端应仅与自己已验证的设备共享机密信息,必要时还可提示用户确认共享操作。
事件定义
m.secret.request
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: |
name |
string |
Required if |
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
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"
}
房间历史可见性
该模块增加了对控制房间中过往事件可见性的支持。
除 world_readable 外,在所有情况下,用户都必须先加入房间才能查看该房间内的事件。一旦用户加入房间,他们将能访问该房间中某个事件子集。该子集如何选择由下文描述的 m.room.history_visibility 事件控制。用户离开房间后,仍可查看离开前被允许查看的事件,但无法查看离开后收到的事件。
m.room.history_visibility 事件有四个选项:
world_readable- 只要此项为m.room.history_visibility的值,任何参与的服务器都可以与任何人共享所有事件,无论其是否曾加入该房间。shared- 以前的事件始终对新加入的成员可见。即使用户不在房间内时发送的事件,房间内的所有事件也都可访问。invited- 新加入成员仅能访问自被邀请起的事件。当成员的状态变为非invite或join时,事件将不再可访问。joined- 新加入成员仅能访问自加入该房间起的事件。当成员的状态变为非join时,事件将不再可访问。
这些选项应用于事件发送时。判断依据是事件被添加到 DAG 时 m.room.history_visibility 的状态。这意味着,若当时的设置更为严格,客户端不能事后选择向新用户显示或隐藏历史记录。
事件
m.room.history_visibility
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: |
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"
}
}
客户端行为
若历史可见性设置为 world_readable,客户端可提示用户,非房间成员也可能读取事件内容。
服务器行为
默认情况下,如果未设置 history_visibility,或其值无效,则视为 shared。关于用户能否查看某个事件,需依据该事件发生时房间的状态判定。
- 如果
history_visibility被设置为world_readable,允许查看。 - 如果用户的
membership为join,允许查看。 - 如果
history_visibility为shared,且用户在事件发送后曾加入过房间,允许查看。 - 如果用户的
membership为invite,且history_visibility被设置为invited,允许查看。 - 否则,拒绝访问。
针对 m.room.history_visibility 事件本身,如果事件之前或之后的 history_visibility 允许用户可见,则应允许该用户查看。例如,即使用户不是房间成员,也应可以看到将 history_visibility 从 world_readable 更改为 joined 或从 joined 更改为 world_readable 的 m.room.history_visibility 事件。
同样,对于用户自身的 m.room.member 事件,如果事件前后的 membership 有任一允许用户查看,则应允许该用户查看该事件。例如,用户始终可以看到将其 membership 设为 join 的事件,或将其从 join 更改为其他值的事件,即使 history_visibility 为 joined。
安全注意事项
出于兼容性考虑,history_visibility 默认值为 shared。客户端需注意,若未设置该事件,则其房间历史对所有成员均可见。
推送通知
+--------------------+ +-------------------+
Matrix HTTP | | | |
Notification Protocol | 应用开发者 | | 设备厂商 |
| | | |
+-------------------+ | +----------------+ | | +---------------+ |
| | | | | | | | | |
| Matrix 主服务器 +-----> 推送网关(Gateway)+------> 推送供应商 | |
| | | | | | | | | |
+-^-----------------+ | +----------------+ | | +----+----------+ |
| | | | | |
Matrix | | | | | |
客户端/服务器API + | | | | |
| | +--------------------+ +-------------------+
| +--+-+ |
| | <-------------------------------------------+
+---+ |
| | 供应商推送协议
+----+
移动设备或客户端
本模块增加了对推送通知的支持。主服务器会将事件的通知发送到用户配置的 HTTP 端点。用户也可以自定义多条规则,决定哪些事件会生成通知。这些规则全部存储并由用户的主服务器管理。这允许用户专属的推送设置在不同客户端应用之间复用。
上述图示显示了推送通知发送到手机时的流程,通知通过手机厂商(例如 Apple 的 APNS 或 Google 的 GCM)进行提交。具体流程如下:
- 客户端应用登录到主服务器。
- 客户端应用在其厂商的推送供应商处注册,并获得某种路由令牌。
- 移动应用通过客户端/服务器 API 添加一个“推送者(pusher)”,并提供为该应用配置的特定推送网关的 URL,同时提供从推送供应商获得的路由令牌。
- 主服务器使用提供的 URL 向推送网关发送 HTTP 请求。推送网关将该通知中继给推送供应商,并附带发送推送通知所需的路由令牌及相关私密凭证。
- 推送供应商将通知发送至设备。
本节相关术语定义如下:
- 推送供应商(Push Provider)
-
推送供应商是由设备厂商管理的服务,可以直接将通知发送到设备。例如 Google Cloud Messaging(GCM)和 Apple Push Notification Service(APNS)都是推送供应商的例子。
- 推送网关(Push Gateway)
-
推送网关是接收主服务器 HTTP 事件通知并将其转发给其他协议(如 iOS 设备的 APNS 或 Android 设备的 GCM)的服务器。客户端在设置 Pusher 时,会告知主服务器该将通知发送至哪个推送网关。
- 推送者(Pusher)
-
Pusher 是主服务器上负责管理并发送 HTTP 通知给用户的工作进程。每个用户可以有多个 pusher,每组装置对应一个。
- 推送规则(Push Rule)
-
推送规则是一条声明了在什么条件下事件会被发送到推送网关,以及通知应该如何呈现的规则。这些规则储存在用户的主服务器上。用户可通过客户端/服务器 API 手动配置、创建与查看这些规则。
- 推送规则集(Push Ruleset)
-
推送规则集根据某些标准限定某组规则的作用范围。比如,某些规则只针对来自特定发信人的消息、特定聊天室、或作为默认规则。推送规则集包含了全部作用域与规则集合。
推送规则
推送规则是一条声明了在什么条件下事件会被发送到推送网关,以及通知该如何呈现的规则。推送规则有多种“类型(kind)”,每条规则都有相应的优先级。每条推送规则必须包含 kind 和 rule_id。rule_id 是在该类型和作用域内部唯一的字符串:同种类型但属于不同设备的规则之间,rule_id 并不要求全局唯一。依据 kind 类型,规则可以具有额外的键。
不同的 kind 类型按如下顺序检查:
-
Override 规则 (
override)。 最高优先级的规则,由用户配置作为覆盖项。 -
内容专属规则 (
content)。 针对匹配某些模式的消息配置行为。内容规则包含一个参数 ——pattern,提供待匹配的glob 风格模式。 该匹配大小写不敏感,必须匹配消息内容的content.body属性中任何以单词边界起止的子串。单词边界指文本的起始或结尾,或者非[A-Z],[a-z],[0-9],_集合中的任意字符。大小写不敏感的具体实现由主服务器定义。 -
房间专属规则 (
room)。 更改某个房间内所有消息的通知行为。房间规则的rule_id总为其作用房间的 ID。 -
发信人专属规则 (
sender)。 针对某个 Matrix 用户 ID 发出的消息配置通知行为。发信人规则的rule_id总为该用户的 Matrix ID。 -
Underride 规则 (
underride)。 与override规则作用相同,但其优先级低于content、room和sender规则。
同一 kind 的规则可以指定顺序优先级,用于确定在多条规则命中时选择哪一条。例如,规则 A 匹配“tea”,规则 B 匹配“time”,则消息 “It’s time for tea” 会被两条规则匹配,随后按顺序决定实际生效的规则。只有优先级最高的规则的 actions 会被发送给推送网关。
每条规则可以启用或禁用。被禁用的规则永不匹配。如果没有任何规则匹配某事件,主服务器不得为该事件通知推送网关。主服务器也不得为用户自己发送的事件通知推送网关。
动作(Actions)
所有规则都有一个关联的 actions 动作列表。动作决定对于匹配的事件,通知是否被送达及如何送达。定义如下:
notify-
为每个匹配事件产生推送通知。
set_tweak-
设置发送通知请求给推送网关时
tweaks字典键中的一项。格式为字典对象,set_tweak为要设置的调整项名,如果需要还可带有value指定其值。已定义以下调整项:
sound-
字符串,表示此通知到达时播放的声音。
default表示播放默认提示音。设备也可根据实际选择如振动等其他告警方式。 highlight-
布尔值,是否应在界面中高亮展示这条消息。通常会以不同颜色/样式突出消息,或调整 UI 以特别提示发生消息的房间。如果给定了没有显式值的
highlight调整项,其值视为true。若未指定则为false。
调整项会透明地通过主服务器传递,客户端应用与推送网关可约定自定义调整项。例如,可指定在移动设备上如何闪烁通知灯。
无参数的动作用字符串表示,否则用以动作名为键、相关参数为其他键的字典表示,例如:{ "set_tweak": "sound", "value": "default" }。
历史动作说明
早期 Matrix 规范包含 dont_notify 和 coalesce 动作。客户端和主服务器必须忽略这些动作,比如遇到时应从动作数组中剔除。因此,包含 ["dont_notify"] 的规则应等效于动作数组为空的规则。
条件(Conditions)
override 和 underride 规则可以有“条件”列表。事件必须满足所有条件,该规则才会匹配。若规则无任何条件,则总是匹配。
无法识别的条件不得匹配任何事件,相当于禁用该规则。
room、sender 和 content 规则不以条件列表的方式定义,而是有预定义条件。房间和发信人规则中,rule_id 的内容决定其行为。
以下条件类型已定义:
event_match
对事件某属性用 glob 模式匹配。参数:
匹配大小写不敏感,必须匹配 key 指定属性的整个值(但 content.body 见下文)。大小写不敏感的实现由主服务器定义。
如事件中 key 指定属性完全不存在或不是字符串,即便 pattern 是 *,条件也不匹配。
例如,若 key 为 content.topic,pattern 为 lunc?*,其事件如下将会匹配:
{
"content": {
"topic": "Lunch plans",
},
"event_id": "$143273582443PhrSn:example.org",
"room_id": "!636q39766251:example.com",
"sender": "@example:example.org",
"state_key": "",
"type": "m.room.topic"
}
其它能匹配的 topic 值有:
"LUNCH"(大小写不敏感,*可匹配零个字符)
以下 topic 不匹配:
" lunch"(前导空格)"lunc"(?必须匹配一个字符)null(不是字符串)
特殊情况:若 key 为 content.body,pattern 必须匹配属性值任一单词边界起止的子串。单词边界指值的起止或非 [A-Z]、[a-z]、[0-9]、_ 集合的字符。
例如,key 若为 content.body,pattern 为 ex*ple,则如下消息会匹配:
{
"content": {
"body": "An example event."
},
"event_id": "$143273976499sgjks:example.org",
"room_id": "!636q39766251:example.com",
"sender": "@example:example.org",
"type": "m.room.message"
}
其它匹配 body 值有:
"exple"(模式可匹配 body 起止)"An exciting triple-whammy"(模式可跨多单词,且-被视为分隔符)
注意,state_key 没有默认隐式条件。即,针对只匹配状态事件的推送规则,必须明确指定对 state_key 的条件。
例如,见下方默认规则
.m.rule.tombstone。
event_property_is
对事件属性值进行精确匹配。参数:
key:事件属性的点分路径,如content.body。value:要匹配的值。
匹配为精确等值,仅支持非复合(canonical JSON)类型:字符串、区间 [-(2**53)+1, (2**53)-1] 内的整数、布尔值和 null。
如 key 指定属性不存在,或类型不是字符串、整数、布尔或 null,条件不匹配。
例如,若 key 为 content.m\.federate,value 为 true,事件如下匹配:
{
"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"
}
下列 m.federate 值不匹配:
"true"(类型不同,字符串)1(不进行类型转换)
event_property_contains
如事件某数组属性精确包含特定值则匹配。参数:
-
key:事件属性的点分路径,如content.body。 -
value:要匹配的值。
仅当数组元素为非复合 canonical JSON 类型(字符串、上述区间内整数、布尔、null)时有效。其他类型将被忽略。
如 key 指定属性不存在,或不是数组,则条件不匹配。
例如,若 key 为 content.alt_aliases,value 为 "#myroom:example.com",如下事件会匹配:
{
"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
}
}
下列 alt_aliases 值不匹配:
":example.com"(部分匹配不通过)
contains_display_name
匹配 content.body 含有房间内该用户当前显示昵称的消息。因显示昵称可能变化,维护对应规则较难,故单独为此设一条件。该条件无参数。
room_member_count
匹配房间当前成员数。参数:
is:一个支持可选前缀的十进制整数,前缀包括==、<、>、>=或<=。如<匹配成员数小于给定数字,依此类推。缺省无前缀时为==。
sender_notification_permission
结合房内当前权限设置,确保事件发送者权限足够以触发通知。
参数:
key:字符串,指定需触发何种类型通知时发信人需具备的权限级别,如room。详见 m.room.power_levels 事件结构。该key用于根据 power level 对象内容查找通知类型对应权限级别。
预定义规则
主服务器可指定“服务器默认规则”。该类规则优先级低于“用户自定义规则”,唯一例外是 .m.rule.master,它总是所有规则中最高的。所有服务器默认规则的 rule_id 必须以点(”.")开头以便区分。以下为规定的服务器默认规则:
默认 Override 规则
.m.rule.master
匹配所有事件。启用后可关闭所有推送通知。不同于其它服务器默认规则,此规则始终优先级最高,连用户自定义规则都排在其后。默认禁用。
定义:
{
"rule_id": ".m.rule.master",
"default": true,
"enabled": false,
"conditions": [],
"actions": []
}
.m.rule.suppress_notices
匹配 msgtype 为 notice 的消息。
定义:
{
"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
匹配针对该用户的新房间邀请。
定义:
{
"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
匹配任意 m.room.member_event。
定义:
{
"rule_id": ".m.rule.member_event",
"default": true,
"enabled": true,
"conditions": [
{
"key": "type",
"kind": "event_match",
"pattern": "m.room.member"
}
],
"actions": []
}
[Added in v1.7]
匹配在 m.mentions 属性下 user_ids 包含用户 Matrix ID 的任意消息。
定义:
{
"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"
}
]
}
[Changed in v1.7]
自 v1.7 起,该规则已废弃,仅在事件未含 m.mentions 属性 时启用。
匹配内容含有用户当前房间显示昵称的消息。
定义:
{
"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"
}
]
}
[Added in v1.7]
匹配拥有相应权限、m.mentions 属性中的 room 字段为 true 的发信人消息。
定义:
{
"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"
}
]
}
[Changed in v1.7]
自 v1.7 起,该规则已废弃,仅在事件未含 m.mentions 属性 时启用。
匹配拥有权限且内容含有 @room 字符串(需全房通知)的消息。
定义:
{
"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.room.tombstone 的状态事件。用于通知房间升级,效果类似于 @room 通知。
定义:
{
"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"
}
]
}
[Added in v1.7]
匹配所有类型为 m.reaction 的事件。用于抑制 m.reaction 事件的通知。
定义:
{
"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]
抑制对 m.room.server_acl 事件的通知。
定义:
{
"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]
抑制与事件替换相关的通知。
定义:
{
"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": []
}
默认 Content 规则
[Changed in v1.7]
自 v1.7 起,该规则已废弃,仅当事件无 m.mentions 属性 时启用。
匹配内容包含用户 Matrix ID 本地部分(以单词边界分隔)的消息。
定义(作为 content 规则):
{
"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"
}
]
}
默认 Underride 规则
.m.rule.call
匹配所有传入 VOIP 呼叫事件。
定义:
{
"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
匹配发送至仅有两名成员的加密房间内的任意加密事件。不同于普通推送规则,加密后事件无法基于内容匹配,故此规则表现为“全匹配”或“不匹配” —— 若为 1:1 房间内所有加密事件都匹配,否则全不匹配。
定义:
{
"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
匹配仅有两名成员的房间内任意消息。
定义:
{
"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
匹配所有聊天消息。
定义:
{
"rule_id": ".m.rule.message",
"default": true,
"enabled": true,
"conditions": [
{
"kind": "event_match",
"key": "type",
"pattern": "m.room.message"
}
],
"actions": [
"notify"
]
}
.m.rule.encrypted
匹配所有加密事件。由于加密,事件内容不能被常规匹配,本规则表现为组房内所有加密事件全匹配或全不匹配。
定义:
{
"rule_id": ".m.rule.encrypted",
"default": true,
"enabled": true,
"conditions": [
{
"kind": "event_match",
"key": "type",
"pattern": "m.room.encrypted"
}
],
"actions": [
"notify"
]
}
推送规则:API
客户端可通过以下 API 全局或针对各设备获取、添加、修改、删除推送规则。
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. |
| Name | Type | Description |
|---|---|---|
content |
[PushRule] |
|
override |
[PushRule] |
|
room |
[PushRule] |
|
sender |
[PushRule] |
|
underride |
[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 |
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 |
rule_id |
string |
Required: The ID of this rule. |
| Name | Type | Description |
|---|---|---|
is |
string |
Required for |
key |
string |
Required for Required for |
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 |
value |
string|integer|boolean|null |
Required for |
{
"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] |
| 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 |
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 |
rule_id |
string |
Required: The ID of this rule. |
| Name | Type | Description |
|---|---|---|
is |
string |
Required for |
key |
string |
Required for Required for |
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 |
value |
string|integer|boolean|null |
Required for |
{
"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
| Name | Type | Description |
|---|---|---|
kind |
string |
Required: The kind of rule One of: |
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
| 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 |
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 |
rule_id |
string |
Required: The ID of this rule. |
| Name | Type | Description |
|---|---|---|
is |
string |
Required for |
key |
string |
Required for Required for |
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 |
value |
string|integer|boolean|null |
Required for |
{
"actions": [],
"default": false,
"enabled": true,
"pattern": "cake*lie",
"rule_id": "nocake"
}
404 response
| 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
| Name | Type | Description |
|---|---|---|
kind |
string |
Required: The kind of rule One of: |
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. |
| 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 |
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 |
pattern |
string |
Only applicable to |
| Name | Type | Description |
|---|---|---|
is |
string |
Required for |
key |
string |
Required for Required for |
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 |
value |
string|integer|boolean|null |
Required for |
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
| 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
| 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
| 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
| Name | Type | Description |
|---|---|---|
kind |
string |
Required: The kind of rule One of: |
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
| 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
| Name | Type | Description |
|---|---|---|
kind |
string |
Required: The kind of rule One of: |
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
| 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
| Name | Type | Description |
|---|---|---|
kind |
string |
Required: The kind of rule One of: |
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
| 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
| Name | Type | Description |
|---|---|---|
kind |
string |
Required: The kind of rule One of: |
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
| 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
| Name | Type | Description |
|---|---|---|
kind |
string |
Required: The kind of rule One of: |
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
| 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."
}
推送规则:事件
当用户更改推送规则,会向所有客户端在下次 /sync 请求的 account_data 部分发送 m.push_rules 事件。
事件内容为用户当前的全部推送规则。
m.push_rules
m.push_rules
Describes all push rules for this user.
| Event type: | Message event |
|---|
Content
| Name | Type | Description |
|---|---|---|
global |
Ruleset |
The global ruleset |
| Name | Type | Description |
|---|---|---|
content |
[PushRule] |
|
override |
[PushRule] |
|
room |
[PushRule] |
|
sender |
[PushRule] |
|
underride |
[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 |
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 |
rule_id |
string |
Required: The ID of this rule. |
| Name | Type | Description |
|---|---|---|
is |
string |
Required for |
key |
string |
Required for Required for |
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 |
value |
string|integer|boolean|null |
Required for |
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"
}
示例
为 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" : []
}'
禁止为用户名为 @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" : []
}'
为所有消息内容含 “cake” 的消息始终通知,并设置专用提示音(规则 ID 为 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"}]
}'
添加规则:禁止通知以 “cake” 开头、“lie” 结尾的消息,并优先级高于上一条规则:
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"]
}'
为成员数不超过 10 的房间内含有 “beer” 的消息添加自定义通知音(优先级高于 room, sender, content 规则):
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"}
]
}'
客户端行为
客户端必须先配置 Pusher,才能接收推送通知。具体 API 如下所述。
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 |
| 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. |
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 |
| Name | Type | Description |
|---|---|---|
format |
string |
The format to use when sending notifications to the Push Gateway. |
url |
URI |
Required if |
{
"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 |
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 |
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 |
data |
PusherData |
Required if If |
device_display_name |
string |
Required if |
kind |
string|null |
Required: The kind of pusher to configure. |
lang |
string |
Required if |
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 |
| Name | Type | Description |
|---|---|---|
format |
string |
The format to send notifications in to Push Gateways if the
|
url |
URI |
Required if |
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
| 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
| 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/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
| Name | Type | Description |
|---|---|---|
from |
string |
Pagination token to continue from. This should be the |
limit |
integer |
Limit on the number of events to return in this request. |
only |
string |
Allows basic filtering of events returned. Supply |
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 |
notifications |
[Notification] |
Required: The list of events that triggered notifications. |
| 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. |
| 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 |
type |
string |
Required: The type of the event. |
unsigned |
UnsignedData |
Contains optional extra information about the event. |
| 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 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 |
prev_content |
EventContent |
The previous Changed in |
redacted_because |
ClientEventWithoutRoomID |
The event that redacted this event, if any. |
transaction_id |
string |
The client-supplied transaction ID, for example, provided via
|
{
"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
}
]
}
接收通知
服务器必须在客户端的 /sync 流中包含未读通知数,并随计数变化更新。通知判定依赖于事件适用的推送规则。
对于加密事件,主服务器仅有限访问事件内容,推送规则需由客户端在解密后处理,客户端收到每条事件都需执行推送规则。这可能导致需修正从主服务器收到的未读通知数。
标记通知为已读
当用户更新已读回执(无论使用 API 还是通过发送事件),在该事件之前(含自身)的通知必须被标记为已读。具体被影响的事件取决于是否使用了线程已读回执。用户可同时发送 m.read 和 m.read.private 回执,两者都可清除通知。
如同一房间内用户同时拥有 m.read 和 m.read.private,应取更“新”或“更靠前”的回执为判断依据。例如,若事件 A、B、C、D 按时间升序排列,m.read 回执在事件 C,m.read.private 在事件 A,则用户已读至 C。如果 m.read.private 更新为 B 或 C,通知状态不变(m.read 回执仍领先);如 m.read.private 更新为 D,则用户已读到 D(m.read 落后于 m.read.private)。
[Added in v1.4] 处理线程已读回执时,服务器需将通知数分配至各线程(主时间线看作一条线程)。判定事件属于哪个线程,服务器应按event 关系查找,直到遇到 m.thread 关系定义的根事件(详见线程模块),但不建议无限遍历。建议实现时至多跳 3 层,未找到线程即视为事件不属于线程。主要确保后续事件如 m.reaction 被正确划分为某线程。
服务器行为
主服务器收到新事件时,为房间内每位本地用户(不含发信人)处理推送规则,结果可能为:
- 生成新的未读通知数;
- 向配置的推送网关发起请求。
新事件导致的未读通知数必须与事件本身一起在同一次 /sync 响应中返回。
推送网关行为
APNS 推荐
APNS 推送通知的具体格式灵活,由客户端应用与其推送网关约定。由于 APNS 需求发送者具有应用开发者的私钥,每个应用需有专属推送网关。推荐如下:
- APNS Token 基于 base64 编码,直接用于 pushkey。
- 正式环境和沙箱环境使用不同的 app_id 。
- APNS 推送网关不必等待 APNS 网关返回错误再响应;可记录失败,并在下次推送相同 pushkey 时返回 ‘rejected’。
安全性注意事项
客户端需指定用于发送事件通知的推送网关 URL 。此 URL 必须为 HTTPS,绝不能是 HTTP。
推送通知会经过推送供应商,消息内容应尽量不随推送本体一起发送。推送网关应发送 “sync” 指令,指导客户端直接向主服务器获取新事件。
第三方邀请
本模块为邀请新成员加入房间提供了支持,即使对方的 Matrix 用户 ID 未知,也可以通过第三方标识符(如电子邮件地址)进行邀请。此处包含两种流程:一种是已知该第三方标识符对应的 Matrix 用户 ID,另一种是未知。无论哪种情况,客户端都通过 /invite 接口并提供第三方标识符的详细信息来发起邀请。
主服务器会向身份服务器查询该标识符是否对应已知的 Matrix 用户 ID:
- 如果已知,则直接向该用户发出邀请。
- 如果未知,主服务器会请求身份服务器记录该邀请的详细信息,并在未来为该标识符分配绑定时通知被邀请者的主服务器有待处理的邀请。身份服务器会向发起邀请的主服务器返回一个令牌和公钥。
当被邀请者的主服务器收到绑定通知时,应在房间的事件图中插入一个 m.room.member 事件,其中 content.membership 为 invite,同时包含一个 content.third_party_invite 属性,以证明被邀请者确实拥有该第三方标识符。更多信息请参见 m.room.member 事件架构说明。
事件
m.room.third_party_invite
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. |
| 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"
}
}
客户端行为
客户端通过第三方标识符请求服务器邀请用户。
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
| 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 |
Request body example
{
"address": "cheeky@monkey.com",
"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
|
429 |
This request was rate-limited. |
200 response
{}
403 response
| 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
| 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
}
服务器行为
在收到 /invite 请求后,服务器需要使用指定的身份服务器查询第三方标识符。如果查询结果返回了 Matrix 用户 ID,则可以启动标准的邀请流程。该流程如下所示:
+---------+ +-------------+ +-----------------+
| 客户端 | | 主服务器 | | 身份服务器 |
+---------+ +-------------+ +-----------------+
| | |
| POST /invite | |
|----------------------------------->| |
| | |
| | GET /lookup |
| |--------------------------------------------------->|
| | |
| | 用户 ID 结果 |
| |<---------------------------------------------------|
| | |
| | 对发现的用户 ID 启动邀请流程 |
| |------------------------------------------ |
| | | |
| |<----------------------------------------- |
| | |
| 完成 /invite 请求 | |
|<-----------------------------------| |
| | |
然而,如果查询结果没有返回已绑定的用户 ID,主服务器必须将邀请信息存储在身份服务器上,并向房间发出有效的 m.room.third_party_invite 事件。该流程如下所示:
+---------+ +-------------+ +-----------------+
| 客户端 | | 主服务器 | | 身份服务器 |
+---------+ +-------------+ +-----------------+
| | |
| POST /invite | |
|----------------------------------->| |
| | |
| | GET /lookup |
| |------------------------------------------------------------>|
| | |
| | “无用户” 结果 |
| |<------------------------------------------------------------|
| | |
| | POST /store-invite |
| |------------------------------------------------------------>|
| | |
| | m.room.third_party_invite 事件所需信息 |
| |<------------------------------------------------------------|
| | |
| | 向房间发出 m.room.third_party_invite 事件 |
| |------------------------------------------- |
| | | |
| |<------------------------------------------ |
| | |
| 完成 /invite 请求 | |
|<-----------------------------------| |
| | |
所有主服务器必须验证事件中 content.third_party_invite.signed 对象的签名。
第三方用户随后需要验证其身份,这将使身份服务器调用主服务器,将该第三方标识符绑定到用户的主服务器。此时,主服务器会将房间中的 m.room.third_party_invite 事件替换为该用户的完整 membership: invite 的 m.room.member 事件。
如果主服务器是通过 m.room.third_party_invite 首次加入某个房间,则已在该房间中的服务器(根据标准服务器间协议选定)必须校验用于签名的公钥仍然有效,方法即如上文所述检查 key_validity_url。
其他主服务器不得仅因 key_validity_url 拒绝房间加入请求,这是为了确保所有主服务器都能获得一致的房间视图。但主服务器可以向其客户端指出成员的成员资格可能存在疑问。
例如,假设 H1、H2、H3 为主服务器,UserA 是 H1 的用户,身份服务器为 IS,第三方邀请的完整流程如下图所示。图中假定 H1、H2 已在房间,H3 尝试加入。
+-------+ +-----------------+ +-----+ +-----+ +-----+ +-----+
| UserA | | 第三方用户 | | H1 | | H2 | | H3 | | IS |
+-------+ +-----------------+ +-----+ +-----+ +-----+ +-----+
| | | | | |
| POST /invite 针对第三方用户 | | | |
|--------------------------------->| | | |
| | | | | |
| | | GET /lookup | | |
| | |---------------------------------------------------------------------------------------------->|
| | | | | |
| | | | 查询结果(空对象) |
| | |<----------------------------------------------------------------------------------------------|
| | | | | |
| | | POST /store-invite | | |
| | |---------------------------------------------------------------------------------------------->|
| | | | | |
| | | | 第三方邀请生成的令牌、公钥等 |
| | |<----------------------------------------------------------------------------------------------|
| | | | | |
| | | (联邦)向房间发出 m.room.third_party_invite | | |
| | |----------------------------------------------->| | |
| | | | | |
| 完成 /invite 请求 | | | |
|<---------------------------------| | | |
| | | | | |
| | 验证身份 | | | |
| |-------------------------------------------------------------------------------------------------------------------->|
| | | | | |
| | | | | POST /3pid/onbind |
| | | | |<---------------------------|
| | | | | |
| | | PUT /exchange_third_party_invite/:roomId | |
| | |<-----------------------------------------------------------------| |
| | | | | |
| | | 验证该请求 | | |
| | |------------------- | | |
| | | | | | |
| | |<------------------ | | |
| | | | | |
| | | (联邦)发出 m.room.member 邀请 | | |
| | |----------------------------------------------->| | |
| | | | | |
| | | | | |
| | | (联邦)将 m.room.member 事件发送给 H2 | |
| | |----------------------------------------------------------------->| |
| | | | | |
| | | 完成 /exchange_third_party_invite/:roomId 请求 | |
| | |----------------------------------------------------------------->| |
| | | | | |
| | | | | 加入房间 |
| | | | |------------------------ |
| | | | | | |
| | | | |<----------------------- |
| | | | | |
注意当 H1 向 H2 和 H3 发送 m.room.member 事件时,H1 不需要等待任何服务器确认收到该事件。同样,H1 可以在向 H2、H3 发送 m.room.member 事件的同时完成 /exchange_third_party_invite 请求。另外,H3 可以在任意时刻完成其从 IS 接收到的 /3pid/onbind 请求——此流程中的完成未在图中展示。
H1 必须校验来自 H3 的请求,确保 signed 属性正确,且 key_validity_url 仍有效。此操作需向 身份服务器 /isvalid 端点发起请求,且应使用已提供的 URL 而非自行构造。查询字符串和返回值必须符合身份服务规范。
不允许其他主服务器仅基于校验 key_validity_url 的结果拒绝事件,因为我们必须保证事件的接受方式具备确定性。如果某些参与服务器无法连接到密钥服务器、密钥服务器宕机或吊销密钥,则其他服务器会拒绝该事件,导致参与服务器的事件图发生分歧。此行为依赖参与服务器间的信任,但该信任已通过服务器间协议隐含。此外,还必须完成公钥签名验证,从而最大限度减少攻击面。
安全性考虑
本模块涉及若干隐私和信任问题。
为保护用户隐私,必须强力防止 Matrix 用户 ID 与第三方标识符之间的映射泄露。尤其是,应尽量阻止通过 Matrix 用户 ID 查找所有第三方标识符(从而能够关联各第三方标识符)。为此,任何事件都不会直接包含第三方标识符,而是采用由身份服务器提供的不透明显示名称。客户端不应因邀请而记住或显示第三方标识符,仅限于邀请方自身的使用。
主服务器不需要信任特定的身份服务器。一般而言,由客户端自行决定信任哪些身份服务器,而非主服务器决定。因此,此 API 从最终用户处获取身份服务器,并未指定受信任服务器集。当然,一些主服务器可能会为其用户提供默认配置或拒绝个别身份服务器,但主服务器无权对其他主服务器的用户信任哪些身份服务器进行干预。
身份服务器或主服务器可能因收到大量请求或存储大量状态而面临拒绝服务攻击风险。如何防御此类风险由实现方自行决定。
服务器端搜索
搜索 API 允许客户端对用户所加入过的所有房间中的事件进行全文搜索,包括用户已经离开的房间。仅搜索用户有权限查看的事件,例如,用户离开房间后发生的事件将不会被包含。
客户端行为
服务器端搜索有一个统一的 HTTP API,具体文档如下。
POST
/_matrix/client/v3/search
Performs a full text search across different categories.
| Rate-limited: | Yes |
|---|---|
| Requires authentication: | Yes |
Request
Request parameters
| Name | Type | Description |
|---|---|---|
next_batch |
string |
The point to return events from. If given, this should be a
|
Request body
| Name | Type | Description |
|---|---|---|
search_categories |
Categories |
Required: Describes which categories to search in and their criteria. |
| Name | Type | Description |
|---|---|---|
room_events |
Room Events Criteria |
Mapping of category name to search 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 One of: |
search_term |
string |
Required: The string to search events for |
| Name | Type | Description |
|---|---|---|
after_limit |
integer |
How many events after the result are
returned. By default, this is |
before_limit |
integer |
How many events before the result are
returned. By default, this is |
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 |
| Name | Type | Description |
|---|---|---|
contains_url |
boolean |
If |
include_redundant_members |
boolean |
If |
lazy_load_members |
boolean |
If |
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 |
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 |
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 |
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 |
unread_thread_notifications |
boolean |
If Added in |
| Name | Type | Description |
|---|---|---|
group_by |
[Group] |
List of groups to request. |
| Name | Type | Description |
|---|---|---|
key |
string |
Key that defines the group. One of: |
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
| Name | Type | Description |
|---|---|---|
search_categories |
Result Categories |
Required: Describes which categories to search in and their criteria. |
| Name | Type | Description |
|---|---|---|
room_events |
Result Room Events |
Mapping of category name to search criteria. |
| 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 |
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 |
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
The key is the room ID for which the |
| 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
|
order |
integer |
Key that can be used to order different groups. |
results |
[string] |
Which results are in this group. |
| 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. |
| 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 |
| 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 |
type |
string |
Required: The type of the event. |
unsigned |
UnsignedData |
Contains optional extra information about the event. |
| 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 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 |
prev_content |
EventContent |
The previous Changed in |
redacted_because |
ClientEvent |
The event that redacted this event, if any. |
transaction_id |
string |
The client-supplied transaction ID, for example, provided via
|
| Name | Type | Description |
|---|---|---|
avatar_url |
URI |
|
displayname |
string |
| 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 |
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
| 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
}
搜索类别
搜索 API 允许客户端按不同类别进行搜索。目前唯一被明确定义的类别是 room_events。
room_events
该类别包含所有用户有权限查看的事件,包括用户已经离开的房间中的事件。搜索会在特定事件类型的特定键值上进行。
支持检索的字段包括:
m.room.message事件的content.bodym.room.name事件的content.namem.room.topic事件的content.topic
加密的房间(端对端加密)不会被包含在搜索范围内。
搜索结果包含一个 rank 键,可用于按相关度排序结果。rank 值越高,结果越相关。
count 字段用于大致表示总结果数。Homeserver 可能会返回一个估计值,而非精确值。
排序方式
客户端可以指定服务器返回结果的排序方式。允许的两种排序方式为:
rank:首先返回最相关的结果。recent:首先返回最新的结果。
默认排序方式为 rank。
分组
客户端可以请求返回带有分组信息的结果,例如按 room_id 分组。在这种情况下,响应中会包含每个不同 room_id 的分组条目。每个分组条目至少包含该分组内的 event_id 列表,也可能包含关于该分组的其他元数据。
当前要求支持的分组方式有:
room_idsender
分页
服务器响应中各处可能会返回一个 next_batch 键。它用于对结果进行分页。若需获取更多结果,客户端应使用相同的请求,并将 next_batch 查询参数设置为该标记。
分页的范围取决于 next_batch 标记返回的位置。例如,在分组内使用该标记将返回该分组中的更多结果。
目前支持的 next_batch 标记位置有:
search_categories.<category>.next_batchsearch_categories.<category>.groups.<group_key>.<group_id>.next_batch
即使存在更多匹配结果,服务器也可以选择不支持分页。在这种情况下,响应中不得返回 next_batch 标记。
安全性注意事项
服务器只能返回用户有权限查看的结果。
客户端访客访问
有时,客户端无需在主服务器上完整注册账户或加入房间即可与房间进行交互是更为理想的。本模块规定了此类客户端应如何与服务器交互,以便以访客身份参与房间。
访客用户通过使用普通的注册接口从主服务器获取访问令牌,将 kind 参数指定为 guest。随后,他们可以像其他用户一样与客户端-服务器 API 进行交互,但如客户端行为子章节所述,仅能访问 API 的子集。主服务器可以选择是否允许本地用户的访客访问,但并不了解其他主服务器上的用户是否为访客。
访客用户也可以通过正常的 register 流程升级其账户,需额外指定一个 POST 参数 guest_access_token,其值为访客令牌。同时,他们还需指定 username 参数(即用户名本地部分),该参数在其他情况下为可选项。
本模块并未完全考虑联合互通(Federation);它依赖于单个主服务器正确遵守本模块设定的规则,而不是允许所有主服务器相互强制执行这些规则。
事件
m.room.guest_access
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: |
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"
}
}
客户端行为
访客账户允许通过以下 API 端点获取事件及相关媒体资源:
- GET /rooms/{roomId}/state
- GET /rooms/{roomId}/context/{eventId}
- GET /rooms/{roomId}/event/{eventId}
- GET /rooms/{roomId}/state/{eventType}/{stateKey}
- GET /rooms/{roomId}/messages
- [Added in
v1.1] GET /rooms/{roomId}/members - GET /rooms/{roomId}/initialSync
- GET /sync
- GET /events 用于房间预览。
- [Added in
v1.12] GET /media/download/{serverName}/{mediaId} - [Added in
v1.12] GET /media/download/{serverName}/{mediaId}/{fileName} - [Added in
v1.12] GET /media/thumbnail/{serverName}/{mediaId}
访客账户允许通过以下 API 端点发送事件:
-
PUT /rooms/{roomId}/send/{eventType}/{txnId}
- [Changed in
v1.2] 访客现在可以发送任何类型的事件,而不仅限于m.room.message事件。
- [Changed in
-
[Added in
v1.2] PUT /rooms/{roomId}/state/{eventType}/{stateKey}
访客账户允许通过以下 API 端点维护其自身账户:
- PUT /profile/{userId}/displayname
- GET /devices
- GET /devices/{deviceId}
- PUT /devices/{deviceId}
- [Added in
v1.2] GET /account/whoami
访客账户允许通过以下 API 端点进行端到端加密操作:
服务器行为
服务器必须仅在房间内存在 m.room.guest_access 状态事件且其 guest_access 值为 can_join 时允许访客用户加入房间。如果 m.room.guest_access 事件被修改为不再允许访客加入,服务器必须将这些访客用户的 m.room.member 状态设置为 leave。
安全性考量
每个主服务器自行管理其访客账户,身份是否为访客账户的信息不会在服务器之间传递。因此,参与房间的任何服务器都被信任能够正确执行本节列出的权限规定。
主服务器可以考虑在访客注册时启用如验证码等保护措施,以防止垃圾信息、拒绝服务(DoS)等攻击。
主服务器可能会对访客账户采取更严格的速率限制,尤其是针对发送状态事件的操作。
房间预览
有时,允许用户在未加入房间的情况下“旁听”并阅读房间中发布的消息,即为房间提供预览,是十分有用的功能。当与 访客访问 结合使用时,这一功能效果尤为明显。
房间预览通过 world_readable 的 房间历史消息可见性 设置以及 GET /events 接口的特殊版本实现。
客户端行为
希望在未加入房间的情况下查看房间内容的客户端,应调用 GET /rooms/:room_id/initialSync,然后调用 GET /events。对于每一个希望查看的房间,客户端都需要并行执行此操作。
当然,客户端也可以调用其他接口,例如 GET /rooms/:room_id/messages 和 GET /search,以访问 /events 流以外的事件。
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
| 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 |
start |
string |
A token which correlates to the first value in |
| 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 |
type |
string |
Required: The type of the event. |
unsigned |
UnsignedData |
Contains optional extra information about the event. |
| 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 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 |
prev_content |
EventContent |
The previous Changed in |
redacted_because |
ClientEvent |
The event that redacted this event, if any. |
transaction_id |
string |
The client-supplied transaction ID, for example, provided via
|
{
"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"
}
服务器行为
对于尚未加入房间的客户端,服务器仅需返回这些事件:在事件发生时,房间状态存在 m.room.history_visibility 状态事件,且其 history_visibility 值为 world_readable。
安全性注意事项
客户端可以向用户展示房间处于 world_readable 状态时,可能会向未加入房间的用户显示消息。通过此模块无法判断是否确有未加入的访客用户实际查看了房间中的事件,也无法列举或统计正在“旁听”的用户数量。
房间标签
用户可以为房间添加标签。标签是带有命名空间的字符串,用于标记房间。一个房间可以有多个标签。标签仅对设置它们的用户可见,但会在该用户的所有设备之间同步。
事件
房间上的标签通过房间的 account_data 部分中的单个 m.tag 事件接收。m.tag 事件的内容包含一个 tags 键,其值是一个对象,将每个标签的名称映射到另一个对象。
与每个标签关联的 JSON 对象包含关于该标签的信息,例如如何对带有某一标签的房间进行排序。
排序信息通过 order 键给出,值为介于 0 到 1 之间的数字。数字比较时,0 会最先显示。因此,order 为 0.2 的房间将显示在 order 为 0.7 的房间之前。如果房间拥有某标签但未包含 order 键,则应排在具有该标签且含有 order 键的房间之后。
标签的名称不得超过 255 字节。
标签的命名空间定义如下:
m.*命名空间保留用于 Matrix 协议中定义的标签。客户端必须忽略这一命名空间中自己不支持的标签。u.*命名空间保留给用户自定义的标签。u.之后的字符串定义为该标签的显示名称。客户端不应从该命名空间的标签推断除显示名之外的含义。- 客户端或应用如果需使用特殊标签以实现高级功能,应参照 state key 方式使用命名空间,例如:
tld.name.* - 任何以
tld.name.*形式出现但与当前客户端命名空间不匹配的标签都应被忽略 - 不符合上述规则的标签应被解释为来自
u.*命名空间的用户标签,视为其名称前已被去掉u.(即标签的名称直接作为显示名)。这些无命名空间的标签是出于历史原因而被支持。新标签应当使用上述定义的命名空间之一。
协议中列出了一些特殊的标签名称:以下标签被定义在 m.* 命名空间下:
m.favourite:用户收藏的房间。应当优先于其他房间显示。m.lowpriority:应当低于其他房间显示优先级。m.server_notice:用于标识服务器通知房间。
m.tag
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. |
| Name | Type | Description |
|---|---|---|
order |
number |
A number in a range |
Examples
{
"content": {
"tags": {
"u.work": {
"order": 0.9
}
}
},
"type": "m.tag"
}
客户端行为
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
| 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} |
| Name | Type | Description |
|---|---|---|
order |
number |
A number in a range |
{
"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
| 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
| Name | Type | Description |
|---|---|---|
order |
number |
A number in a range |
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
| 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
{}
客户端配置
客户端可以在他们的主服务器上为其账户存储自定义配置数据。这些账户数据将在不同设备间同步,并且能够在特定设备上的多次安装之间保持持久性。用户只能查看其自己账户的账户数据。
账户数据可以是全局的,也可以限定在特定房间内。这里没有继承机制:如果某个 type 类型的数据在房间的账户数据中缺失,不会自动回退到同类型的全局账户数据。
事件
客户端通过 /sync 响应中的 account_data 部分接收账户数据事件。
这些事件也可以在 /events 响应中接收,或者作为房间 /sync 响应中的 account_data 部分收到。出现在 /events 中的 m.tag 事件会带有所对应房间的 room_id。
客户端行为
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
| 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
| 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
| 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
| 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
| 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
| 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
| 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
| 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
| 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
| 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
| 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
| 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
| 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
| 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
| 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."
}
服务器行为
服务器必须拒绝设置服务器自身管理的事件类型的账户数据,并返回 405 错误响应。
目前,仅包括 m.fully_read
和 m.push_rules 事件类型。这一规定同样适用于全局和按房间划分的账户数据。
[Changed in v1.10] m.push_rules 被添加到了拒绝列表中。
服务器必须允许客户端正常读取上述事件类型的数据。
服务器管理
该模块为服务器管理员增加了检查服务器状态和数据的能力。
客户端行为
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
| 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. |
| Name | Type | Description |
|---|---|---|
sessions |
[SessionInfo] |
A user’s sessions (i.e. what they did with an access token from one login). |
| Name | Type | Description |
|---|---|---|
connections |
[ConnectionInfo] |
Information particular connections in the session. |
| 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"
}
事件上下文
该 API 返回发生在指定事件之前和之后的一系列事件。这样,客户端可以获取围绕某一事件的上下文信息。
客户端行为
用于检索事件上下文的 HTTP API 只有一个,具体文档如下。
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
| Name | Type | Description |
|---|---|---|
eventId |
string |
Required: The event to get context around. |
roomId |
string |
Required: The room to get events from. |
| Name | Type | Description |
|---|---|---|
filter |
string |
A JSON See Filtering for more information. |
limit |
integer |
The maximum number of context events to return. The limit applies
to the sum of the |
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. |
| 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 |
type |
string |
Required: The type of the event. |
unsigned |
UnsignedData |
Contains optional extra information about the event. |
| 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 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 |
prev_content |
EventContent |
The previous Changed in |
redacted_because |
ClientEvent |
The event that redacted this event, if any. |
transaction_id |
string |
The client-supplied transaction ID, for example, provided via
|
{
"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"
}
}
]
}
安全注意事项
服务器只能返回用户有权限查看的结果。
SSO 客户端登录/认证
单点登录(Single Sign-On, SSO)是一个通用术语,指的是允许用户通过单一的基于 Web 的认证门户登录应用程序的协议。例如 OpenID Connect、“中央认证服务”(CAS)以及 SAML。
本模块允许 Matrix 主服务器将用户认证委托给支持这些协议之一的外部认证服务器。在此过程中,涉及三个系统:
- 一个使用本规范定义的 API 的 Matrix 客户端,它试图将用户认证至 Matrix 主服务器。
- 一个实现了本规范所定义 API 的 Matrix 主服务器,但其用户认证委托给了认证服务器。
- 一个“认证服务器”,负责对用户进行认证。
本规范只关注 Matrix 客户端与主服务器之间的通信,与用于与认证服务器通信的 SSO 协议无关。不同的 Matrix 主服务器实现可能支持不同的 SSO 协议。
实现 SSO 流程的客户端和主服务器需要同时考虑 登录 和 用户交互式认证。两个过程类似,但存在细微差异。
通常,SSO 系统要求在认证服务器上配置一个“回调”URI。用户完成认证后,浏览器会被重定向到该 URI。Matrix 主服务器的实现需提供合适的端点。例如,对于 CAS 认证,主服务器应为管理员提供配置 CAS 服务器及消费票据的 REST 端点的方法。
主服务器可以选择性地向用户显示多个可选的 SSO 选项,通常以多个“使用 $provider 登录”按钮的形式展示。这些被称为“身份提供者”(IdPs)。
客户端通过 SSO 登录
流程概述如下:
- Matrix 客户端调用
GET /login,以查询支持的登录类型,主服务器在响应中包含带有"type": "m.login.sso"的流程。 - 为发起
m.login.sso登录类型,Matrix 客户端引导用户浏览器跳转至用户主服务器上的/login/sso/redirect端点。若用户选择了某个identity_providers,则可能是该端点的 IdP 版本。 - 主服务器以 HTTP 重定向响应至 SSO 用户界面,浏览器跟随重定向。
- 认证服务器与主服务器交互,验证用户身份及其他认证信息,过程中可能涉及多次重定向。
- 浏览器被引导至客户端提供的
redirectUrl,并带有loginToken查询参数,供客户端登录使用。 - 客户端通过携带
type为m.login.token的/login端点调用,用登录令牌换取访问令牌。
对于原生应用,1 至 4 步通常通过打开嵌入式 Web 视图实现。
流程如下图所示:
Matrix 客户端 Matrix 主服务器 认证服务器
| | |
|-------------(0) GET /login------->| |
|<-------------登录类型--------------| |
| | |
| Webview | |
| | | |
|----->| | |
| |--(1) GET /login/sso/redirect-->| |
| |<---------(2) 302----------------| |
| | | |
| |<========(3) 认证流程==============>| |
| | | |
| |<--(4) 重定向到 redirectUrl--| |
|<-----| | |
| | |
|---(5) POST /login (携 login token)->| |
|<-------------访问令牌---------------| |
在此规范的旧版 r0.4.0
版本 中,如果主服务器提供 m.login.cas 登录流程,则可以通过 CAS 认证。本规范废弃了 m.login.cas 的使用,转而推荐使用 m.login.sso,其过程相同,仅区别在于重定向端点:对于 m.login.cas,使用 /cas/redirect,对于 m.login.sso,使用 /sso/redirect(见下文)。其他端点保持一致。
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 One of: |
| 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 Unregistered brands are permitted using the Common Namespaced Identifier Grammar,
though excluding the namespace requirements. For example, |
icon |
string |
Optional Clients SHOULD use the deprecated 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"
}
客户端行为
客户端通过引导浏览器跳转至 /login/sso/redirect
(或使用某个 identity_providers 时跳转至 /login/sso/redirect/{idpId})
并携带适当的 redirectUrl 启动流程。认证成功后,浏览器将被重定向至该 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
| 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
| Name | Type | Description |
|---|---|---|
idpId |
string |
Required: The |
| 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. |
安全注意事项
-
通过操控
redirectUrl参数进行的 CSRF 攻击客户端应校验对
redirectUrl的任何请求。攻击者可能伪造查询参数,导致跨站请求伪造(CSRF)攻击。例如,假设一个 Web 客户端托管于
https://client.example.com,希望在主服务器 服务器名称 为server.example.org上发起 SSO 登录,其通过在redirectUrl查询参数中存储服务器名实现重定向:https://server.example.org/login/sso/redirect?redirectUrl=https://client.example.com?hs=server.example.org。攻击者可能诱导受害者访问
https://server.example.org/login/sso/redirect?redirectUrl=https://client.example.com?hs=evil.com,导致客户端将登录令牌发送到攻击者控制的evil.com。为防范此类风险,客户端不得将状态(如登录主服务器的地址)存储于任何可能被外部进程修改的位置。
状态应存储在 localStorage 或 Cookie 中。
-
为进一步安全,客户端应在
redirectUrl中携带唯一标识符,并拒绝不含已知标识符的回调,以防未授权登录尝试和重放攻击。
服务器行为
服务器应注意,identity_providers 是可选的,较老的客户端可能无法正确解析该值。在这些情况下,客户端将使用通用的 /redirect 端点,而不是 /redirect/{idpId} 端点。
重定向到认证服务器
服务器应如下处理 /_matrix/client/v3/login/sso/redirect:
- 构建适合 SSO 系统的请求。
- 存储足够的状态信息,以便在 SSO 流程完成后能够安全恢复流程。可通过为用户浏览器设置 Cookie 的方式完成,即在响应头中添加
Set-Cookie。 - 将用户浏览器重定向到 SSO 登录页,并带上合适参数。
另请参见下方“安全注意事项”。
处理认证服务器回调
通常,会有一个回调 URI 同时用于登录与用户交互式认证,由主服务器实现区分当前流程。
主服务器应验证来自 SSO 系统的响应:这可能需要对认证服务器发起附加调用,和/或验证响应中的签名。
主服务器后续操作如下:
- 主服务器必须将认证服务器收到的用户详细信息映射为合法的 Matrix 用户标识符。可参见 其他字符集的映射 指南。
- 若生成的用户标识符为新用户,则应注册为新用户。
- 主服务器应生成一个短期登录令牌(login token)。这是一个不透明令牌,可用于以
m.login.token类型调用/loginAPI。令牌有效期建议限制在五秒左右。 - 主服务器在最初的
/_matrix/client/v3/login/sso/redirect请求所带的redirectUrl上,添加名为loginToken的查询参数,值为生成的登录令牌。(注意:redirectURL可能含有已有的查询参数。若已存在一个或多个loginToken参数,应先移除后再添加新的。) - 主服务器将浏览器重定向到构建完成的 URI。
安全注意事项
-
主服务器应确保登录令牌不会发送给恶意客户端。
例如,假设主服务器为
server.example.org。攻击者诱导受害者点击https://server.example.org/login/sso/redirect?redirectUrl=https://evil.com,结果导致登录令牌发送至攻击者控制的evil.com,这属于 CSRF 攻击。为缓解风险,主服务器在处理
/_matrix/client/v3/login/sso/redirect端点时,重定向到 SSO 登录页前或在认证服务器回调后,应征得用户同意,将 Matrix 账号的访问权限授予redirectUrl指定的站点。可以设置信任白名单,仅允许已知受信任的客户端 URL。主服务器自身的 登录回退 实现可排除在外。
-
为进一步安全,主服务器可追踪待处理请求,防止未授权认证。可通过在处理
/_matrix/client/v3/login/sso/redirect时设置 Cookie,并在从认证服务器回调时校验并清除该 Cookie 实现。
用户交互式认证中的 SSO
用户交互式认证 适用于客户端-服务器端点,需要对用户身份进行额外确认(超出持有访问令牌以外)。通常情况下用户需要重新输入密码,但对于将认证委托给 SSO 服务器的主服务器,这意味着在用户交互式认证过程中重定向到认证服务器。
该实现基于用户交互式认证的 回退 机制。
客户端行为
客户端除确保实现回退机制,并将 m.login.sso 类型认作任意未知类型外,无需额外操作,即应为
/_matrix/client/v3/auth/m.login.sso/fallback/web?session=<session_id>
打开浏览器窗口。流程完成后,客户端仅携带 session 重新尝试请求。
服务器行为
重定向至认证服务器
服务器应以类似于 /_matrix/client/v3/login/sso/redirect 的方式处理
/_matrix/client/v3/auth/m.login.sso/fallback/web:
- 构建适合 SSO 系统的请求。
- 存储足够的状态信息,以便在 SSO 流程完成后能够安全恢复流程。可通过为用户浏览器设置 Cookie 的方式实现,即在响应头设置
Set-Cookie。 - 将用户浏览器重定向到 SSO 登录页,并携带适当参数。
另请参阅下方“安全注意事项”。
处理来自认证服务器的回调
通常,会有单一回调 URI 同时用于登录和用户交互式认证,由主服务器确定当前流程。
主服务器应对来自 SSO 系统的响应进行验证:这可能需要对认证服务器进行额外调用,和/或验证响应的签名。
随后主服务器向用户浏览器返回用户交互式认证回退完成页面。
安全注意事项
-
操作确认
主服务器应确认用户同意继续操作。用户交互式认证的目标是防止被盗用的
access_token用于接管用户账号。仅重定向到 SSO 系统远远不够,因为用户可能尚未意识到正在进行的操作,或 SSO 系统可能会自动确认认证。例如,主服务器可向用户展示类似如下内容的页面:
某客户端正在尝试从您的账户移除设备。请通过单点登录重新认证以确认此操作。如果此操作并非您本人发起,请警惕您的账户可能被盗用!
此操作确认可发生在重定向到 SSO 认证页前(处理
/_matrix/client/v3/auth/m.login.sso/fallback/web端点时)、或认证服务器回调后。如果在认证前确认,尤其要防范下述未授权认证尝试。 -
为进一步安全,主服务器应追踪待处理请求,防止未授权认证。例如可在处理
/_matrix/client/v3/auth/m.login.sso/fallback/web时设置 Cookie,并在认证服务器回调时校验并清除该 Cookie。
私信
Matrix 中的所有通信都发生在房间内。有时,用户希望能够直接与某一个特定的人交流。本模块定义了一种方法,可以将某些房间标记为与指定对象的“私聊”。这并不意味着聊天只能局限于两个人之间,因为这会排除自动“机器人”用户,或者“个人助手”等替用户在其不在时回复私信的功能。
一个房间未必会被房间内所有成员视为“私聊”,但存在一种信号机制,用于向被邀请人传递聊天是否为“私聊”的信息。
事件
m.direct
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"
}
客户端行为
要与其他用户发起私聊,邀请方的客户端应在 /createRoom 时设置 is_direct 标志。当用户操作的流程意图是与某人直接交流,而不是邀请该人加入一个公共房间时,客户端都应设置该标志。例如,用户在某个人的头像旁点击“开始聊天”时,意味着应设置 is_direct 标志。
被邀请方的客户端可以利用 m.room.member 事件中的 is_direct 标志自动将该房间标记为私聊,但这不是强制要求:客户端也可以弹窗提示用户,或完全忽略该标志。
邀请人和被邀请人的客户端都应通过在账户数据中存储一个 m.direct 事件,将该房间记为私聊,接口为 /user/<user_id>/account_data/<type>。
服务端行为
当 /createRoom 接口中提供了 is_direct 标志时,所属服务器必须在 /createRoom 调用所邀请成员的邀请事件中设置 is_direct 标志。
忽略用户
在通过 Matrix 进行通信时,您可能希望出于某些原因忽略特定用户。本模块定义了客户端和服务器如何实现对用户的忽略。
事件
m.ignored_user_list
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"
}
客户端行为
若要忽略(实质上是屏蔽)某个用户,客户端应将目标用户添加至其账户数据中的 m.ignored_user_list 事件,使用 /user/<user_id>/account_data/<type> 完成操作。用户被忽略后,客户端将不再收到该用户发送的事件,状态事件除外。客户端应选择隐藏被新忽略用户之前发送的内容,或执行一次不带历史 token 的新 /sync。
被忽略用户对新房间的邀请将不会发送给客户端。服务器也可以选择代表客户端拒绝该邀请。
即使用户被忽略,状态事件仍然会发送给客户端。这是为了确保某些部分(如房间名)不会因为用户被忽略而在客户端表现出不同。
要将某个用户从忽略列表中移除,只需从账户数据事件中移除该用户。服务器将恢复发送此前被忽略用户的事件,但不应发送用户被忽略期间错过的事件。若需接收到被忽略期间发送的事件,客户端应执行一次全新同步。客户端还可取消对之前因忽略该用户而隐藏的事件的隐藏。
服务器行为
在 m.ignored_user_list 更新后,所有客户端的同步 API 应立即开始忽略(或取消忽略)相关用户。客户端负责确定是隐藏已发送的事件,还是开启新的同步流。
服务器仍需向客户端发送被忽略用户发送的状态事件。
服务器不得将被忽略用户发起的房间邀请发送给客户端。服务器也可以选择拒绝该邀请。
贴纸消息
该模块允许用户在房间或直接消息会话中发送贴纸消息。
贴纸消息是一种特殊的图片消息,展示时不带有控件(例如,不会显示“下载”链接,或在点击时以灯箱方式显示,如 m.image 事件中所示)。
贴纸消息旨在在消息时间线上提供简单的“反馈”事件。Matrix 客户端应提供某种机制,以显示贴纸的“body”,例如在悬停时作为工具提示显示,或在点击贴纸图片时以弹窗形式展示。
事件
贴纸事件以单个 m.sticker 事件的形式,在房间的 timeline 部分,通过 /sync 接收。
m.sticker
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 |
string |
Required: The URL to the sticker image. This must be a valid |
| 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. |
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 |
string |
The URL (typically |
w |
integer |
The intended display width of the image in pixels. This may differ from the intrinsic dimensions of the image file. |
| 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. |
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"
}
}
客户端行为
支持此消息类型的客户端应直接在时间线中展示事件 URL 指向的图片内容。
应在 info 对象中提供缩略图图片。这主要是为那些尚未完全支持 m.sticker 事件类型的客户端提供的降级方案。在大多数情况下,将缩略图 URL 设为与主事件内容相同的 URL 即可。
建议贴纸图片内容的大小为 512x512 像素或更小。图片文件的尺寸应为 info 对象中指定的理想展示尺寸的两倍,以便在高 DPI 屏幕上渲染更清晰的图像。
举报内容
用户可能会遇到他们认为不适当的内容,并且应当能够将其举报给服务器管理员或房间管理员进行审核。本模块定义了用户举报内容的方法。
客户端行为
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
| Name | Type | Description |
|---|---|---|
roomId |
string |
Required: The room being reported. |
Request body
| Name | Type | Description |
|---|---|---|
reason |
string |
Required: The reason the room is being reported. May be blank. |
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
| 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
| 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.
Furthermore, it might be possible for clients to deduce whether a reported event exists by timing the response. This is because only a report for an existing event will require the homeserver to do further processing. To combat this, homeservers MAY add a random delay when generating a response.
| Rate-limited: | No |
|---|---|
| Requires authentication: | Yes |
Request
Request 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
| 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."
}
POST
/_matrix/client/v3/users/{userId}/report
Added in v1.14
Reports a user 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 any rooms that the reported user is joined to.
Clients may wish to ignore users after reporting them.
Clients could infer whether a reported user exists based on the 404 response. Homeservers that wish to conceal this information MAY return 200 responses regardless of the existence of the reported user.
Furthermore, it might be possible for clients to deduce whether a reported user exists by timing the response. This is because only a report for an existing user will require the homeserver to do further processing. To combat this, homeservers MAY add a random delay when generating a response.
| Rate-limited: | Yes |
|---|---|
| Requires authentication: | Yes |
Request
Request parameters
| Name | Type | Description |
|---|---|---|
userId |
User ID |
Required: The user being reported. |
Request body
| Name | Type | Description |
|---|---|---|
reason |
string |
Required: The reason the room is being reported. May be blank. |
Request body example
{
"reason": "this makes me sad"
}
Responses
| Status | Description |
|---|---|
200 |
The user has been reported successfully or the server chose to not disclose whether the users exists. |
404 |
The user was not found on the homeserver. |
429 |
This request was rate-limited. |
200 response
{}
404 response
| Name | Type | Description |
|---|---|---|
errcode |
string |
Required: An error code. |
error |
string |
A human-readable error message. |
{
"errcode": "M_NOT_FOUND",
"error": "The user was not found."
}
429 response
| 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
}
服务器行为
服务器可以以任何他们认为合适的方式处理被举报的内容。这可能包括专用的房间,用于提醒服务器管理员有关被举报内容的信息,或者其他通知相关人员的机制。
尤其是在有害内容爆发期间,用户可能会举报整个房间而不仅仅是单个事件。因此,服务器管理员和安全团队在处理举报时,应当谨慎,避免关闭那些可能本身是合法的房间。
[Changed in v1.8] 在处理事件举报时,服务器在接受举报前必须验证举报用户当前已加入该事件所在的房间。
[Added in v1.13] 相反,服务器不得根据举报用户是否已加入房间来限制对房间的举报。这是因为用户即使未加入房间,也可能会接触到有害内容。例如,通过房间目录或邀请等途径。
[Added in v1.14] 同样,服务器不得根据举报用户是否已加入被举报用户所在的任意房间来限制对用户的举报。这是因为用户即使未加入房间,也可能会接触到有害内容。例如,通过用户目录或邀请等途径。
第三方网络
应用服务可以通过桥接提供对第三方网络的访问。这使得 Matrix 用户能够与其他通信平台上的用户进行交流,消息由应用服务在双方之间传递。单个应用服务可以桥接多个第三方网络,以及这些网络中的许多具体位置。单个第三方网络位置可以桥接到多个 Matrix 房间。
第三方查找
客户端可能希望为加入第三方位置和连接第三方用户提供丰富的界面。实现此类界面所需的信息由第三方查找功能提供。
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
| 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.
| 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
| 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
| Name | Type | Description |
|---|---|---|
protocol |
string |
Required: The protocol used to communicate to the third-party network. |
| 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.
| 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
| 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
| 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
| Name | Type | Description |
|---|---|---|
field_types |
{string: Field Type} |
Required: The type definitions for the fields defined in 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. The instances are modified by the homeserver from the response of
|
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. |
| 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. |
| Name | Type | Description |
|---|---|---|
desc |
string |
Required: A human-readable description for the protocol, such as the name. |
fields |
object |
Required: Preset values for |
icon |
string |
An optional content URI representing the protocol. Overrides the one provided at the higher level Protocol object. |
instance_id |
string |
A unique identifier for this instance on the homeserver. This field is added
to the response of This is the identifier to use as the |
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",
"instance_id": "irc-freenode",
"network_id": "freenode"
}
],
"location_fields": [
"network",
"channel"
],
"user_fields": [
"network",
"nickname"
]
}
404 response
| 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. |
| Name | Type | Description |
|---|---|---|
field_types |
{string: Field Type} |
Required: The type definitions for the fields defined in 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. The instances are modified by the homeserver from the response of
|
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. |
| 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. |
| Name | Type | Description |
|---|---|---|
desc |
string |
Required: A human-readable description for the protocol, such as the name. |
fields |
object |
Required: Preset values for |
icon |
string |
An optional content URI representing the protocol. Overrides the one provided at the higher level Protocol object. |
instance_id |
string |
A unique identifier for this instance on the homeserver. This field is added
to the response of This is the identifier to use as the |
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",
"instance_id": "gitter-gitter",
"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",
"instance_id": "irc-freenode",
"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
| 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.
| 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
| 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
| Name | Type | Description |
|---|---|---|
protocol |
string |
Required: The name of the protocol. |
| 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.
| 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
| Name | Type | Description |
|---|---|---|
errcode |
string |
Required: An error code. |
error |
string |
A human-readable error message. |
{
"errcode": "M_NOT_FOUND"
}
OpenID
该模块允许用户通过第三方服务验证其身份。第三方服务需要具备 Matrix 兼容性,即需要能够解析 Matrix 主服务器以便用用户的令牌交换身份信息。
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
| 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
| 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 |
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 |
{
"access_token": "SomeT0kenHere",
"expires_in": 3600,
"matrix_server_name": "example.com",
"token_type": "Bearer"
}
429 response
| 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
}
房间的服务器访问控制列表(ACLs)
在某些场景下,房间管理员可能希望阻止恶意或不受信任的服务器参与他们的房间。向房间发送一个 m.room.server_acl 状态事件,是在联邦层面上阻止服务器参与房间的有效方法。
服务器 ACL 也可用于使房间仅与有限的服务器集合联邦,或事后让房间不再与任何其他服务器联邦,这类似于在 m.room.create 事件中设置 m.federate 值。
m.room.server_acl
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:
- If there is no
m.room.server_aclevent in the room state, allow. - If the server name is an IP address (v4 or v6) literal, and
allow_ip_literalsis present andfalse, deny. - If the server name matches an entry in the
denylist, deny. - If the server name matches an entry in the
allowlist, allow. - 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 |
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"
}
}
端口号不受支持,因为解析器无法确定应该匹配端口号还是 IP 地址字面量。此外,几乎不会有人只信任某个域上的特定端口而不信任其他端口,尤其是考虑到服务器主机可以轻易更换端口。
CIDR 表示法不支持 IP 地址,因为 Matrix 并不鼓励使用 IP 作为服务器的身份标识。相反,提供了一个通用的 allow_ip_literals 选项用于全面禁止它们。
客户端行为
客户端除了发送该事件外,无需执行任何额外操作。客户端应在用户界面中描述对服务器 ACL 的更改,例如在时间线上展示。
客户端可以选择在拒绝服务器访问房间之前,先踢出受影响的用户,以帮助防止这些服务器参与,并向用户提供被排除在房间之外的反馈。
服务器行为
当房间状态中存在 m.room.server_acl 事件时,服务器必须阻止被列入黑名单的服务器发送事件或参与房间。具体受影响的 API,在服务器-服务器 API 规范中有详细说明。
如果被拒绝的服务器仍然是房间成员,服务器仍应向其发送事件。
安全性考量
服务器 ACL 只有在房间内的每个服务器都遵守它们时才有效。不遵守 ACL 的服务器仍可能允许被拒绝服务器发送的事件进入房间,并将其泄露给房间内的其他服务器。要在房间有效执行 ACL,还应在房间内拒绝那些不遵守 ACL 的服务器。
用户和房间提及
[Changed in v1.7]
本模块允许用户在房间事件中“提及”其他用户和房间。 这主要用于指示收件人应接收到关于该事件的通知。 通过在事件的 m.mentions 内容属性中包含元数据以引用被提及的实体来实现这一点。
m.mentions 的定义如下:
m.mentions
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.
| Name | Type | Description |
|---|---|---|
room |
boolean |
A boolean set to |
user_ids |
[string] |
A list of Matrix IDs of mentioned users. |
Examples
{
"user_ids": [
"@alice:example.org"
]
}
一个事件的内容示例如下:
{
"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"]
}
}
此外,请参阅 .m.rule.is_user_mention 和
.m.rule.is_room_mention 推送规则。
用户不应将自己的 Matrix ID 添加到 m.mentions 属性中,因为发出的消息无法对本人进行通知。
如果加密事件的载荷中包含 m.mentions,应像普通内容一样进行加密。为正确处理加密房间中的提及,必须首先对事件进行解密。参见接收通知。
请注意,为了向后兼容,像 .m.rule.contains_display_name、
.m.rule.contains_user_name 和
.m.rule.roomnotif 这样的推送规则,仍将在事件的 body 包含用户显示名或 ID 时匹配。为避免无意的通知,建议客户端在每个事件中都包含一个 m.mentions 属性。(若无提及内容,可为一个空对象。)
在以往的规范版本中,用户提及是通过在事件明文 body 中包含用户的显示名或其 Matrix ID localpart 实现的,而房间提及则是包含字符串“@room”。这种方式容易导致混乱和 bug。
客户端行为
虽然可以悄悄地提及用户,但推荐在 m.room.message 事件的 HTML 正文中包含一个 Matrix URI。这仅适用于 msgtype 为 m.text、m.emote 或 m.notice 的 m.room.message 事件。事件的 format 必须为 org.matrix.custom.html,因此需要有 formatted_body。
客户端在向即将发送的事件中添加用于提及的 Matrix URI 时,应遵循以下准则:
- 当链接到用户时,在锚文本中显示用户可能存在歧义的显示名。若用户无显示名,则使用用户的 ID。
- 当链接到房间时,使用该房间的规范别名(canonical alias)。若房间无规范别名,则优先使用房间上列出的别名之一。若找不到任何别名,则退回到房间 ID。在所有情况下,锚文本应为所链接的别名或房间 ID。
锚文本部分应在事件的 body 中用于原本表示该链接的位置,如上例所示。
客户端应将提及与其他元素区分开来。例如,可以通过改变提及的背景色以突出其与普通链接的不同。
如果当前用户在消息中被提及,客户端应将此提及以不同于其他提及的方式显示,例如使用红色背景以提醒用户本人被提及。请注意,用户可能会被提及但事件中未包含其 Matrix URI。
点击提及时,应导航到相应的用户或房间信息。
房间升级
有时,由于各种原因,房间可能需要升级为不同的房间版本。本模块定义了一种在需要时将房间升级到不同房间版本的方法。
事件
m.room.tombstone
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"
}
}
客户端行为
能够识别 m.room.tombstone 事件及 m.room.create 事件中的 predecessor 字段的客户端,应向用户传达房间已升级的信息。一种实现方式是将旧房间从用户的房间列表中隐藏,并显示横幅,在新旧房间间提供相互链接——确保在引用旧房间时永久链接仍能正常工作。另一种做法是虚拟合并房间,使旧房间的时间线能够无缝延续到新房间的时间线,用户无需在房间间切换即可继续体验。
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
| 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
| 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
| 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"
}
服务器行为
当客户端请求将已知房间升级为已知版本时,服务器应:
-
检查用户有权限在房间中发送
m.room.tombstone事件。 -
[Changed in
v1.4] 创建一个替代房间,并在新房间中发送包含predecessor字段、相应room_version以及从前置房间复制的type字段的m.room.create事件。如果前一个房间未设置type,则新房间的创建事件同样不指定type。 -
将可转移的状态事件复制到新房间。具体转移哪些内容留给实现方决定,不过推荐转移的状态事件包括:
m.room.server_aclm.room.encryptionm.room.namem.room.avatarm.room.topicm.room.guest_accessm.room.history_visibilitym.room.join_rulesm.room.power_levels
会员事件不应用于转移到新房间,这是因为服务器在技术上无法冒充来自其他主服务器的用户。此外,服务器也不应转移对发送者有敏感要求的状态事件,例如 Matrix 命名空间之外的事件,客户端可能要求这些事件的发送者满足特定条件。
-
将所有本地别名迁移到新房间。
-
向旧房间发送
m.room.tombstone事件,以指示该房间不再建议继续使用。 -
如有可能,还应修改旧房间的权限级别(power levels),以阻止发送事件和邀请新用户。例如,将
events_default和invite设置为50与users_default + 1中的较大者。
当用户加入新房间时,服务器应自动转移或复制用户的一些个性化设置,如通知、标签等。
服务器通知
Homeserver 提供商通常希望以官方身份向用户发送消息,或者其资源限制会影响用户使用 homeserver 的能力。例如,homeserver 可能每月只允许一定数量的活跃用户,并且已经超出了该限制。为了向用户传达这一限制,homeserver 会使用服务器通知房间(Server Notices Room)。
房间的外观(名称、主题、头像等)作为实现细节留给实现方决定。建议 homeserver 对房间进行装饰,使其在用户看来像是一个官方房间。
事件
通知会作为正常的 m.room.message 事件发送到客户端,在服务器通知房间内的事件 msgtype 为 m.server_notice。客户端必须忽略服务器通知房间外,msgtype 为 m.server_notice 的事件。
server_notice_type 的指定取值如下:
m.server_notice.usage_limit_reached
服务器已超出某项限制,需要服务器管理员进行干预。limit_type 描述已达成的限制类型。limit_type 的指定取值如下:
monthly_active_user
服务器在过去 30 天内的活跃用户数已超过最大值。服务器正在拒绝新的连接。“活跃”的定义作为实现细节留给实现方决定,但建议服务器将同步用户视为“活跃”。
m.room.message,msgtype: m.server_notice
m.room.message,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 |
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
|
msgtype |
string |
Required: One of: |
server_notice_type |
string |
Required: The type of notice being represented. |
Examples
{
"content": {
"admin_contact": "mailto:server.admin@example.org",
"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"
}
}
客户端行为
客户端可以通过房间的 m.server_notice 标签识别服务器通知房间。活跃通知通过服务器通知房间中的置顶事件表示。在该房间中被置顶的服务器通知事件,客户端应通过特殊的用户界面展示给用户,而不是通过普通的置顶事件界面。例如,客户端可以显示警告横幅或弹窗以引起用户注意。在服务器通知房间中被置顶但不是服务器通知事件的事件,应与房间中其他置顶事件一样展示。
客户端不得期望能够拒绝加入服务器通知房间的邀请。尝试拒绝该邀请必须导致返回 M_CANNOT_LEAVE_SERVER_NOTICE_ROOM 错误。服务器不应阻止用户在加入服务器通知房间后离开该房间,但如果服务器要阻止离开房间,则必须使用同样的错误码。
服务器行为
服务器应为每个用户管理正好 1 个服务器通知房间。服务器必须通过 m.server_notice 标签向客户端标识此房间。服务器应向目标用户发送邀请,而不是自动将其加入服务器通知房间。
服务器如何向客户端发送通知、以及使用哪个用户发送事件,均作为服务器的实现细节处理。
内容审核策略列表
Matrix 作为一个开放网络,任何人都可以参与,因而存在着种类极为丰富的内容。让用户有权选择希望看到的内容以及想要屏蔽的内容显得尤为重要。进一步来说,房间管理员和服务器管理员同样应当能够选择他们不希望在其房间和服务器中托管的内容。
协议对此持中立立场:它不应为任何特定实体判断哪些内容为不当内容,而应赋权这些实体自行做出决策。因此,这里描述了一套通用框架,用于传递“内容审核策略列表”或“内容审核策略房间”。需要注意的是,本模块仅描述数据结构,而不涉及如何解读它们:决定过滤内容的实体最适合根据自身需求解读这些规则。
内容审核策略列表以房间状态事件的形式存储。对于房间的配置没有任何限制(可以是公开、私有、加密等)。
目前有三类实体可能会被规则影响:user(用户)、server(服务器)和 room(房间)。三者均以 m.policy.rule.<kind> 状态事件进行描述。策略规则的 state_key 是由规则发送者自定义的任意字符串。
规则包含关于为何制定此规则的建议及原因。reason 是描述 recommendation 的、供人阅读的字符串。目前,仅定义了一个建议类型——m.ban。
m.ban 建议
当使用此建议时,应尽可能禁止规则中所影响的实体参与活动。其执行方式特意作为实现细节留给开发者,以避免协议对如何解读策略列表做出强制规定。以下是一个简单实现建议:
- 针对
user(用户)规则…- 作用于用户:应将该用户加入订阅者的忽略列表。
- 作用于房间:应禁止该用户进入该房间(可见时或立即)。
- 作用于服务器:不应允许该用户向服务器上的其他用户发送邀请。
- 针对
room(房间)规则…- 作用于用户:该用户应离开该房间,并不得重新加入(参见 MSC2270 风格的忽略策略)。
- 作用于房间:无操作,因为房间无法对自己执行禁令。
- 作用于服务器:服务器应阻止用户加入该房间,并阻止他们收到该房间的邀请。
- 针对
server(服务器)规则…- 作用于用户:不应让该用户从该服务器接收事件或邀请。
- 作用于房间:应将该服务器添加到 ACL 的拒绝服务器列表中。
- 作用于服务器:订阅者应尽量避免与该服务器进行联邦交互,通过阻止来自该服务器的邀请,不发送除非必要的流量(不发送外部邀请)来实现。
订阅策略列表
这一部分有意留作实现细节。对于使用客户端-服务器 API 的实现而言,这只需像加入或窥视房间一样简单。但加入或窥视并非必需:实现可以通过轮询更新或采用其他方式接收策略规则的更新。
事件
状态事件中描述的 entity 采用 glob 风格模式进行解释。注意,针对房间的规则既可以描述房间 ID 也可以描述房间别名——如有需要,由订阅者负责将别名解析为房间 ID。
m.policy.rule.user
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 |
reason |
string |
Required: The human-readable description for the |
recommendation |
string |
Required: The suggested action to take. Currently only |
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
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 |
reason |
string |
Required: The human-readable description for the |
recommendation |
string |
Required: The suggested action to take. Currently only |
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
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 |
reason |
string |
Required: The human-readable description for the |
recommendation |
string |
Required: The suggested action to take. Currently only |
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"
}
}
客户端行为
如上所述,客户端行为未做强制定义。
服务器行为
本模块对服务器没有提出额外要求。
安全考虑
本模块可用于构建一套共享黑名单系统,如果部署不当,可能导致现有社区出现分化。对所有社群而言,这未必是合适的解决方案。
具体实现中,依据如何处理订阅关系,用户ID 有可能与策略列表关联进而暴露该用户的立场。例如,若客户端实现让用户加入策略房间,则会向策略房间的观察者显示该用户的 ID。未来,MSC1228 和 MSC1777(或相似手段)可有助于缓解这一问题。
空间
[Added in v1.2]
空间常用于将主题相似的房间进行分组(例如公共的“Official matrix.org rooms”空间或个人的“Work stuff”空间),是一种用于组织房间的方式,同时自身也被表示为房间。
空间通过 m.space 房间类型 定义,因此被称为“空间房间(space-room)”。空间的名称、主题、头像、别名等,均通过空间房间内已有的相关状态事件来定义。
在空间房间内发送普通的 m.room.message 事件是不推荐的——客户端通常并不预期会有方法渲染该房间的时间线。因此,空间房间应当通过设置 m.room.power_levels 中的 events_default 为足够高的数值来禁止普通事件的发布。在默认的权限层级结构中,该值应为 100。客户端还可进一步不对空间房间计入通知数。
空间成员资格通过现有的房间管理机制定义和控制:即 m.room.member、m.room.history_visibility 和 m.room.join_rules。建议公共空间与公共房间采用类似设置:world_readable 的历史可见性、已发布的规范别名以及适当的公共加入规则。邀请(包括第三方邀请)同样适用于空间房间。
常规房间的其他特性也同样适用于空间,如可设置任意状态事件、存储房间账户数据等。空间本质上是具有额外功能的房间。
管理空间内包含的房间/空间
空间组成了一个房间层级体系,客户端可借此将房间列表结构化为树状视图。父子关系有两种定义方式:在空间房间中通过 m.space.child 状态事件,或在子房间中通过 m.space.parent 状态事件。
多数情况下,应同时定义子房间与父房间的关系,以便于发现空间及其内容。仅使用 m.space.child 时,空间实质上就像一个由空间管理者精心挑选的房间列表,而房间本身可能并不知晓被包含其中。仅使用 m.space.parent 时,房间则会被“秘密”添加到空间中,而不会被空间直接宣传。
鉴于空间本身就是房间,因此可以在空间中嵌套空间,也存在创建环路(循环)的可能。尽管明确禁止创建循环,实际实现时仍可能遇到这种情况,必须注意避免无限循环。
客户端及服务器还需警惕树结构过长,以免带来性能问题。
m.space.child 关系
采用此方法时,状态事件发送至作为父房间的空间房间,事件的 state_key 为子房间的ID。
例如,实现以下结构:
#space:example.org
#general:example.org (!abcdefg:example.org)
!private:example.org
则 #space:example.org 的状态为:
为简明起见,省略无关字段。
{
"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"]
}
}
子房间本身无需任何状态事件(当然也可以存在)。如此,用户可以无需房间版主/管理员明确授权,定义个人/私有空间组织自己的房间。
通过在相关状态事件的 content 中省略 via 键(如通过撤回或清空 content),可将子房间从空间中移除。
m.space.child
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 Must consist of ASCII characters within the range
See Ordering of children within a space for information on how the ordering works. |
suggested |
boolean |
Optional (default |
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"
}
}
空间内子项排序
当客户端展示空间的子项时,应按照以下算法排序。在部分场景(如传统的左侧房间列表),客户端可能会覆盖排序规则以提升用户体验。但理论上的空间摘要视图会显示有序的子项。
对于空间的所有子项,先将带有有效 order 键的子项按照 Unicode 码点字典序排序,使得 \x20(空格)在 \x7E(~)之前。再将剩余未定义 order 的子项按其 m.space.child 事件的 origin_server_ts 时间戳升序排列,置于前者之后。
若 order 值相同,则按事件时间戳排序。若时间戳也相同,则按照房间ID(即 state key)升序字典序排列。
注意此处对 ASCII 空格的精确用法,以下为一组空间子项的合理排序示例:
为简明起见,省略无关字段。
[
{
"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"]
}
}
]
!b:example.org排在最前,因为\x20字典序在aaaa之前。!a:example.org紧随其后,因为aaaa字典序在first之前。!c:example.org接下来,因为first是最后一个order值。!e:example.org其后,因为其事件时间戳最小。!d:example.org最后,因为其事件时间戳最大。
m.space.parent 关系
房间还可以通过在自身状态中添加父房间事件来声明属于某个空间。类似空间中的子事件,父事件的 state_key 是父空间的房间ID,content 中的 via 列表用于说明链接是否有效,以及可通过哪些服务器加入。
为避免房间伪称属于某空间,m.space.parent 事件在下列任一条件满足时才应被接受:
- 在设定为父空间的房间中可找到对应的
m.space.child事件。 m.space.parent事件的发送者在该父空间拥有足够权限可发送m.space.child状态事件(无需确有对应子事件存在)。
如客户端尚未加入父空间,可能需要窥探父空间房间状态。若客户端无法窥探,则应认为链接无效。
第二个条件的后果是:若房间管理员在父空间内被降权、退出或被移除父空间,则之前合法的 m.space.parent 事件可能变为无效。
m.space.parent 事件的 content 可包含布尔值 canonical,表示该父空间为该房间的主空间。例如可用于让客户端通过窥探该空间发现其他相关房间并向用户推荐。只能有一个主(canonical)父空间,尽管这一点没有强制约束。若有冲突,采用 Unicode 码点升序排序的最小房间ID进行决议。
m.space.parent
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 When multiple |
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"
}
}
在空间中发现房间
客户端常需帮助用户探索某空间包含哪些房间/空间。可通过在客户端中遍历该空间的 m.space.child 状态事件并窥探房间以获取名称等信息,然而这种方式在大多数场景下并不实用。
为此,提供了一个层级API以深度优先方式遍历空间树并发现带美观细节的房间信息。
GET /hierarchy API 按深度优先方式工作:遇到子项为空间时将递归至该空间,然后返回非空间子房间。
循环虽被禁止,但仍有可能出现。服务器应优雅地中断循环。
此外,某个子房间(例如作为孙子房间)可能多次出现在响应中。
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
| Name | Type | Description |
|---|---|---|
roomId |
string |
Required: The room ID of the space to get a hierarchy for. |
| Name | Type | Description |
|---|---|---|
from |
string |
A pagination token from a previous result. If specified, |
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 |
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
|
403 |
The user cannot view or peek on the room. A meaningful
|
429 |
This request was rate-limited. |
200 response
| Name | Type | Description |
|---|---|---|
next_batch |
string |
A token to supply to |
rooms |
[SpaceHierarchyRoomsChunk] |
Required: The rooms for the current page, with the current filters. The server should return any rooms where at least one of the following conditions is true:
|
| 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 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 |
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 Added in |
topic |
string |
The topic of the room, if any. |
world_readable |
boolean |
Required: Whether the room may be viewed by guest users without joining. |
| Name | Type | Description |
|---|---|---|
content |
EventContent |
Required: The |
origin_server_ts |
integer |
Required: The |
sender |
string |
Required: The |
state_key |
string |
Required: The |
type |
string |
Required: The |
{
"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
| 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
| 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
| 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 /hierarchy 请求所需信息。该端点响应应在一段时间内做缓存。响应还可包含请求用户已加入或本地服务器已知的房间信息——本地数据应优先于远端服务器数据。
请注意,返回客户端的响应基于用户上下文。建议服务器对数据做短暂缓存,但仍须进行权限校验以确保响应对于该用户的准确性。
事件替换
[Added in v1.4]
事件替换,或称“消息编辑事件”,是指那些使用 事件关系,rel_type 为 m.replace 的事件,表示原始事件将被替换。
一条消息编辑事件的示例如下:
{
"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"
}
},
// ... 事件所需的其他字段
}
替换事件的 content 必须包含 m.new_content 属性,用于定义替换后的内容。正常的 content 属性(如 body、msgtype 等)则为不支持替换事件的客户端提供兼容回退。
m.new_content 可以包含事件内容中通常存在的任意属性,例如 formatted_body(参见 m.room.message msgtypes)。
替换事件的有效性
替换事件需满足一系列要求,才能被视为有效替换:
- 如同所有的事件关系一样,原始事件和替换事件必须具有相同的
room_id(即不能在一个房间发送事件,在另一个房间发送其编辑版本)。 - 原始事件与替换事件必须拥有相同的
sender(即不能编辑他人的消息)。 - 替换事件和原始事件的
type必须相同(即不能更改原始事件的类型)。 - 替换事件和原始事件不得包含
state_key属性(即完全不能编辑状态事件)。 - 原始事件本身不能具有
rel_type为m.replace(即不能编辑一条编辑事件——但可以为同一原始事件发送多次编辑)。 - 替换事件(若适用,解密后)必须包含
m.new_content属性。
如果未满足上述任一条件,则实现应忽略该替换事件(不应替换原文内容,也不应将该编辑纳入服务端聚合)。
请注意,替换事件 m.room.message 的 msgtype 属性不必与原始事件相同。例如,将 m.text 事件替换为 m.emote 是合法的。
编辑加密事件
若原始事件是 加密 的,则替换事件也应加密。在这种情况下,m.new_content 被放置于加密负载的内容中。如同所有事件关系,m.relates_to 属性必须位于事件的未加密(明文)部分。
例如,一个加密事件的替换事件可能如下所示:
{
"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>"
}
// 未显示无关字段
}
一旦解密,负载内容可能如下:
{
"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"
}
}
}
请注意:
- 加密负载中没有
m.relates_to属性。如果有,将会被忽略。 m.room.encrypted事件明文内容中没有m.new_content属性。如果有,同样会被忽略。
加密替换事件的负载必须如常加密,包括像往常一样推进任何 Megolm 会话。不应重复使用原有的 Megolm ratchet 条目。
应用 m.new_content
应用替换时,原始事件的 content 被视为被 m.new_content 全量覆盖,仅保留 m.relates_to 属性不变。m.new_content 内部的任何 m.relates_to 属性均被忽略。
例如,给定以下两条事件:
{
"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"
}
}
}
……最终结果如下所示:
{
"event_id": "$original_event",
"type": "m.room.message",
"content": {
"body": "I really like *chocolate* cake",
"msgtype": "m.text",
"com.example.extension_property": "chocolate"
}
}
注意此时 formatted_body 已不存在,因为替换事件中已省略该字段。
服务器行为
服务端对 m.replace 关系的聚合
[Changed in v1.7]
请注意,同一个原始事件可以有多个 m.replace 关系的事件(例如多次编辑)。这些应由主服务器进行 聚合。
m.replace 关系的聚合格式会提供最新的替换事件,格式 同常规。
最新事件通过比较 origin_server_ts 决定;若有两个或以上替换事件 origin_server_ts 相同,则以字典序最大的 event_id 为最新。
同其他子事件聚合一样,对应于 m.replace 关系的聚合包含在被目标事件的 unsigned 的 m.relations 属性下。例如:
{
"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"
}
}
}
}
}
// 未显示无关字段
}
如果原始事件被 抹除,则任何
m.replace 关系不应与其打包(无论后续替换本身是否被抹除)。请注意,此行为特定于 m.replace 关系。另请参考下文 已编辑事件的抹除。
注意:原始事件的 content 保持不变。特别是服务器不应将内容用替换事件内容替换。
此前规范版本要求服务器在向客户端提供已编辑事件时替换其内容(除
GET /_matrix/client/v3/rooms/{roomId}/event/{eventId}
接口外)。然而,这样会导致客户端实现难以保持一致,因此服务器不再进行此操作。
客户端行为
由于服务器不会替换任何已编辑事件内容,客户端应注意所有收到的替换事件,并尽可能和适当时应用替换。
客户端作者请注意 替换事件的有效性 要求,忽略所有无效的替换事件。
永久链接
创建指向事件的链接(即永久链接)时,客户端将构建指向其当前所见事件的链接(可能是消息编辑事件)。
查看该永久链接的客户端应定位到原始事件,并显示该事件的最新版本。
已编辑事件的抹除
当使用 rel_type 为 m.replace 的事件被 抹除 时,该编辑修订被移除。如果有后续编辑,影响较小;但如果这是最新编辑,则事件实际上回退为被抹除编辑前的内容。
抹除原始消息则实际上移除该消息及所有后续编辑,使其不再出现在可见时间线上。在这种情况下,homeserver 会如同处理其他抹除事件一样,为原始事件返回空的 content,且如
前述 替换事件不会打包于原始事件对应的聚合中。注意后续编辑本身并没有被真正抹除:它们仅在可见时间线之外不发挥作用。
带有提及的事件编辑
编辑包含 用户和房间提及 的事件时,替换事件会含有两个 m.mentions 属性:
- 位于
content顶层的,记录该修订中产生的新提及。 - 位于
m.new_content属性内的,记录事件最新版本中所有已解析的提及。
以上差异可确保用户不会对事件的每次编辑都收到通知,但又允许提及新用户(或在编辑幅度足够大的情况下重新通知)。
例如,存在一条提及 Alice 的事件:
{
"event_id": "$original_event",
"type": "m.room.message",
"content": {
"body": "Hello Alice!",
"m.mentions": {
"user_ids": ["@alice:example.org"]
}
}
}
编辑后同时提及 Bob:
{
"content": {
"body": "* Hello Alice & Bob!",
"m.mentions": {
"user_ids": [
// 仅包含新提及的用户
"@bob:example.org"
]
},
"m.new_content": {
"body": "Hello Alice & Bob!",
"m.mentions": {
"user_ids": [
// 包含所有已提及的用户
"@alice:example.org",
"@bob:example.org"
]
},
},
"m.relates_to": {
"rel_type": "m.replace",
"event_id": "$original_event"
}
},
// 事件需要的其他字段
}
若某一修订移除了某个用户的提及,则该用户的 Matrix ID 不应出现在任何 m.mentions 属性中。
客户端也可据此调整 提及事件的客户端行为,通过检查 m.new_content 下的 m.mentions 属性判定事件是否提及当前用户。
回复消息的编辑
对替换 回复 的事件存在特殊约束:与原始回复不同,m.relates_to 对象中不得出现 m.in_reply_to 属性,因为这将显得多余(见上文应用 m.new_content 章节已说明原始事件的 m.relates_to 会保留),且与事件关系机制“一事件只存在一个‘父级’”的理念相悖。
[Changed in v1.13]
规范早期版本允许替换 回复 的事件在 content 中包含回退信息。此规则已废除。
编辑回复的示例如下:
{
"type": "m.room.message",
// 未显示无关字段
"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"
}
}
}
事件注释与反应
[Added in v1.7]
m.annotation 关系类型
注释是一类采用event 关系并带有 rel_type 为 m.annotation 的事件。
注释通常用于“反应”:例如,如果用户想要对某个事件表示点赞,则客户端会发送一个带有相应表情(如👍)的注释事件。另一个可能的用例是让机器人发送事件来指示某条命令的成功或失败。
除了标准属性 event_id 和 rel_type 外,m.relates_to 属性中的 rel_type: m.annotation 应包含一个 key,用于表示所应用的注释。例如,在使用表情符号进行反应时,key 包含所使用的表情符号。
如下所示是一个 m.annotation 关系的示例:
"m.relates_to": {
"rel_type": "m.annotation",
"event_id": "$some_event_id",
"key": "👍"
}
任何类型的事件都可以被注释,包括状态事件。
事件
m.reaction
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. |
| 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 ( |
rel_type |
string |
One of: |
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"
}
}
客户端行为
注释设计的意图是对其进行计数,而不是单独展示。客户端必须为每个事件统计他们观察到的给定事件 type 和注释 key 的注释数量;这些计数通常在时间线上与事件一起显示。
在进行计数时:
-
每个事件
type和注释key通常应分别计数,但是否实际分开计数取决于具体的实现。 -
被忽略用户发送的注释事件应排除在计数之外。
-
来自同一用户(即
sender相同)的多个完全相同的注释(即type与key都相同)应计为一次注释。 -
对于引用了自身带有
m.relates_to,其rel_type为m.annotation或rel_type为m.replace的事件的注释事件,实现应忽略。换句话说,替换事件或注释本身不能被再次注释。注释应仅指向原始事件。 -
当某个注释被撤回时,应从计数中移除。
反应不可编辑,因为替换事件不会更改 m.relates_to(见应用 m.new_content),并且 m.reaction 中没有其他有意义的内容。如果用户希望更改自己的反应,应该撤回原始反应,并发送新的反应事件替代。
m.reaction 中的 key 字段可以为任意字符串,因此客户端在渲染过长反应内容时需采取合理方式。例如,客户端可以省略过长的反应内容。
服务器行为
避免重复注释
HomeServer 应当防止用户针对同一事件,使用相同的事件 type 和注释 key 发送第二次注释(除非第一次发送的事件已被撤回)。
试图发送此类注释应返回 400 错误以及错误码 M_DUPLICATE_ANNOTATION。
但这并不能保证重复的注释不会通过联邦网络到达。客户端在计数注释时有责任对接收到的注释进行去重。
服务器端对 m.annotation 关系的聚合
m.annotation 关系不会被服务器聚合。换句话说,m.annotation 不会包含在 m.relations 属性中。
线程
[Added in v1.4]
线程允许用户在一个房间中以可视方式分支他们的对话。通常在线上讨论多个主题时使用,线程相较于传统的 富回复 能够提供更有组织的交流方式,而富回复未必能够兼顾所有场景。
客户端应当在时间线上以区别于普通消息或回复的方式渲染线程,例如为线程提供一些上下文信息,但将完整的对话历史隐藏于可展开内容之后。
线程通过 rel_type 设为 m.thread 来建立,并引用 线程根(即该线程事件所指向的主时间线事件)。无法从本身已是事件关系子事件(即带有 m.relates_to 和 rel_type 属性的事件,参考 关系类型)创建线程。因此,线程也无法嵌套。
与富回复链不同,线程中的所有事件都引用线程根,而不是最新的消息。
下面通过示例展示线程及其形成方式:
{
// 已省略无关字段
"type": "m.room.message",
"event_id": "$alice_hello",
"sender": "@alice:example.org",
"content": {
"msgtype": "m.text",
"body": "Hello world! How are you?"
}
}
{
// 已省略无关字段
"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?"
}
}
{
// 已省略无关字段
"type": "m.room.message",
"event_id": "$alice_reply",
"sender": "@alice:example.org",
"content": {
"m.relates_to": {
"rel_type": "m.thread",
"event_id": "$alice_hello" // 注意:始终指向 *线程根*
},
"msgtype": "m.text",
"body": "I'm doing great! Thanks for asking."
}
}
如上所示,任何没有 rel_type 的事件都可以仅通过 m.thread 关系被引用来成为线程根。
非线程化客户端的回退机制
能够理解线程的客户端应直接以线程方式处理,但某些客户端(出于历史原因或功能范围限制)可能无法向用户有效展现对话历史。
为此,支持线程的客户端发送事件时应包含 富回复 元数据,以尝试形成对话的回复链。这种方式在高线程活跃房间中并不理想,但可以为用户提供与房间内其他消息相关的上下文信息。
该兼容方式通过合并两种关系并为 is_falling_back 标记设为 true 实现。
// 在事件内容中……
"m.relates_to": {
// m.thread 关系结构
"rel_type": "m.thread",
"event_id": "$root",
// 富回复结构
"m.in_reply_to": {
// 线程中客户端已知的最新消息,应选取其他客户端有较大渲染概率的事件,
// 如 `m.room.message` 事件。
"event_id": "$target"
},
// 标记此事件为带回复回退的线程
"is_falling_back": true
}
对于对线程有一定感知(即不直接渲染线程、但知道规范中有该功能)的客户端,可以将对带有 rel_type 为 m.thread 事件的富回复视作线程内部的回复,以实现线程客户端侧的对话连续性。
实现方法为:从被回复事件中复制出 event_id(线程根),添加 m.in_reply_to 元数据,并在 m.relates_to 中加入 is_falling_back: true。
线程内的回复
在 非线程化客户端的回退机制 部分,为 m.relates_to 新增了 is_falling_back 字段。当未提供该字段时,默认为 false,这同样允许线程消息本身作为回复。
除了 is_falling_back 为 false(或未指定)以外,客户端应利用非线程化客户端的回退机制在线程内创建回复,并据此渲染事件。
服务器行为
m.thread 关系的验证
服务器应拒绝客户端针对带有 m.relates_to 属性的事件尝试发起线程的请求。如果客户端试图对带有 m.relates_to 属性的事件作为目标事件,则应返回 HTTP 400 错误及相应错误信息,按照 标准错误响应 结构处理。
此种情况目前没有单独的错误码:服务器应与 HTTP 400 一同返回 M_UNKNOWN。
服务器侧对 m.thread 关系的聚合
由于线程总是引用线程根,一个事件将拥有多个“子事件”,共同组成该线程。服务器应对这些事件进行 聚合。
线程聚合的数据包括用户在该线程中的参与情况、线程(服务器已知范围内)大致的事件数量,以及线程内最新(按服务器视角的拓扑顺序)的一条消息。
与任何其他子事件聚合一样,m.thread 聚合结果通过 unsigned 下的 m.relations 属性返回给线程根。例如:
{
"event_id": "$root_event",
// 未显示无关字段
"unsigned": {
"m.relations": {
"m.thread": {
"latest_event": {
// 线程中最新事件的序列化副本。
// 部分字段为简化未示出。
"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 为线程中由未被忽略的用户发送,服务器视拓扑顺序最新的一条事件。
注意,正如上例,latest_event 的子事件本身也需被聚合并包含在该事件下的 m.relations 下。服务器需注意避免形成循环,尽管由于 m.thread 不允许指向带有 m.relates_to 属性的事件,目前不可能产生循环。
count 仅指向目标事件的 rel_type 为 m.thread 的事件数量,未包含被忽略用户发送的事件。
current_user_participated 为 true 时,表明认证用户满足以下任一条件:
- 是线程根事件的
sender; - 是某个引用线程根且
rel_type为m.thread的事件的sender。
查询房间内线程
客户端如需获取某线程内的所有事件,可通过
GET /relations/{threadRootId}/m.thread;
如需获取某房间内所有线程,则需专用 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
| Name | Type | Description |
|---|---|---|
roomId |
string |
Required: The room ID where the thread roots are located. |
| 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 One of: |
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
|
403 |
The user cannot view or peek on the room. A meaningful
|
429 |
This request was rate-limited. |
200 response
| Name | Type | Description |
|---|---|---|
chunk |
[ClientEvent] |
Required: The thread roots, ordered by the 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 |
| 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 |
type |
string |
Required: The type of the event. |
unsigned |
UnsignedData |
Contains optional extra information about the event. |
| 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 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 |
prev_content |
EventContent |
The previous Changed in |
redacted_because |
ClientEvent |
The event that redacted this event, if any. |
transaction_id |
string |
The client-supplied transaction ID, for example, provided via
|
{
"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
| 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
| 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
| 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
}
引用关系
[Added in v1.5]
以通用方式引用其他事件时,可以使用 rel_type 为 m.reference 作为一种关系类型。引用本身没有特定含义,通常依赖于具体的应用场景。例如,密钥验证框架便使用引用关系,将不同的事件与某次特定的验证尝试关联起来。
希望支持话题或回复功能的客户端应使用除引用以外的其他关系类型。引用通常用于关联数据而非消息。
服务器行为
m.reference 的服务端聚合
m.reference 关系的聚合格式包含一个名为 chunk 的属性,该属性列出了所有对该事件(父事件)进行 m.reference 的事件。目前,chunk 中的事件只包含单一的 event_id 字段。
例如,给定一个带有如下 m.reference 关系的事件:
{
"content": {
"m.relates_to": {
"rel_type": "m.reference",
"event_id": "$another_event"
}
// 其他需要的内容字段
}
// 事件所需的其他字段
}
其聚合结果如下所示:
{
"m.reference": {
"chunk": [
{ "event_id": "$one" },
{ "event_id": "$two" }
]
}
}