|
@@ -0,0 +1,1264 @@
|
|
|
+=========================
|
|
|
+Synapse Client-Server API
|
|
|
+=========================
|
|
|
+
|
|
|
+The following specification outlines how a client can send and receive data from
|
|
|
+a home server.
|
|
|
+
|
|
|
+[[TODO(kegan): 4/7/14 Grilling
|
|
|
+- Mechanism for getting historical state changes (e.g. topic updates) - add
|
|
|
+ query param flag?
|
|
|
+- Generic mechanism for linking first class events (e.g. feedback) with other s
|
|
|
+ first class events (e.g. messages)?
|
|
|
+- Generic mechanism for updating 'stuff about the room' (e.g. favourite coffee)
|
|
|
+ AND specifying clobbering rules (clobber/add to list/etc)?
|
|
|
+- How to ensure a consistent view for clients paginating through room lists?
|
|
|
+ They aren't really ordered in any way, and if you're paginating
|
|
|
+ through them, how can you show them a consistent result set? Temporary 'room
|
|
|
+ list versions' akin to event version? How does that work?
|
|
|
+]]
|
|
|
+
|
|
|
+[[TODO(kegan):
|
|
|
+Outstanding problems / missing spec:
|
|
|
+- Push
|
|
|
+- Typing notifications
|
|
|
+]]
|
|
|
+
|
|
|
+Terminology
|
|
|
+-----------
|
|
|
+Stream Tokens:
|
|
|
+An opaque token used to make further streaming requests. When using any
|
|
|
+pagination streaming API, responses will contain a start and end stream token.
|
|
|
+When reconnecting to the stream, these tokens can be used to tell the server
|
|
|
+where the client got up to in the stream.
|
|
|
+
|
|
|
+Event ID:
|
|
|
+Every event that comes down the event stream or that is returned from the REST
|
|
|
+API has an associated event ID (event_id). This ID will be the same between the
|
|
|
+REST API and the event stream, so any duplicate events can be clobbered
|
|
|
+correctly without knowing anything else about the event.
|
|
|
+
|
|
|
+Message ID:
|
|
|
+The ID of a message sent by a client in a room. Clients send IMs to each other
|
|
|
+in rooms. Each IM sent by a client must have a unique message ID which is unique
|
|
|
+for that particular client.
|
|
|
+
|
|
|
+User ID:
|
|
|
+The @username:host style ID of the client. When registering for an account, the
|
|
|
+client specifies their username. The user_id is this username along with the
|
|
|
+home server's unique hostname. When federating between home servers, the user_id
|
|
|
+is used to uniquely identify users across multiple home servers.
|
|
|
+
|
|
|
+Room ID:
|
|
|
+The room_id@host style ID for the room. When rooms are created, the client either
|
|
|
+specifies or is allocated a room ID. This room ID must be used to send messages
|
|
|
+in that room. Like with clients, there may be multiple rooms with the same ID
|
|
|
+across multiple home servers. The room_id is used to uniquely identify a room
|
|
|
+when federating.
|
|
|
+
|
|
|
+Global message ID:
|
|
|
+The globally unique ID for a message. This ID is formed from the msg_id, the
|
|
|
+client's user_id and the room_id. This uniquely identifies any
|
|
|
+message. It is represented with '-' as the delimeter between IDs. The
|
|
|
+global_msg_id is of the form: room_id-user_id-msg_id
|
|
|
+
|
|
|
+
|
|
|
+REST API and the Event Stream
|
|
|
+-----------------------------
|
|
|
+Clients send data to the server via a RESTful API. They can receive data via
|
|
|
+this API or from an event stream. An event stream is a special path which
|
|
|
+streams all events the client may be interested in. This makes it easy to
|
|
|
+immediately receive updates from the REST API. All data is represented as JSON.
|
|
|
+
|
|
|
+Pagination streaming API
|
|
|
+========================
|
|
|
+Clients are often interested in very large datasets. The data itself could
|
|
|
+be 1000s of messages in a given room, 1000s of rooms in a public room list, or
|
|
|
+1000s of events (presence, typing, messages, etc) in the system. It is not
|
|
|
+practical to send vast quantities of data to the client every time they
|
|
|
+request a list of public rooms for example. There needs to be a way to show a
|
|
|
+subset of this data, and apply various filters to it. This is what the pagination
|
|
|
+streaming API is. This API defines standard request/response parameters which
|
|
|
+can be used when navigating this stream of data.
|
|
|
+
|
|
|
+Pagination Request Query Parameters
|
|
|
+-----------------------------------
|
|
|
+Clients may wish to paginate results from the event stream, or other sources of
|
|
|
+information where the amount of information may be a problem,
|
|
|
+e.g. in a room with 10,000s messages. The pagination query parameters provide a
|
|
|
+way to navigate a 'window' around a large set of data. These
|
|
|
+parameters are only valid for GET requests.
|
|
|
+
|
|
|
+ S e r v e r - s i d e d a t a
|
|
|
+ |-------------------------------------------------|
|
|
|
+START ^ ^ END
|
|
|
+ |_______________|
|
|
|
+ |
|
|
|
+ Client-extraction
|
|
|
+
|
|
|
+'START' and 'END' are magic token values which specify the start and end of the
|
|
|
+dataset respectively.
|
|
|
+
|
|
|
+Query parameters:
|
|
|
+ from : $streamtoken - The opaque token to start streaming from.
|
|
|
+ to : $streamtoken - The opaque token to end streaming at. Typically,
|
|
|
+ clients will not know the item of data to end at, so this will usually be
|
|
|
+ START or END.
|
|
|
+ limit : integer - An integer representing the maximum number of items to
|
|
|
+ return.
|
|
|
+
|
|
|
+For example, the event stream has events E1 -> E15. The client wants the last 5
|
|
|
+events and doesn't know any previous events:
|
|
|
+
|
|
|
+S E
|
|
|
+|-E1-E2-E3-E4-E5-E6-E7-E8-E9-E10-E11-E12-E13-E14-E15-|
|
|
|
+| | |
|
|
|
+| _____| |
|
|
|
+|__________________ | ___________________|
|
|
|
+ | | |
|
|
|
+ GET /events?to=START&limit=5&from=END
|
|
|
+ Returns:
|
|
|
+ E15,E14,E13,E12,E11
|
|
|
+
|
|
|
+
|
|
|
+Another example: a public room list has rooms R1 -> R17. The client is showing 5
|
|
|
+rooms at a time on screen, and is on page 2. They want to
|
|
|
+now show page 3 (rooms R11 -> 15):
|
|
|
+
|
|
|
+S E
|
|
|
+| 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | stream token
|
|
|
+|-R1-R2-R3-R4-R5-R6-R7-R8-R9-R10-R11-R12-R13-R14-R15-R16-R17| room
|
|
|
+ |____________| |________________|
|
|
|
+ | |
|
|
|
+ Currently |
|
|
|
+ viewing |
|
|
|
+ |
|
|
|
+ GET /rooms/list?from=9&to=END&limit=5
|
|
|
+ Returns: R11,R12,R13,R14,R15
|
|
|
+
|
|
|
+Note that tokens are treated in an *exclusive*, not inclusive, manner. The end
|
|
|
+token from the intial request was '9' which corresponded to R10. When the 2nd
|
|
|
+request was made, R10 did not appear again, even though from=9 was specified. If
|
|
|
+you know the token, you already have the data.
|
|
|
+
|
|
|
+Pagination Response
|
|
|
+-------------------
|
|
|
+Responses to pagination requests MUST follow the format:
|
|
|
+{
|
|
|
+ "chunk": [ ... , Responses , ... ],
|
|
|
+ "start" : $streamtoken,
|
|
|
+ "end" : $streamtoken
|
|
|
+}
|
|
|
+Where $streamtoken is an opaque token which can be used in another query to
|
|
|
+get the next set of results. The "start" and "end" keys can only be omitted if
|
|
|
+the complete dataset is provided in "chunk".
|
|
|
+
|
|
|
+If the client wants earlier results, they should use from=$start_streamtoken,
|
|
|
+to=START. Likewise, if the client wants later results, they should use
|
|
|
+from=$end_streamtoken, to=END.
|
|
|
+
|
|
|
+Unless specified, the default pagination parameters are from=START, to=END,
|
|
|
+without a limit set. This allows you to hit an API like
|
|
|
+/events without any query parameters to get everything.
|
|
|
+
|
|
|
+The Event Stream
|
|
|
+----------------
|
|
|
+The event stream returns events using the pagination streaming API. When the
|
|
|
+client disconnects for a while and wants to reconnect to the event stream, they
|
|
|
+should specify from=$end_streamtoken. This lets the server know where in the
|
|
|
+event stream the client is. These tokens are completely opaque, and the client
|
|
|
+cannot infer anything from them.
|
|
|
+
|
|
|
+ GET /events?from=$LAST_STREAM_TOKEN
|
|
|
+ REST Path: /events
|
|
|
+ Returns (success): A JSON array of Event Data.
|
|
|
+ Returns (failure): An Error Response
|
|
|
+
|
|
|
+LAST_STREAM_TOKEN is the last stream token obtained from the event stream. If the
|
|
|
+client is connecting for the first time and does not know any stream tokens,
|
|
|
+they can use "START" to request all events from the start. For more information
|
|
|
+on this, see "Pagination Request Query Parameters".
|
|
|
+
|
|
|
+The event stream supports shortpoll and longpoll with the "timeout" query
|
|
|
+parameter. This parameter specifies the number of milliseconds the server should
|
|
|
+hold onto the connection waiting for incoming events. If no events occur in this
|
|
|
+period, the connection will be closed and an empty chunk will be returned. To
|
|
|
+use shortpoll, specify "timeout=0".
|
|
|
+
|
|
|
+Event Data
|
|
|
+----------
|
|
|
+This is a JSON object which looks like:
|
|
|
+{
|
|
|
+ "event_id" : $EVENT_ID,
|
|
|
+ "type" : $EVENT_TYPE,
|
|
|
+ $URL_ARGS,
|
|
|
+ "content" : {
|
|
|
+ $EVENT_CONTENT
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+EVENT_ID
|
|
|
+ An ID identifying this event. This is so duplicate events can be suppressed on
|
|
|
+ the client.
|
|
|
+
|
|
|
+EVENT_TYPE
|
|
|
+ The namespaced event type (m.*)
|
|
|
+
|
|
|
+URL_ARGS
|
|
|
+ Path specific data from the REST API.
|
|
|
+
|
|
|
+EVENT_CONTENT
|
|
|
+ The event content, matching the REST content PUT previously.
|
|
|
+
|
|
|
+Events are differentiated via the event type "type" key. This is the type of
|
|
|
+event being received. This can be expanded upon by using different namespaces.
|
|
|
+Every event MUST have a 'type' key.
|
|
|
+
|
|
|
+Most events will have a corresponding REST URL. This URL will generally have
|
|
|
+data in it to represent the resource being modified,
|
|
|
+e.g. /rooms/$room_id. The event data will contain extra top-level keys to expose
|
|
|
+this information to clients listening on an event
|
|
|
+stream. The event content maps directly to the contents submitted via the REST
|
|
|
+API.
|
|
|
+
|
|
|
+For example:
|
|
|
+ Event Type: m.example.room.members
|
|
|
+ REST Path: /examples/room/$room_id/members/$user_id
|
|
|
+ REST Content: { "membership" : "invited" }
|
|
|
+
|
|
|
+is represented in the event stream as:
|
|
|
+
|
|
|
+{
|
|
|
+ "event_id" : "e_some_event_id",
|
|
|
+ "type" : "m.example.room.members",
|
|
|
+ "room_id" : $room_id,
|
|
|
+ "user_id" : $user_id,
|
|
|
+ "content" : {
|
|
|
+ "membership" : "invited"
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+As convention, the URL variable "$varname" will map directly onto the name
|
|
|
+of the JSON key "varname".
|
|
|
+
|
|
|
+Error Responses
|
|
|
+---------------
|
|
|
+If the client sends an invalid request, the server MAY respond with an error
|
|
|
+response. This is of the form:
|
|
|
+{
|
|
|
+ "error" : "string",
|
|
|
+ "errcode" : "string"
|
|
|
+}
|
|
|
+The 'error' string will be a human-readable error message, usually a sentence
|
|
|
+explaining what went wrong.
|
|
|
+
|
|
|
+The 'errcode' string will be a unique string which can be used to handle an
|
|
|
+error message e.g. "M_FORBIDDEN". These error codes should have their namespace
|
|
|
+first in ALL CAPS, followed by a single _. For example, if there was a custom
|
|
|
+namespace com.mydomain.here, and a "FORBIDDEN" code, the error code should look
|
|
|
+like "COM.MYDOMAIN.HERE_FORBIDDEN". There may be additional keys depending on
|
|
|
+the error, but the keys 'error' and 'errcode' will always be present.
|
|
|
+
|
|
|
+Some standard error codes are below:
|
|
|
+
|
|
|
+M_FORBIDDEN:
|
|
|
+Forbidden access, e.g. bad access token, failed login.
|
|
|
+
|
|
|
+M_BAD_JSON:
|
|
|
+Request contained valid JSON, but it was malformed in some way, e.g. missing
|
|
|
+required keys, invalid values for keys.
|
|
|
+
|
|
|
+M_NOT_JSON:
|
|
|
+Request did not contain valid JSON.
|
|
|
+
|
|
|
+M_NOT_FOUND:
|
|
|
+No resource was found for this request.
|
|
|
+
|
|
|
+Some requests have unique error codes:
|
|
|
+
|
|
|
+M_USER_IN_USE:
|
|
|
+Encountered when trying to register a user ID which has been taken.
|
|
|
+
|
|
|
+M_ROOM_IN_USE:
|
|
|
+Encountered when trying to create a room which has been taken.
|
|
|
+
|
|
|
+M_BAD_PAGINATION:
|
|
|
+Encountered when specifying bad pagination values to a Pagination Streaming API.
|
|
|
+
|
|
|
+
|
|
|
+========
|
|
|
+REST API
|
|
|
+========
|
|
|
+
|
|
|
+All content must be application/json. Some keys are required, while others are
|
|
|
+optional. Unless otherwise specified,
|
|
|
+all HTTP PUT/POST/DELETEs will return a 200 OK with an empty response body on
|
|
|
+success, and a 4xx/5xx with an optional Error Response on failure. When sending
|
|
|
+data, if there are no keys to send, an empty JSON object should be sent.
|
|
|
+
|
|
|
+All POST/PUT/GET/DELETE requests MUST have an 'access_token' query parameter to
|
|
|
+allow the server to authenticate the client. All
|
|
|
+POST requests MUST be submitted as application/json.
|
|
|
+
|
|
|
+All paths MUST be namespaced by the version of the API being used. This should
|
|
|
+be:
|
|
|
+
|
|
|
+/matrix/client/api/v1
|
|
|
+
|
|
|
+All REST paths in this section MUST be prefixed with this. E.g.
|
|
|
+ REST Path: /rooms/$room_id
|
|
|
+ Absolute Path: /matrix/client/api/v1/rooms/$room_id
|
|
|
+
|
|
|
+Registration
|
|
|
+============
|
|
|
+Clients must register with the server in order to use the service. After
|
|
|
+registering, the client will be given an
|
|
|
+access token which must be used in ALL requests as a query parameter
|
|
|
+'access_token'.
|
|
|
+
|
|
|
+Registering for an account
|
|
|
+--------------------------
|
|
|
+ POST /register
|
|
|
+ With: A JSON object containing the key "user_id" which contains the desired
|
|
|
+ user_id, or an empty JSON object to have the server allocate a user_id
|
|
|
+ automatically.
|
|
|
+ Returns (success): 200 OK with a JSON object:
|
|
|
+ {
|
|
|
+ "user_id" : "string [user_id]",
|
|
|
+ "access_token" : "string"
|
|
|
+ }
|
|
|
+ Returns (failure): An Error Response. M_USER_IN_USE if the user ID is taken.
|
|
|
+
|
|
|
+
|
|
|
+Unregistering an account
|
|
|
+------------------------
|
|
|
+ POST /unregister
|
|
|
+ With query parameters: access_token=$ACCESS_TOKEN
|
|
|
+ Returns (success): 200 OK
|
|
|
+ Returns (failure): An Error Response.
|
|
|
+
|
|
|
+
|
|
|
+Logging in to an existing account
|
|
|
+=================================
|
|
|
+If the client has already registered, they need to be able to login to their
|
|
|
+account. The home server may provide many different ways of logging in, such
|
|
|
+as user/password auth, login via a social network (OAuth), login by confirming
|
|
|
+a token sent to their email address, etc. This section does NOT define how home
|
|
|
+servers should authorise their users who want to login to their existing
|
|
|
+accounts. This section defines the standard interface which implementations
|
|
|
+should follow so that ANY client can login to ANY home server.
|
|
|
+
|
|
|
+The login process breaks down into the following:
|
|
|
+ 1: Get login process info.
|
|
|
+ 2: Submit the login stage credentials.
|
|
|
+ 3: Get access token or be told the next stage in the login process and repeat
|
|
|
+ step 2.
|
|
|
+
|
|
|
+Getting login process info:
|
|
|
+ GET /login
|
|
|
+ Returns (success): 200 OK with LoginInfo.
|
|
|
+ Returns (failure): An Error Response.
|
|
|
+
|
|
|
+Submitting the login stage credentials:
|
|
|
+ POST /login
|
|
|
+ With: LoginSubmission
|
|
|
+ Returns (success): 200 OK with LoginResult
|
|
|
+ Returns (failure): An Error Response
|
|
|
+
|
|
|
+Where LoginInfo is a JSON object which MUST have a "type" key which denotes the
|
|
|
+login type. If there are multiple login stages, this object MUST also contain a
|
|
|
+"stages" key, which has a JSON array of login types denoting all the steps in
|
|
|
+order to login, including the first stage which is in "type". This allows the
|
|
|
+client to make an informed decision as to whether or not they can natively
|
|
|
+handle the entire login process, or whether they should fallback (see below).
|
|
|
+
|
|
|
+Where LoginSubmission is a JSON object which MUST have a "type" key.
|
|
|
+
|
|
|
+Where LoginResult is a JSON object which MUST have either a "next" key OR an
|
|
|
+"access_token" key, depending if the login process is over or not. This object
|
|
|
+MUST have a "session" key if multiple POSTs need to be sent to /login.
|
|
|
+
|
|
|
+Fallback
|
|
|
+--------
|
|
|
+If the client does NOT know how to handle the given type, they should:
|
|
|
+ GET /login/fallback
|
|
|
+This MUST return an HTML page which can perform the entire login process.
|
|
|
+
|
|
|
+Password-based
|
|
|
+--------------
|
|
|
+Type: "m.login.password"
|
|
|
+LoginSubmission:
|
|
|
+{
|
|
|
+ "type": "m.login.password",
|
|
|
+ "user": <user_id>,
|
|
|
+ "password": <password>
|
|
|
+}
|
|
|
+
|
|
|
+Example:
|
|
|
+Assume you are @bob:matrix.org and you wish to login on another mobile device.
|
|
|
+First, you GET /login which returns:
|
|
|
+{
|
|
|
+ "type": "m.login.password"
|
|
|
+}
|
|
|
+Your client knows how to handle this, so your client prompts the user to enter
|
|
|
+their username and password. This is then submitted:
|
|
|
+{
|
|
|
+ "type": "m.login.password",
|
|
|
+ "user": "@bob:matrix.org",
|
|
|
+ "password": "monkey"
|
|
|
+}
|
|
|
+The server checks this, finds it is valid, and returns:
|
|
|
+{
|
|
|
+ "access_token": "abcdef0123456789"
|
|
|
+}
|
|
|
+
|
|
|
+OAuth2-based
|
|
|
+------------
|
|
|
+Type: "m.login.oauth2"
|
|
|
+This is a multi-stage login.
|
|
|
+
|
|
|
+LoginSubmission:
|
|
|
+{
|
|
|
+ "type": "m.login.oauth2",
|
|
|
+ "user": <user_id>
|
|
|
+}
|
|
|
+Returns:
|
|
|
+{
|
|
|
+ "uri": <Authorization Request uri OR service selection uri>
|
|
|
+}
|
|
|
+
|
|
|
+The home server acts as a 'confidential' Client for the purposes of OAuth2.
|
|
|
+
|
|
|
+If the uri is a "sevice selection uri", it is a simple page which prompts the
|
|
|
+user to choose which service to authorize with. On selection of a service, they
|
|
|
+link through to Authorization Request URIs. If there is only 1 service which the
|
|
|
+home server accepts when logging in, this indirection can be skipped and the
|
|
|
+"uri" key can be the Authorization Request URI.
|
|
|
+
|
|
|
+The client visits the Authorization Request URI, which then shows the OAuth2
|
|
|
+Allow/Deny prompt. Hitting 'Allow' returns the redirect URI with the auth code.
|
|
|
+Home servers can choose any path for the redirect URI. The client should visit
|
|
|
+the redirect URI, which will then finish the OAuth2 login process, granting the
|
|
|
+home server an access token for the chosen service. When the home server gets
|
|
|
+this access token, it knows that the cilent has authed with the 3rd party, and
|
|
|
+so can return a LoginResult.
|
|
|
+
|
|
|
+The OAuth redirect URI (with auth code) MUST return a LoginResult.
|
|
|
+
|
|
|
+Example:
|
|
|
+Assume you are @bob:matrix.org and you wish to login on another mobile device.
|
|
|
+First, you GET /login which returns:
|
|
|
+{
|
|
|
+ "type": "m.login.oauth2"
|
|
|
+}
|
|
|
+Your client knows how to handle this, so your client prompts the user to enter
|
|
|
+their username. This is then submitted:
|
|
|
+{
|
|
|
+ "type": "m.login.oauth2",
|
|
|
+ "user": "@bob:matrix.org"
|
|
|
+}
|
|
|
+The server only accepts auth from Google, so returns the Authorization Request
|
|
|
+URI for Google:
|
|
|
+{
|
|
|
+ "uri": "https://accounts.google.com/o/oauth2/auth?response_type=code&
|
|
|
+ client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&scope=photos"
|
|
|
+}
|
|
|
+The client then visits this URI and authorizes the home server. The client then
|
|
|
+visits the REDIRECT_URI with the auth code= query parameter which returns:
|
|
|
+{
|
|
|
+ "access_token": "0123456789abcdef"
|
|
|
+}
|
|
|
+
|
|
|
+Email-based (code)
|
|
|
+------------------
|
|
|
+Type: "m.login.email.code"
|
|
|
+This is a multi-stage login.
|
|
|
+
|
|
|
+First LoginSubmission:
|
|
|
+{
|
|
|
+ "type": "m.login.email.code",
|
|
|
+ "user": <user_id>
|
|
|
+ "email": <email address>
|
|
|
+}
|
|
|
+Returns:
|
|
|
+{
|
|
|
+ "session": <session id>
|
|
|
+}
|
|
|
+
|
|
|
+The email contains a code which must be sent in the next LoginSubmission:
|
|
|
+{
|
|
|
+ "type": "m.login.email.code",
|
|
|
+ "session": <session id>,
|
|
|
+ "code": <code in email sent>
|
|
|
+}
|
|
|
+Returns:
|
|
|
+{
|
|
|
+ "access_token": <access token>
|
|
|
+}
|
|
|
+
|
|
|
+Example:
|
|
|
+Assume you are @bob:matrix.org and you wish to login on another mobile device.
|
|
|
+First, you GET /login which returns:
|
|
|
+{
|
|
|
+ "type": "m.login.email.code"
|
|
|
+}
|
|
|
+Your client knows how to handle this, so your client prompts the user to enter
|
|
|
+their email address. This is then submitted:
|
|
|
+{
|
|
|
+ "type": "m.login.email.code",
|
|
|
+ "user": "@bob:matrix.org",
|
|
|
+ "email": "bob@mydomain.com"
|
|
|
+}
|
|
|
+The server confirms that bob@mydomain.com is linked to @bob:matrix.org, then
|
|
|
+sends an email to this address and returns:
|
|
|
+{
|
|
|
+ "session": "ewuigf7462"
|
|
|
+}
|
|
|
+The client's screen changes to a code submission page. The email arrives and it
|
|
|
+says something to the effect of "please enter 2348623 into the app". This is
|
|
|
+the submitted along with the session:
|
|
|
+{
|
|
|
+ "type": "m.login.email.code",
|
|
|
+ "session": "ewuigf7462",
|
|
|
+ "code": "2348623"
|
|
|
+}
|
|
|
+The server accepts this and returns:
|
|
|
+{
|
|
|
+ "access_token": "abcdef0123456789"
|
|
|
+}
|
|
|
+
|
|
|
+Email-based (url)
|
|
|
+-----------------
|
|
|
+Type: "m.login.email.url"
|
|
|
+This is a multi-stage login.
|
|
|
+
|
|
|
+First LoginSubmission:
|
|
|
+{
|
|
|
+ "type": "m.login.email.url",
|
|
|
+ "user": <user_id>
|
|
|
+ "email": <email address>
|
|
|
+}
|
|
|
+Returns:
|
|
|
+{
|
|
|
+ "session": <session id>
|
|
|
+}
|
|
|
+
|
|
|
+The email contains a URL which must be clicked. After it has been clicked, the
|
|
|
+client should perform a request:
|
|
|
+{
|
|
|
+ "type": "m.login.email.code",
|
|
|
+ "session": <session id>
|
|
|
+}
|
|
|
+Returns:
|
|
|
+{
|
|
|
+ "access_token": <access token>
|
|
|
+}
|
|
|
+
|
|
|
+Example:
|
|
|
+Assume you are @bob:matrix.org and you wish to login on another mobile device.
|
|
|
+First, you GET /login which returns:
|
|
|
+{
|
|
|
+ "type": "m.login.email.url"
|
|
|
+}
|
|
|
+Your client knows how to handle this, so your client prompts the user to enter
|
|
|
+their email address. This is then submitted:
|
|
|
+{
|
|
|
+ "type": "m.login.email.url",
|
|
|
+ "user": "@bob:matrix.org",
|
|
|
+ "email": "bob@mydomain.com"
|
|
|
+}
|
|
|
+The server confirms that bob@mydomain.com is linked to @bob:matrix.org, then
|
|
|
+sends an email to this address and returns:
|
|
|
+{
|
|
|
+ "session": "ewuigf7462"
|
|
|
+}
|
|
|
+The client then starts polling the server with the following:
|
|
|
+{
|
|
|
+ "type": "m.login.email.url",
|
|
|
+ "session": "ewuigf7462"
|
|
|
+}
|
|
|
+(Alternatively, the server could send the device a push notification when the
|
|
|
+email has been validated). The email arrives and it contains a URL to click on.
|
|
|
+The user clicks on the which completes the login process with the server. The
|
|
|
+next time the client polls, it returns:
|
|
|
+{
|
|
|
+ "access_token": "abcdef0123456789"
|
|
|
+}
|
|
|
+
|
|
|
+N-Factor auth
|
|
|
+-------------
|
|
|
+Multiple login stages can be combined with the "next" key in the LoginResult.
|
|
|
+
|
|
|
+Example:
|
|
|
+A server demands an email.code then password auth before logging in. First, the
|
|
|
+client performs a GET /login which returns:
|
|
|
+{
|
|
|
+ "type": "m.login.email.code",
|
|
|
+ "stages": ["m.login.email.code", "m.login.password"]
|
|
|
+}
|
|
|
+The client performs the email login (See "Email-based (code)"), but instead of
|
|
|
+returning an access_token, it returns:
|
|
|
+{
|
|
|
+ "next": "m.login.password"
|
|
|
+}
|
|
|
+The client then presents a user/password screen and the login continues until
|
|
|
+this is complete (See "Password-based"), which then returns the "access_token".
|
|
|
+
|
|
|
+Rooms
|
|
|
+=====
|
|
|
+A room is a conceptual place where users can send and receive messages. Rooms
|
|
|
+can be created, joined and left. Messages are sent
|
|
|
+to a room, and all participants in that room will receive the message. Rooms are
|
|
|
+uniquely identified via the room_id.
|
|
|
+
|
|
|
+Creating a room (with a room ID)
|
|
|
+--------------------------------
|
|
|
+ Event Type: m.room.create [TODO(kegan): Do we generate events for this?]
|
|
|
+ REST Path: /rooms/$room_id
|
|
|
+ Valid methods: PUT
|
|
|
+ Required keys: None.
|
|
|
+ Optional keys:
|
|
|
+ visibility : [public|private] - Set whether this room shows up in the public
|
|
|
+ room list.
|
|
|
+ Returns:
|
|
|
+ On Failure: MAY return a suggested alternative room ID if this room ID is
|
|
|
+ taken.
|
|
|
+ {
|
|
|
+ suggested_room_id : $new_room_id
|
|
|
+ error : "Room already in use."
|
|
|
+ errcode : "M_ROOM_IN_USE"
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+Creating a room (without a room ID)
|
|
|
+-----------------------------------
|
|
|
+ Event Type: m.room.create [TODO(kegan): Do we generate events for this?]
|
|
|
+ REST Path: /rooms
|
|
|
+ Valid methods: POST
|
|
|
+ Required keys: None.
|
|
|
+ Optional keys:
|
|
|
+ visibility : [public|private] - Set whether this room shows up in the public
|
|
|
+ room list.
|
|
|
+ Returns:
|
|
|
+ On Success: The allocated room ID. Additional information about the room
|
|
|
+ such as the visibility MAY be included as extra keys in this response.
|
|
|
+ {
|
|
|
+ room_id : $room_id
|
|
|
+ }
|
|
|
+
|
|
|
+Setting the topic for a room
|
|
|
+----------------------------
|
|
|
+ Event Type: m.room.topic
|
|
|
+ REST Path: /rooms/$room_id/topic
|
|
|
+ Valid methods: GET/PUT
|
|
|
+ Required keys:
|
|
|
+ topic : $topicname - Set the topic to $topicname in room $room_id.
|
|
|
+
|
|
|
+
|
|
|
+See a list of public rooms
|
|
|
+--------------------------
|
|
|
+ REST Path: /public/rooms?pagination_query_parameters
|
|
|
+ Valid methods: GET
|
|
|
+ This API can use pagination query parameters.
|
|
|
+ Returns:
|
|
|
+ {
|
|
|
+ "chunk" : JSON array of RoomInfo JSON objects - Required.
|
|
|
+ "start" : "string (start token)" - See Pagination Response.
|
|
|
+ "end" : "string (end token)" - See Pagination Response.
|
|
|
+ "total" : integer - Optional. The total number of rooms.
|
|
|
+ }
|
|
|
+
|
|
|
+RoomInfo: Information about a single room.
|
|
|
+ Servers MUST send the key: room_id
|
|
|
+ Servers MAY send the keys: topic, num_members
|
|
|
+ {
|
|
|
+ "room_id" : "string",
|
|
|
+ "topic" : "string",
|
|
|
+ "num_members" : integer
|
|
|
+ }
|
|
|
+
|
|
|
+Room Members
|
|
|
+============
|
|
|
+
|
|
|
+Invite/Joining/Leaving a room
|
|
|
+-----------------------------
|
|
|
+ Event Type: m.room.member
|
|
|
+ REST Path: /rooms/$room_id/members/$user_id/state
|
|
|
+ Valid methods: PUT/GET/DELETE
|
|
|
+ Required keys:
|
|
|
+ membership : [join|invite] - The membership state of $user_id in room
|
|
|
+ $room_id.
|
|
|
+
|
|
|
+Where:
|
|
|
+ join - Indicate you ($user_id) are joining the room $room_id.
|
|
|
+ invite - Indicate that $user_id has been invited to room $room_id.
|
|
|
+
|
|
|
+User $user_id can leave room $room_id by DELETEing this path.
|
|
|
+
|
|
|
+Checking the user list of a room
|
|
|
+--------------------------------
|
|
|
+ REST Path: /rooms/$room_id/members/list
|
|
|
+ This API can use pagination query parameters.
|
|
|
+ Valid methods: GET
|
|
|
+ Returns:
|
|
|
+ A pagination response with chunk data as m.room.member events.
|
|
|
+
|
|
|
+Messages
|
|
|
+========
|
|
|
+Users send messages to other users in rooms. These messages may be text, images,
|
|
|
+video, etc. Clients may also want to acknowledge messages by sending feedback,
|
|
|
+in the form of delivery/read receipts.
|
|
|
+
|
|
|
+Server-attached keys
|
|
|
+--------------------
|
|
|
+The server MAY attach additional keys to messages and feedback. If a client
|
|
|
+submits keys with the same name, they will be clobbered by
|
|
|
+the server.
|
|
|
+
|
|
|
+Required keys:
|
|
|
+from : "string [user_id]"
|
|
|
+ The user_id of the user who sent the message/feedback.
|
|
|
+
|
|
|
+Optional keys:
|
|
|
+hsob_ts : integer
|
|
|
+ A timestamp (ms resolution) representing when the message/feedback got to the
|
|
|
+ sender's home server ("home server outbound timestamp").
|
|
|
+
|
|
|
+hsib_ts : integer
|
|
|
+ A timestamp (ms resolution) representing when the
|
|
|
+ message/feedback got to the receiver's home server ("home server inbound
|
|
|
+ timestamp"). This may be the same as hsob_ts if the sender/receiver are on the
|
|
|
+ same home server.
|
|
|
+
|
|
|
+Sending messages
|
|
|
+----------------
|
|
|
+ Event Type: m.room.message
|
|
|
+ REST Path: /rooms/$room_id/messages/$from/$msg_id
|
|
|
+ Valid methods: GET/PUT
|
|
|
+ URL parameters:
|
|
|
+ $from : user_id - The sender's user_id. This value will be clobbered by the
|
|
|
+ server before sending.
|
|
|
+ Required keys:
|
|
|
+ msgtype: [m.text|m.emote|m.image|m.audio|m.video|m.location|m.file] -
|
|
|
+ The type of message. Not to be confused with the Event 'type'.
|
|
|
+ Optional keys:
|
|
|
+ sender_ts : integer - A timestamp (ms resolution) representing the
|
|
|
+ wall-clock time when the message was sent from the client.
|
|
|
+ Reserved keys:
|
|
|
+ body : "string" - The human readable string for compatibility with clients
|
|
|
+ which cannot process a given msgtype. This key is optional, but
|
|
|
+ if it is included, it MUST be human readable text
|
|
|
+ describing the message. See individual msgtypes for more
|
|
|
+ info on what this means in practice.
|
|
|
+
|
|
|
+Each msgtype may have required fields of their own.
|
|
|
+
|
|
|
+msgtype: m.text
|
|
|
+----------------
|
|
|
+Required keys:
|
|
|
+ body : "string" - The body of the message.
|
|
|
+Optional keys:
|
|
|
+ None.
|
|
|
+
|
|
|
+msgtype: m.emote
|
|
|
+-----------------
|
|
|
+Required keys:
|
|
|
+ body : "string" - *tries to come up with a witty explanation*.
|
|
|
+Optional keys:
|
|
|
+ None.
|
|
|
+
|
|
|
+msgtype: m.image
|
|
|
+-----------------
|
|
|
+Required keys:
|
|
|
+ url : "string" - The URL to the image.
|
|
|
+Optional keys:
|
|
|
+ body : "string" - info : JSON object (ImageInfo) - The image info for image
|
|
|
+ referred to in 'url'.
|
|
|
+ thumbnail_url : "string" - The URL to the thumbnail.
|
|
|
+ thumbnail_info : JSON object (ImageInfo) - The image info for the image
|
|
|
+ referred to in 'thumbnail_url'.
|
|
|
+
|
|
|
+ImageInfo: Information about an image.
|
|
|
+{
|
|
|
+ "size" : integer (size of image in bytes),
|
|
|
+ "w" : integer (width of image in pixels),
|
|
|
+ "h" : integer (height of image in pixels),
|
|
|
+ "mimetype" : "string (e.g. image/jpeg)"
|
|
|
+}
|
|
|
+
|
|
|
+Interpretation of 'body' key: The alt text of the image, or some kind of content
|
|
|
+description for accessibility e.g. "image attachment".
|
|
|
+
|
|
|
+msgtype: m.audio
|
|
|
+-----------------
|
|
|
+Required keys:
|
|
|
+ url : "string" - The URL to the audio.
|
|
|
+Optional keys:
|
|
|
+ info : JSON object (AudioInfo) - The audio info for the audio referred to in
|
|
|
+ 'url'.
|
|
|
+
|
|
|
+AudioInfo: Information about a piece of audio.
|
|
|
+{
|
|
|
+ "mimetype" : "string (e.g. audio/aac)",
|
|
|
+ "size" : integer (size of audio in bytes),
|
|
|
+ "duration" : integer (duration of audio in milliseconds)
|
|
|
+}
|
|
|
+
|
|
|
+Interpretation of 'body' key: A description of the audio e.g. "Bee Gees -
|
|
|
+Stayin' Alive", or some kind of content description for accessibility e.g.
|
|
|
+"audio attachment".
|
|
|
+
|
|
|
+msgtype: m.video
|
|
|
+-----------------
|
|
|
+Required keys:
|
|
|
+ url : "string" - The URL to the video.
|
|
|
+Optional keys:
|
|
|
+ info : JSON object (VideoInfo) - The video info for the video referred to in
|
|
|
+ 'url'.
|
|
|
+
|
|
|
+VideoInfo: Information about a video.
|
|
|
+{
|
|
|
+ "mimetype" : "string (e.g. video/mp4)",
|
|
|
+ "size" : integer (size of video in bytes),
|
|
|
+ "duration" : integer (duration of video in milliseconds),
|
|
|
+ "w" : integer (width of video in pixels),
|
|
|
+ "h" : integer (height of video in pixels),
|
|
|
+ "thumbnail_url" : "string (URL to image)",
|
|
|
+ "thumbanil_info" : JSON object (ImageInfo)
|
|
|
+}
|
|
|
+
|
|
|
+Interpretation of 'body' key: A description of the video e.g. "Gangnam style",
|
|
|
+or some kind of content description for accessibility e.g. "video attachment".
|
|
|
+
|
|
|
+msgtype: m.location
|
|
|
+--------------------
|
|
|
+Required keys:
|
|
|
+ geo_uri : "string" - The geo URI representing the location.
|
|
|
+Optional keys:
|
|
|
+ thumbnail_url : "string" - The URL to a thumnail of the location being
|
|
|
+ represented.
|
|
|
+ thumbnail_info : JSON object (ImageInfo) - The image info for the image
|
|
|
+ referred to in 'thumbnail_url'.
|
|
|
+
|
|
|
+Interpretation of 'body' key: A description of the location e.g. "Big Ben,
|
|
|
+London, UK", or some kind of content description for accessibility e.g.
|
|
|
+"location attachment".
|
|
|
+
|
|
|
+
|
|
|
+Sending feedback
|
|
|
+----------------
|
|
|
+When you receive a message, you may want to send delivery receipt to let the
|
|
|
+sender know that the message arrived. You may also want to send a read receipt
|
|
|
+when the user has read the message. These receipts are collectively known as
|
|
|
+'feedback'.
|
|
|
+
|
|
|
+ Event Type: m.room.message.feedback
|
|
|
+ REST Path: /rooms/$room_id/messages/$msgfrom/$msg_id/feedback/$from/$feedback
|
|
|
+ Valid methods: GET/PUT
|
|
|
+ URL parameters:
|
|
|
+ $msgfrom - The sender of the message's user_id.
|
|
|
+ $from : user_id - The sender of the feedback's user_id. This value will be
|
|
|
+ clobbered by the server before sending.
|
|
|
+ $feedback : [d|r] - Specify if this is a [d]elivery or [r]ead receipt.
|
|
|
+ Required keys:
|
|
|
+ None.
|
|
|
+ Optional keys:
|
|
|
+ sender_ts : integer - A timestamp (ms resolution) representing the
|
|
|
+ wall-clock time when the receipt was sent from the client.
|
|
|
+
|
|
|
+Receiving messages (bulk/pagination)
|
|
|
+------------------------------------
|
|
|
+ Event Type: m.room.message
|
|
|
+ REST Path: /rooms/$room_id/messages/list
|
|
|
+ Valid methods: GET
|
|
|
+ Query Parameters:
|
|
|
+ feedback : [true|false] - Specify if feedback should be bundled with each
|
|
|
+ message.
|
|
|
+ This API can use pagination query parameters.
|
|
|
+ Returns:
|
|
|
+ A JSON array of Event Data in "chunk" (see Pagination Response). If the
|
|
|
+ "feedback" parameter was set, the Event Data will also contain a "feedback"
|
|
|
+ key which contains a JSON array of feedback, with each element as Event Data
|
|
|
+ with compressed feedback for this message.
|
|
|
+
|
|
|
+Event Data with compressed feedback is a special type of feedback with
|
|
|
+contextual keys removed. It is designed to limit the amount of redundant data
|
|
|
+being sent for feedback. This removes the type, event_id, room ID,
|
|
|
+message sender ID and message ID keys.
|
|
|
+
|
|
|
+ ORIGINAL (via event streaming)
|
|
|
+{
|
|
|
+ "event_id":"e1247632487",
|
|
|
+ "type":"m.room.message.feedback",
|
|
|
+ "from":"string [user_id]",
|
|
|
+ "feedback":"string [d|r]",
|
|
|
+ "room_id":"$room_id",
|
|
|
+ "msg_id":"$msg_id",
|
|
|
+ "msgfrom":"$msgfromid",
|
|
|
+ "content":{
|
|
|
+ "sender_ts":139880943
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+ COMPRESSED (via /messages/list)
|
|
|
+{
|
|
|
+ "from":"string [user_id]",
|
|
|
+ "feedback":"string [d|r]",
|
|
|
+ "content":{
|
|
|
+ "sender_ts":139880943
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+When you join a room $room_id, you may want the last 10 messages with feedback.
|
|
|
+This is represented as:
|
|
|
+ GET
|
|
|
+ /rooms/$room_id/messages/list?from=END&to=START&limit=10&feedback=true
|
|
|
+
|
|
|
+You may want to get 10 messages even earlier than that without feedback. If the
|
|
|
+start stream token from the previous request was stok_019173, this request would
|
|
|
+be:
|
|
|
+ GET
|
|
|
+ /rooms/$room_id/messages/list?from=stok_019173&to=START&limit=10&
|
|
|
+ feedback=false
|
|
|
+
|
|
|
+NOTE: Care must be taken when using this API in conjunction with event
|
|
|
+ streaming. It is possible that this will return a message which will
|
|
|
+ then come down the event stream, resulting in a duplicate message. Clients
|
|
|
+ should clobber based on the global message ID, or event ID.
|
|
|
+
|
|
|
+
|
|
|
+Get current state for all rooms (aka IM Initial Sync API)
|
|
|
+-------------------------------
|
|
|
+ REST Path: /im/sync
|
|
|
+ Valid methods: GET
|
|
|
+ This API can use pagination query parameters. Pagination is applied on a per
|
|
|
+ *room* basis. E.g. limit=1 means "get 1 message for each room" and not "get 1
|
|
|
+ room's messages". If there is no limit, all messages for all rooms will be
|
|
|
+ returned.
|
|
|
+ If you want 1 room's messages, see "Receiving messages (bulk/pagination)".
|
|
|
+ Additional query parameters:
|
|
|
+ feedback: [true] - Bundles feedback with messages.
|
|
|
+ Returns:
|
|
|
+ An array of RoomStateInfo.
|
|
|
+
|
|
|
+RoomStateInfo: A snapshot of information about a single room.
|
|
|
+ {
|
|
|
+ "room_id" : "string",
|
|
|
+ "membership" : "string [join|invite]",
|
|
|
+ "messages" : {
|
|
|
+ "start": "string",
|
|
|
+ "end": "string",
|
|
|
+ "chunk":
|
|
|
+ m.room.message pagination stream events (with feedback if specified),
|
|
|
+ this is the same as "Receiving messages (bulk/pagination)".
|
|
|
+ }
|
|
|
+ }
|
|
|
+The "membership" key is the calling user's membership state in the given
|
|
|
+"room_id". The "messages" key may be omitted if the "membership" value is
|
|
|
+"invite". Additional keys may be added to the top-level object, such as:
|
|
|
+ "topic" : "string" - The topic for the room in question.
|
|
|
+ "room_image_url" : "string" - The URL of the room image if specified.
|
|
|
+ "num_members" : integer - The number of members in the room.
|
|
|
+
|
|
|
+
|
|
|
+Profiles
|
|
|
+========
|
|
|
+
|
|
|
+Getting/Setting your own displayname
|
|
|
+------------------------------------
|
|
|
+ REST Path: /profile/$user_id/displayname
|
|
|
+ Valid methods: GET/PUT
|
|
|
+ Required keys:
|
|
|
+ displayname : The displayname text
|
|
|
+
|
|
|
+Getting/Setting your own avatar image URL
|
|
|
+-----------------------------------------
|
|
|
+The homeserver does not currently store the avatar image itself, but offers
|
|
|
+storage for the user to specify a web URL that points at the required image,
|
|
|
+leaving it up to clients to fetch it themselves.
|
|
|
+ REST Path: /profile/$user_id/avatar_url
|
|
|
+ Valid methods: GET/PUT
|
|
|
+ Required keys:
|
|
|
+ avatar_url : The URL path to the required image
|
|
|
+
|
|
|
+Getting other user's profile information
|
|
|
+----------------------------------------
|
|
|
+Either of the above REST methods may be used to fetch other user's profile
|
|
|
+information by the client, either on other local users on the same homeserver or
|
|
|
+for users from other servers entirely.
|
|
|
+
|
|
|
+
|
|
|
+Presence
|
|
|
+========
|
|
|
+
|
|
|
+In the following messages, the presence state is an integer enumeration of the
|
|
|
+following states:
|
|
|
+ 0 : OFFLINE
|
|
|
+ 1 : BUSY
|
|
|
+ 2 : ONLINE
|
|
|
+ 3 : FREE_TO_CHAT
|
|
|
+
|
|
|
+Aside from OFFLINE, the protocol doesn't assign any special meaning to these
|
|
|
+states; they are provided as an approximate signal for users to give to other
|
|
|
+users and for clients to present them in some way that may be useful. Clients
|
|
|
+could have different behaviours for different states of the user's presence, for
|
|
|
+example to decide how much prominence or sound to use for incoming event
|
|
|
+notifications.
|
|
|
+
|
|
|
+Getting/Setting your own presence state
|
|
|
+---------------------------------------
|
|
|
+ REST Path: /presence/$user_id/status
|
|
|
+ Valid methods: GET/PUT
|
|
|
+ Required keys:
|
|
|
+ state : [0|1|2|3] - The user's new presence state
|
|
|
+ Optional keys:
|
|
|
+ status_msg : text string provided by the user to explain their status
|
|
|
+
|
|
|
+Fetching your presence list
|
|
|
+---------------------------
|
|
|
+ REST Path: /presence_list/$user_id
|
|
|
+ Valid methods: GET/(post)
|
|
|
+ Returns:
|
|
|
+ An array of presence list entries. Each entry is an object with the
|
|
|
+ following keys:
|
|
|
+ {
|
|
|
+ "user_id" : string giving the observed user's ID
|
|
|
+ "state" : int giving their status
|
|
|
+ "status_msg" : optional text string
|
|
|
+ "displayname" : optional text string from the user's profile
|
|
|
+ "avatar_url" : optional text string from the user's profile
|
|
|
+ }
|
|
|
+
|
|
|
+Maintaining your presence list
|
|
|
+------------------------------
|
|
|
+ REST Path: /presence_list/$user_id
|
|
|
+ Valid methods: POST/(get)
|
|
|
+ With: A JSON object optionally containing either of the following keys:
|
|
|
+ "invite" : a list of strings giving user IDs to invite for presence
|
|
|
+ subscription
|
|
|
+ "drop" : a list of strings giving user IDs to remove from your presence
|
|
|
+ list
|
|
|
+
|
|
|
+Receiving presence update events
|
|
|
+--------------------------------
|
|
|
+ Event Type: m.presence
|
|
|
+ Keys of the event's content are the same as those returned by the presence
|
|
|
+ list.
|
|
|
+
|
|
|
+Examples
|
|
|
+========
|
|
|
+
|
|
|
+The following example is the story of "bob", who signs up at "sy.org" and joins
|
|
|
+the public room "room_beta@sy.org". They get the 2 most recent
|
|
|
+messages (with feedback) in that room and then send a message in that room.
|
|
|
+
|
|
|
+For context, here is the complete chat log for room_beta@sy.org:
|
|
|
+
|
|
|
+Room: "Hello world" (room_beta@sy.org)
|
|
|
+Members: (2) alice@randomhost.org, friend_of_alice@randomhost.org
|
|
|
+Messages:
|
|
|
+ alice@randomhost.org : hi friend!
|
|
|
+ [friend_of_alice@randomhost.org DELIVERED]
|
|
|
+ alice@randomhost.org : you're my only friend
|
|
|
+ [friend_of_alice@randomhost.org DELIVERED]
|
|
|
+ alice@randomhost.org : afk
|
|
|
+ [friend_of_alice@randomhost.org DELIVERED]
|
|
|
+ [ bob@sy.org joins ]
|
|
|
+ bob@sy.org : Hi everyone
|
|
|
+ [ alice@randomhost.org changes the topic to "FRIENDS ONLY" ]
|
|
|
+ alice@randomhost.org : Hello!!!!
|
|
|
+ alice@randomhost.org : Let's go to another room
|
|
|
+ alice@randomhost.org : You're not my friend
|
|
|
+ [ alice@randomhost.org invites bob@sy.org to the room
|
|
|
+ commoners@randomhost.org]
|
|
|
+
|
|
|
+
|
|
|
+REGISTER FOR AN ACCOUNT
|
|
|
+POST: /register
|
|
|
+Content: {}
|
|
|
+Returns: { "user_id" : "bob@sy.org" , "access_token" : "abcdef0123456789" }
|
|
|
+
|
|
|
+GET PUBLIC ROOM LIST
|
|
|
+GET: /rooms/list?access_token=abcdef0123456789
|
|
|
+Returns:
|
|
|
+{
|
|
|
+ "total":3,
|
|
|
+ "chunk":
|
|
|
+ [
|
|
|
+ { "room_id":"room_alpha@sy.org", "topic":"I am a fish" },
|
|
|
+ { "room_id":"room_beta@sy.org", "topic":"Hello world" },
|
|
|
+ { "room_id":"room_xyz@sy.org", "topic":"Goodbye cruel world" }
|
|
|
+ ]
|
|
|
+}
|
|
|
+
|
|
|
+JOIN ROOM room_beta@sy.org
|
|
|
+PUT
|
|
|
+/rooms/room_beta%40sy.org/members/bob%40sy.org/state?
|
|
|
+ access_token=abcdef0123456789
|
|
|
+Content: { "membership" : "join" }
|
|
|
+Returns: 200 OK
|
|
|
+
|
|
|
+GET LATEST 2 MESSAGES WITH FEEDBACK
|
|
|
+GET
|
|
|
+/rooms/room_beta%40sy.org/messages/list?from=END&to=START&limit=2&
|
|
|
+ feedback=true&access_token=abcdef0123456789
|
|
|
+Returns:
|
|
|
+{
|
|
|
+ "chunk":
|
|
|
+ [
|
|
|
+ {
|
|
|
+ "event_id":"01948374",
|
|
|
+ "type":"m.room.message",
|
|
|
+ "room_id":"room_beta@sy.org",
|
|
|
+ "msg_id":"avefifu",
|
|
|
+ "from":"alice@randomhost.org",
|
|
|
+ "hs_ts":139985736,
|
|
|
+ "content":{
|
|
|
+ "msgtype":"m.text",
|
|
|
+ "body":"afk"
|
|
|
+ }
|
|
|
+ "feedback": [
|
|
|
+ {
|
|
|
+ "from":"friend_of_alice@randomhost.org",
|
|
|
+ "feedback":"d",
|
|
|
+ "hs_ts":139985850,
|
|
|
+ "content":{
|
|
|
+ "sender_ts":139985843
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "event_id":"028dfe8373",
|
|
|
+ "type":"m.room.message",
|
|
|
+ "room_id":"room_beta@sy.org",
|
|
|
+ "msg_id":"afhgfff",
|
|
|
+ "from":"alice@randomhost.org",
|
|
|
+ "hs_ts":139970006,
|
|
|
+ "content":{
|
|
|
+ "msgtype":"m.text",
|
|
|
+ "body":"you're my only friend"
|
|
|
+ }
|
|
|
+ "feedback": [
|
|
|
+ {
|
|
|
+ "from":"friend_of_alice@randomhost.org",
|
|
|
+ "feedback":"d",
|
|
|
+ "hs_ts":139970144,
|
|
|
+ "content":{
|
|
|
+ "sender_ts":139970122
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ "start": "stok_04823947",
|
|
|
+ "end": "etok_1426425"
|
|
|
+}
|
|
|
+
|
|
|
+SEND MESSAGE IN ROOM
|
|
|
+PUT
|
|
|
+/rooms/room_beta%40sy.org/messages/bob%40sy.org/m0001?
|
|
|
+ access_token=abcdef0123456789
|
|
|
+Content: { "msgtype" : "text" , "body" : "Hi everyone" }
|
|
|
+Returns: 200 OK
|
|
|
+
|
|
|
+
|
|
|
+Checking the event stream for this user:
|
|
|
+GET: /events?from=START&access_token=abcdef0123456789
|
|
|
+Returns:
|
|
|
+{
|
|
|
+ "chunk":
|
|
|
+ [
|
|
|
+ {
|
|
|
+ "event_id":"e10f3d2b",
|
|
|
+ "type":"m.room.member",
|
|
|
+ "room_id":"room_beta@sy.org",
|
|
|
+ "user_id":"bob@sy.org",
|
|
|
+ "content":{
|
|
|
+ "membership":"join"
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "event_id":"1b352d32",
|
|
|
+ "type":"m.room.message",
|
|
|
+ "room_id":"room_beta@sy.org",
|
|
|
+ "msg_id":"m0001",
|
|
|
+ "from":"bob@sy.org",
|
|
|
+ "hs_ts":140193857,
|
|
|
+ "content":{
|
|
|
+ "msgtype":"m.text",
|
|
|
+ "body":"Hi everyone"
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ "start": "stok_9348635",
|
|
|
+ "end": "etok_1984723"
|
|
|
+}
|
|
|
+
|
|
|
+Client disconnects for a while and the topic is updated in this room, 3 new
|
|
|
+messages arrive whilst offline, and bob is invited to another room.
|
|
|
+
|
|
|
+GET /events?from=etok_1984723&access_token=abcdef0123456789
|
|
|
+Returns:
|
|
|
+{
|
|
|
+ "chunk":
|
|
|
+ [
|
|
|
+ {
|
|
|
+ "event_id":"feee0294",
|
|
|
+ "type":"m.room.topic",
|
|
|
+ "room_id":"room_beta@sy.org",
|
|
|
+ "from":"alice@randomhost.org",
|
|
|
+ "content":{
|
|
|
+ "topic":"FRIENDS ONLY",
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "event_id":"a028bd9e",
|
|
|
+ "type":"m.room.message",
|
|
|
+ "room_id":"room_beta@sy.org",
|
|
|
+ "msg_id":"z839409",
|
|
|
+ "from":"alice@randomhost.org",
|
|
|
+ "hs_ts":140195000,
|
|
|
+ "content":{
|
|
|
+ "msgtype":"m.text",
|
|
|
+ "body":"Hello!!!"
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "event_id":"49372d9e",
|
|
|
+ "type":"m.room.message",
|
|
|
+ "room_id":"room_beta@sy.org",
|
|
|
+ "msg_id":"z839410",
|
|
|
+ "from":"alice@randomhost.org",
|
|
|
+ "hs_ts":140196000,
|
|
|
+ "content":{
|
|
|
+ "msgtype":"m.text",
|
|
|
+ "body":"Let's go to another room"
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "event_id":"10abdd01",
|
|
|
+ "type":"m.room.message",
|
|
|
+ "room_id":"room_beta@sy.org",
|
|
|
+ "msg_id":"z839411",
|
|
|
+ "from":"alice@randomhost.org",
|
|
|
+ "hs_ts":140197000,
|
|
|
+ "content":{
|
|
|
+ "msgtype":"m.text",
|
|
|
+ "body":"You're not my friend"
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "event_id":"0018453d",
|
|
|
+ "type":"m.room.member",
|
|
|
+ "room_id":"commoners@randomhost.org",
|
|
|
+ "from":"alice@randomhost.org",
|
|
|
+ "user_id":"bob@sy.org",
|
|
|
+ "content":{
|
|
|
+ "membership":"invite"
|
|
|
+ }
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ "start": "stok_0184288",
|
|
|
+ "end": "etok_1348723"
|
|
|
+}
|