Skip to content
Snippets Groups Projects
specification.rst 82.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • convention, e.g. ``com.example.myapp.event``.  This ensures event types are
    suitably namespaced for each application and reduces the risk of clashes.
    
    State events
    
    State events can be sent by ``PUT`` ing to
    |/rooms/<room_id>/state/<event_type>/<state_key>|_.  These events will be
    overwritten if ``<room id>``, ``<event type>`` and ``<state key>`` all match.
    If the state event has no ``state_key``, it can be omitted from the path. These
    requests **cannot use transaction IDs** like other ``PUT`` paths because they
    cannot be differentiated from the ``state_key``. Furthermore, ``POST`` is
    unsupported on state paths. Valid requests look like::
    
      PUT /rooms/!roomid:domain/state/m.example.event
      { "key" : "without a state key" }
    
      PUT /rooms/!roomid:domain/state/m.another.example.event/foo
      { "key" : "with 'foo' as the state key" }
    
      POST /rooms/!roomid:domain/state/m.example.event/
      { "key" : "cannot use POST here" }
    
      PUT /rooms/!roomid:domain/state/m.another.example.event/foo/11
      { "key" : "txnIds are not supported" }
    
    Care should be taken to avoid setting the wrong ``state key``::
    
      PUT /rooms/!roomid:domain/state/m.another.example.event/11
      { "key" : "with '11' as the state key, but was probably intended to be a txnId" }
    
    The ``state_key`` is often used to store state about individual users, by using
    the user ID as the ``state_key`` value. For example::
    
      PUT /rooms/!roomid:domain/state/m.favorite.animal.event/%40my_user%3Adomain.com
      { "animal" : "cat", "reason": "fluffy" }
    
    In some cases, there may be no need for a ``state_key``, so it can be omitted::
    
      PUT /rooms/!roomid:domain/state/m.room.bgd.color
      { "color": "red", "hex": "#ff0000" }
    
    See `Room Events`_ for the ``m.`` event specification.
    
    Non-state events
    ----------------
    Non-state events can be sent by sending a request to
    |/rooms/<room_id>/send/<event_type>|_.  These requests *can* use transaction
    IDs and ``PUT``/``POST`` methods. Non-state events allow access to historical
    events and pagination, making it best suited for sending messages.  For
    example::
    
      POST /rooms/!roomid:domain/send/m.custom.example.message
      { "text": "Hello world!" }
    
      PUT /rooms/!roomid:domain/send/m.custom.example.message/11
      { "text": "Goodbye world!" }
    
    See `Room Events`_ for the ``m.`` event specification.
    
    Syncing rooms
    -------------
    .. NOTE::
      This section is a work in progress.
    
    When a client logs in, they may have a list of rooms which they have already
    joined. These rooms may also have a list of events associated with them. The
    purpose of 'syncing' is to present the current room and event information in a
    convenient, compact manner. The events returned are not limited to room events;
    presence events will also be returned. There are two APIs provided:
    
     - |initialSync|_ : A global sync which will present room and event information
       for all rooms the user has joined.
    
     - |/rooms/<room_id>/initialSync|_ : A sync scoped to a single room. Presents
       room and event information for this room only.
    
      - TODO: JSON response format for both types
      - TODO: when would you use global? when would you use scoped?
    
    Getting events for a room
    -------------------------
    There are several APIs provided to ``GET`` events for a room:
    
    ``/rooms/<room id>/state/<event type>/<state key>``
      Description:
        Get the state event identified.
      Response format:
        A JSON object representing the state event **content**.
      Example:
        ``/rooms/!room:domain.com/state/m.room.name`` returns ``{ "name": "Room name" }``
    
    |/rooms/<room_id>/state|_
      Description:
        Get all state events for a room.
      Response format:
        ``[ { state event }, { state event }, ... ]``
      Example:
    
    |/rooms/<room_id>/members|_
      Description:
        Get all ``m.room.member`` state events.
      Response format:
        ``{ "start": "<token>", "end": "<token>", "chunk": [ { m.room.member event }, ... ] }``
      Example:
    
    |/rooms/<room_id>/messages|_
      Description:
        Get all ``m.room.message`` and ``m.room.member`` events. This API supports
        pagination using ``from`` and ``to`` query parameters, coupled with the
        ``start`` and ``end`` tokens from an |initialSync|_ API.
      Response format:
        ``{ "start": "<token>", "end": "<token>" }``
      Example:
    
        Get all relevant events for a room. This includes state events, paginated
        non-state events and presence events.
    
    Redactions
    ----------
    
    Since events are extensible it is possible for malicious users and/or servers
    to add keys that are, for example offensive or illegal. Since some events
    cannot be simply deleted, e.g. membership events, we instead 'redact' events.
    This involves removing all keys from an event that are not required by the
    protocol. This stripped down event is thereafter returned anytime a client or
    remote server requests it.
    
    Events that have been redacted include a ``redacted_because`` key whose value
    is the event that caused it to be redacted, which may include a reason.
    
    Redacting an event cannot be undone, allowing server owners to delete the
    offending content from the databases.
    
    Currently, only room admins can redact events by sending a ``m.room.redaction``
    
    event, but server admins also need to be able to redact events by a similar
    mechanism.
    
    Room Events
    ===========
    .. NOTE::
      This section is a work in progress.
    
    This specification outlines several standard event types, all of which are
    prefixed with ``m.``
    
    ``m.room.name``
      Summary:
        Set the human-readable name for the room.
      Type: 
        State event
      JSON format:
        ``{ "name" : "string" }``
      Example:
        ``{ "name" : "My Room" }``
      Description:
        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. The room name can also be set when creating a room using |createRoom|_
        with the ``name`` key.
    
    ``m.room.topic``
      Summary:
        Set a topic for the room.
      Type: 
        State event
      JSON format:
        ``{ "topic" : "string" }``
      Example:
        ``{ "topic" : "Welcome to the real world." }``
      Description:
        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.
    
    ``m.room.member``
      Summary:
        The current membership state of a user in the room.
      Type: 
        State event
      JSON format:
        ``{ "membership" : "enum[ invite|join|leave|ban ]" }``
      Example:
        ``{ "membership" : "join" }``
      Description:
        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. See the `Rooms`_ section for how to use the membership APIs.
    
    ``m.room.create``
      Summary:
        The first event in the room.
      Type: 
        State event
      JSON format:
        ``{ "creator": "string"}``
      Example:
        ``{ "creator": "@user:example.com" }``
      Description:
        This is the first event in a room and cannot be changed. It acts as the 
        root of all other events.
    
    ``m.room.join_rules``
      Summary:
        Descripes how/if people are allowed to join.
      Type: 
        State event
      JSON format:
        ``{ "join_rule": "enum [ public|knock|invite|private ]" }``
      Example:
        ``{ "join_rule": "public" }``
      Description:
    
       
    ``m.room.power_levels``
      Summary:
        Defines the power levels of users in the room.
      Type: 
        State event
      JSON format:
        ``{ "<user_id>": <int>, ..., "default": <int>}``
      Example:
        ``{ "@user:example.com": 5, "@user2:example.com": 10, "default": 0 }`` 
      Description:
        If a user is in the list, then they have the associated power level. 
        Otherwise they have the default level. If not ``default`` key is supplied,
        it is assumed to be 0.
    
    ``m.room.add_state_level``
      Summary:
        Defines the minimum power level a user needs to add state.
      Type: 
        State event
      JSON format:
        ``{ "level": <int> }``
      Example:
        ``{ "level": 5 }``
      Description:
        To add a new piece of state to the room a user must have the given power 
        level. This does not apply to updating current state, which is goverened
        by the ``required_power_level`` event key.
        
    ``m.room.send_event_level``
      Summary:
        Defines the minimum power level a user needs to send an event.
      Type: 
        State event
      JSON format:
        ``{ "level": <int> }``
      Example:
        ``{ "level": 0 }``
      Description:
        To send a new event into the room a user must have at least this power 
        level. This allows ops to make the room read only by increasing this level,
        or muting individual users by lowering their power level below this
        threshold.
    
    ``m.room.ops_levels``
      Summary:
        Defines the minimum power levels that a user must have before they can 
        kick and/or ban other users.
      Type: 
        State event
      JSON format:
        ``{ "ban_level": <int>, "kick_level": <int> }``
      Example:
        ``{ "ban_level": 5, "kick_level": 5 }``
      Description:
        This defines who can ban and/or kick people in the room. Most of the time
        ``ban_level`` will be greater than or equal to ``kick_level`` since 
        banning is more severe than kicking.
    
    ``m.room.aliases``
      Summary:
        These state events are used to inform the room about what room aliases it
        has.
      Type:
        State event
      JSON format:
        ``{ "aliases": ["string", ...] }``
      Example:
        ``{ "aliases": ["#foo:example.com"] }``
      Description:
        A server `may` inform the room that it has added or removed an alias for
        the room. This is purely for informational purposes and may become stale.
        Clients `should` check that the room alias is still valid before using it.
        The ``state_key`` of the event is the homeserver which owns the room alias.
    
    ``m.room.message``
      Summary:
        A message.
      Type: 
        Non-state event
      JSON format:
        ``{ "msgtype": "string" }``
      Example:
        ``{ "msgtype": "m.text", "body": "Testing" }``
      Description:
        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.  Whilst not required, the ``body`` key
        SHOULD be used with every kind of ``msgtype`` as a fallback mechanism when
        a client cannot render the message. For more information on the types of
        messages which can be sent, see `m.room.message msgtypes`_.
    
    ``m.room.message.feedback``
      Summary:
        A receipt for a message.
      Type: 
        Non-state event
      JSON format:
        ``{ "type": "enum [ delivered|read ]", "target_event_id": "string" }``
      Example:
        ``{ "type": "delivered", "target_event_id": "e3b2icys" }``
      Description:
        Feedback events are events sent to acknowledge a message in some way. There
        are two supported acknowledgements: ``delivered`` (sent when the event has
        been received) and ``read`` (sent when the event has been observed by the
        end-user). The ``target_event_id`` should reference the ``m.room.message``
        event being acknowledged. 
    
    ``m.room.redaction``
      Summary:
        Indicates a previous event has been redacted.
      Type:
        Non-state event
      JSON format:
        ``{ "reason": "string" }``
      Description:
    
        Events can be redacted by either room or server admins. Redacting an event
        means that all keys not required by the protocol are stripped off, allowing
        admins to remove offensive or illegal content that may have been attached
        to any event. This cannot be undone, allowing server owners to physically
        delete the offending data.  There is also a concept of a moderator hiding a
        non-state event, which can be undone, but cannot be applied to state
        events.
        The event that has been redacted is specified in the ``redacts`` event
        level key.
    
    m.room.message msgtypes
    -----------------------
    Each ``m.room.message`` MUST have a ``msgtype`` key which identifies the type
    of message being sent. Each type has their own required and optional keys, as
    outlined below:
    
    ``m.text``
      Required keys:
        - ``body`` : "string" - The body of the message.
      Optional keys:
        None.
      Example:
        ``{ "msgtype": "m.text", "body": "I am a fish" }``
    
    ``m.emote``
      Required keys:
        - ``body`` : "string" - The emote action to perform.
      Optional keys:
        None.
      Example:
        ``{ "msgtype": "m.emote", "body": "tries to come up with a witty explanation" }``
    
    ``m.image``
      Required keys:
        - ``url`` : "string" - The URL to the image.
      Optional keys:
        - ``info`` : "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``.
        - ``body`` : "string" - The alt text of the image, or some kind of content
          description for accessibility e.g. "image attachment".
    
      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)",
          }
    
    ``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``.
        - ``body`` : "string" - A description of the audio e.g. "Bee Gees - Stayin'
          Alive", or some kind of content description for accessibility e.g.
          "audio attachment".
      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),
          }
    
    ``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``.
        - ``body`` : "string" - A description of the video e.g. "Gangnam style", or
          some kind of content description for accessibility e.g. "video
          attachment".
    
          {
            "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)
          }
    
    ``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``.
        - ``body`` : "string" - A description of the location e.g. "Big Ben,
          London, UK", or some kind of content description for accessibility e.g.
          "location attachment".
    
    The following keys can be attached to any ``m.room.message``:
    
      Optional keys:
        - ``sender_ts`` : integer - A timestamp (ms resolution) representing the
          wall-clock time when the message was sent from the client.
    
    Presence
    ========
    .. NOTE::
      This section is a work in progress.
    
    Each user has the concept of presence information. This encodes the
    "availability" of that user, suitable for display on other user's clients. This
    is transmitted as an ``m.presence`` event and is one of the few events which
    are sent *outside the context of a room*. The basic piece of presence
    information is represented by the ``presence`` key, which is an enum of one of
    the following:
    
      - ``online`` : The default state when the user is connected to an event
        stream.
      - ``unavailable`` : The user is not reachable at this time.
      - ``offline`` : The user is not connected to an event stream.
      - ``free_for_chat`` : The user is generally willing to receive messages
        moreso than default.
    
      - ``hidden`` : Behaves as offline, but allows the user to see the client
        state anyway and generally interact with client features. (Not yet
        implemented in synapse).
    
    This basic ``presence`` field applies to the user as a whole, regardless of how
    many client devices they have connected. The home server should synchronise
    this status choice among multiple devices to ensure the user gets a consistent
    experience.
    
    In addition, the server maintains a timestamp of the last time it saw an active
    action from the user; either sending a message to a room, or changing presence
    state from a lower to a higher level of availability (thus: changing state from
    ``unavailable`` to ``online`` will count as an action for being active, whereas
    in the other direction will not). This timestamp is presented via a key called
    ``last_active_ago``, which gives the relative number of miliseconds since the
    message is generated/emitted, that the user was last seen active.
    
    Transmission
    ------------
    .. NOTE::
      This section is a work in progress.
    
      - Transmitted as an EDU.
      - Presence lists determine who to send to.
    
    Presence List
    -------------
    Each user's home server stores a "presence list" for that user. This stores a
    list of other user IDs the user has chosen to add to it. To be added to this
    list, the user being added must receive permission from the list owner. Once
    granted, both user's HS(es) store this information. Since such subscriptions
    are likely to be bidirectional, HSes may wish to automatically accept requests
    when a reverse subscription already exists.
    
    Presence and Permissions
    ------------------------
    For a viewing user to be allowed to see the presence information of a target
    user, either:
    
     - The target user has allowed the viewing user to add them to their presence
       list, or
     - The two users share at least one room in common
    
    In the latter case, this allows for clients to display some minimal sense of
    presence information in a user list for a room.
    
    
    Voice over IP
    =============
    Matrix can also be used to set up VoIP calls. This is part of the core
    specification, although is still in a very early stage. Voice (and video) over
    Matrix is based on the WebRTC standards.
    
    Call events are sent to a room, like any other event. This means that clients
    must only send call events to rooms with exactly two participants as currently
    the WebRTC standard is based around two-party communication.
    
    Events
    ------
    ``m.call.invite``
    This event is sent by the caller when they wish to establish a call.
    
      Required keys:
        - ``call_id`` : "string" - A unique identifier for the call
        - ``offer`` : "offer object" - The session description
        - ``version`` : "integer" - The version of the VoIP specification this
          message adheres to. This specification is version 0.
        - ``lifetime`` : "integer" - 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.
          
      Optional keys:
        None.
      Example:
        ``{ "version" : 0, "call_id": "12345", "offer": { "type" : "offer", "sdp" : "v=0\r\no=- 6584580628695956864 2 IN IP4 127.0.0.1[...]" } }``
    
    ``Offer Object``
      Required keys:
        - ``type`` : "string" - The type of session description, in this case
          'offer'
        - ``sdp`` : "string" - The SDP text of the session description
    
    ``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.
    
      Required keys:
        - ``call_id`` : "string" - The ID of the call this event relates to
        - ``version`` : "integer" - The version of the VoIP specification this
          messages adheres to. his specification is version 0.
        - ``candidates`` : "array of candidate objects" - Array of object
          describing the candidates.
    
      Required Keys:
        - ``sdpMid`` : "string" - The SDP media type this candidate is intended
          for.
        - ``sdpMLineIndex`` : "integer" - The index of the SDP 'm' line this
          candidate is intended for
        - ``candidate`` : "string" - The SDP 'a' line of the candidate
    
      Required keys:
        - ``call_id`` : "string" - The ID of the call this event relates to
        - ``version`` : "integer" - The version of the VoIP specification this
          messages
        - ``answer`` : "answer object" - Object giving the SDK answer
    
      Required keys:
        - ``type`` : "string" - The type of session description. 'answer' in this
          case.
        - ``sdp`` : "string" - The SDP text of the session description
    
    ``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.
    
      Required keys:
        - ``call_id`` : "string" - The ID of the call this event relates to
        - ``version`` : "integer" - The version of the VoIP specification this
          messages
    
    Message Exchange
    ----------------
    A call is set up with messages exchanged as follows:
    
       Caller                   Callee
     m.call.invite ----------->
     m.call.candidate -------->
     [more candidates events]
                             User answers call
                      <------ m.call.answer
                   [...]
                      <------ m.call.hangup
                      
    Or a rejected call:
    
       Caller                   Callee
     m.call.invite ----------->
     m.call.candidate -------->
     [more candidates events]
                            User rejects call
                     <------- m.call.hangup
    
    Calls are negotiated according to the WebRTC specification.
    
    Glare
    -----
    This specification aims to address the problem of two users calling each other
    at roughly the same time and their invites crossing on the wire. It is a far
    better experience for the users if their calls are connected if it is clear
    that their intention is to set up a call with one another.
    
    In Matrix, calls are to rooms rather than users (even if those rooms may only
    contain one other user) so we consider calls which are to the same room.
    
    The rules for dealing with such a situation are as follows:
    
     - If an invite to a room is received whilst the client is preparing to send an
       invite to the same room, the client should cancel its outgoing call and
       instead automatically accept the incoming call on behalf of the user.
     - If an invite to a room is received after the client has sent an invite to
       the same room and is waiting for a response, the client should perform a
       lexicographical comparison of the call IDs of the two calls and use the
       lesser of the two calls, aborting the greater. If the incoming call is the
       lesser, the client should accept this call on behalf of the user.
    
    The call setup should appear seamless to the user as if they had simply placed
    a call and the other party had accepted. Thusly, any media stream that had been
    setup for use on a call should be transferred and used for the call that
    replaces it.
     
    
    Profiles
    ========
    .. NOTE::
      This section is a work in progress.
    
      - Metadata extensibility
      - Changing profile info generates m.presence events ("presencelike")
      - keys on m.presence are optional, except presence which is required
      - m.room.member is populated with the current displayname at that point in time.
      - That is added by the HS, not you.
      - Display name changes also generates m.room.member with displayname key f.e. room
        the user is in.
    
    Internally within Matrix users are referred to by their user ID, which is
    typically a compact unique identifier. Profiles grant users the ability to see
    human-readable names for other users that are in some way meaningful to them.
    Additionally, profiles can publish additional information, such as the user's
    age or location.
    
    A Profile consists of a display name, an avatar picture, and a set of other
    metadata fields that the user may wish to publish (email address, phone
    numbers, website URLs, etc...). This specification puts no requirements on the
    display name other than it being a valid unicode string.
    
    .. NOTE::
      This section is a work in progress.
    
      - 3PIDs and identity server, functions
    
    Federation is the term used to describe how to communicate between Matrix home
    
    servers. Federation is a mechanism by which two home servers can exchange
    Matrix event messages, both as a real-time push of current events, and as a
    historic fetching mechanism to synchronise past history for clients to view. It
    
    Matthew Hodgson's avatar
    Matthew Hodgson committed
    uses HTTPS connections between each pair of servers involved as the underlying
    
    transport. Messages are exchanged between servers in real-time by active
    pushing from each server's HTTP client into the server of the other. Queries to
    fetch historic data for the purpose of back-filling scrollback buffers and the
    like can also be performed. Currently routing of messages between homeservers
    is full mesh (like email) - however, fan-out refinements to this design are
    currently under consideration.
    
    
    There are three main kinds of communication that occur between home servers:
    
    
       These are single request/response interactions between a given pair of
    
    Matthew Hodgson's avatar
    Matthew Hodgson committed
       servers, initiated by one side sending an HTTPS GET request to obtain some
    
       information, and responded by the other. They are not persisted and contain
    
       no long-term significant history. They simply request a snapshot state at
       the instant the query is made.
    
       These are notifications of events that are pushed from one home server to
    
       another. They are not persisted and contain no long-term significant
       history, nor does the receiving home server have to reply to them.
    
       These are notifications of events that are broadcast from one home server to
       any others that are interested in the same "context" (namely, a Room ID).
       They are persisted to long-term storage and form the record of history for
       that context.
    
    
    EDUs and PDUs are further wrapped in an envelope called a Transaction, which is
    transferred from the origin to the destination home server using an HTTP PUT
    request.
    
    .. WARNING::
      This section may be misleading or inaccurate.
    
    
    The transfer of EDUs and PDUs between home servers is performed by an exchange
    
    of Transaction messages, which are encoded as JSON objects, passed over an HTTP
    PUT request. A Transaction is meaningful only to the pair of home servers that
    
    exchanged it; they are not globally-meaningful.
    
    Each transaction has:
     - An opaque transaction ID.
    
     - A timestamp (UNIX epoch time in milliseconds) generated by its origin
       server.
    
     - An origin and destination server name.
     - A list of "previous IDs".
    
     - A list of PDUs and EDUs - the actual message payload that the Transaction
       carries.
    
     
    ``origin``
      Type: 
        String
      Description:
        DNS name of homeserver making this transaction.
        
    ``ts``
      Type: 
        Integer
      Description:
        Timestamp in milliseconds on originating homeserver when this transaction 
        started.
        
    ``previous_ids``
      Type:
        List of strings
      Description:
        List of transactions that were sent immediately prior to this transaction.
        
    ``pdus``
      Type:
        List of Objects.
      Description:
        List of updates contained in this transaction.
    
     {
      "transaction_id":"916d630ea616342b42e98a3be0b74113",
    
      "ts":1404835423000,
      "origin":"red",
      "destination":"blue",
      "prev_ids":["e1da392e61898be4d2009b9fecce5325"],
      "pdus":[...],
    
    The ``prev_ids`` field contains a list of previous transaction IDs that the
    ``origin`` server has sent to this ``destination``. Its purpose is to act as a
    
    sequence checking mechanism - the destination server can check whether it has
    successfully received that Transaction, or ask for a retransmission if not.
    
    
    The ``pdus`` field of a transaction is a list, containing zero or more PDUs.[*]
    
    Each PDU is itself a JSON object containing a number of keys, the exact details
    of which will vary depending on the type of PDU. Similarly, the ``edus`` field
    is another list containing the EDUs. This key may be entirely absent if there
    are no EDUs to transfer.
    
    
    (* Normally the PDU list will be non-empty, but the server should cope with
    
    receiving an "empty" transaction, as this is useful for informing peers of
    other transaction IDs they should be aware of. This effectively acts as a push
    
    mechanism to encourage peers to continue to replicate content.)
    
    
    .. WARNING::
      This section may be misleading or inaccurate.
    
    
    All PDUs have:
     - An ID
     - A context
     - A declaration of their type
    
     - A list of other PDU IDs that have been seen recently on that context
       (regardless of which origin sent them)
    
    ``context``
      Type:
        String
      Description:
        Event context identifier
        
    ``origin``
      Type:
        String
      Description:
        DNS name of homeserver that created this PDU.
        
    ``pdu_id``
      Type:
        String
      Description:
        Unique identifier for PDU within the context for the originating homeserver
    
    ``ts``
      Type:
        Integer
      Description:
    
        Timestamp in milliseconds on originating homeserver when this PDU was
        created.
    
    
    ``pdu_type``
      Type:
        String
      Description:
        PDU event type.
    
    ``prev_pdus``
      Type:
        List of pairs of strings
      Description:
    
        The originating homeserver and PDU ids of the most recent PDUs the
    
        homeserver was aware of for this context when it made this PDU.
    
    ``depth``
      Type:
        Integer
      Description:
        The maximum depth of the previous PDUs plus one.
    
    
    
    .. TODO-spec paul
      - Update this structure so that 'pdu_id' is a two-element [origin,ref] pair
        like the prev_pdus are
    
    
    For state updates:
    
    ``is_state``
      Type:
        Boolean
      Description:
        True if this PDU is updating state.
        
    ``state_key``
      Type:
        String
      Description:
        Optional key identifying the updated state within the context.
        
    ``power_level``
      Type:
        Integer
      Description:
        The asserted power level of the user performing the update.
        
    ``min_update``
      Type:
        Integer
      Description:
        The required power level needed to replace this update.
    
    ``prev_state_id``
      Type:
        String
      Description:
        PDU event type.
        
    ``prev_state_origin``
      Type:
        String
      Description:
        The PDU id of the update this replaces.
        
    ``user``
      Type:
        String
      Description:
        The user updating the state.
    
      "context":"#example.green",
      "origin":"green",
      "ts":1404838188000,
      "pdu_type":"m.text",
      "prev_pdus":[["blue","99d16afbc857975916f1d73e49e52b65"]],
      "content":...
    
    In contrast to Transactions, it is important to note that the ``prev_pdus``
    
    field of a PDU refers to PDUs that any origin server has sent, rather than
    
    previous IDs that this ``origin`` has sent. This list may refer to other PDUs
    sent by the same origin as the current one, or other origins.
    
    
    Because of the distributed nature of participants in a Matrix conversation, it
    is impossible to establish a globally-consistent total ordering on the events.
    
    However, by annotating each outbound PDU at its origin with IDs of other PDUs
    it has received, a partial ordering can be constructed allowing causality
    
    relationships to be preserved. A client can then display these messages to the
    end-user in some order consistent with their content and ensure that no message
    that is semantically in reply of an earlier one is ever displayed before it.
    
    PDUs fall into two main categories: those that deliver Events, and those that
    synchronise State. For PDUs that relate to State synchronisation, additional
    keys exist to support this:
    
    
      "state_key":TODO-doc
      "power_level":TODO-doc
      "prev_state_id":TODO-doc
      "prev_state_origin":TODO-doc}
    
    
    EDUs, by comparison to PDUs, do not have an ID, a context, or a list of
    "previous" IDs. The only mandatory fields for these are the type, origin and
    destination home server names, and the actual nested content.
    
    
     {"edu_type":"m.presence",
      "origin":"blue",
      "destination":"orange",
      "content":...}
    
      
      
    Protocol URLs
    =============
    .. WARNING::
      This section may be misleading or inaccurate.
    
    All these URLs are namespaced within a prefix of::
    
      /_matrix/federation/v1/...
    
    For active pushing of messages representing live activity "as it happens"::
    
      PUT .../send/:transaction_id/
        Body: JSON encoding of a single Transaction
    
    
    The transaction_id path argument will override any ID given in the JSON body.
    The destination name will be set to that of the receiving server itself. Each
    embedded PDU in the transaction body will be processed.
    
    
    To fetch a particular PDU::
    
      GET .../pdu/:origin/:pdu_id/
        Response: JSON encoding of a single Transaction containing one PDU
    
    Retrieves a given PDU from the server. The response will contain a single new
    Transaction, inside which will be the requested PDU.
      
    
    To fetch all the state of a given context::