summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--docs/client-server/howto.rst531
-rw-r--r--jsfiddles/create_room_send_msg/demo.html2
-rw-r--r--jsfiddles/create_room_send_msg/demo.js8
-rw-r--r--jsfiddles/event_stream/demo.html2
-rw-r--r--jsfiddles/event_stream/demo.js8
-rw-r--r--jsfiddles/example_app/demo.html2
-rw-r--r--jsfiddles/example_app/demo.js28
-rw-r--r--jsfiddles/register_login/demo.html2
-rw-r--r--jsfiddles/register_login/demo.js8
-rw-r--r--jsfiddles/room_memberships/demo.html2
-rw-r--r--jsfiddles/room_memberships/demo.js10
11 files changed, 465 insertions, 138 deletions
diff --git a/docs/client-server/howto.rst b/docs/client-server/howto.rst
index 3660c73d36..54b80e7dea 100644
--- a/docs/client-server/howto.rst
+++ b/docs/client-server/howto.rst
@@ -1,6 +1,4 @@
-TODO(kegan): Tweak joinalias API keys/path? Event stream historical > live needs
-a token (currently doesn't). im/sync responses include outdated event formats
-(room membership change messages). Room config (specifically: message history,
+TODO(kegan):  Room config (specifically: message history,
 public rooms). /register seems super simplistic compared to /login, maybe it
 would be better if /register used the same technique as /login? /register should
 be "user" not "user_id".
@@ -15,7 +13,7 @@ implementation, there may be variations in relation to registering/logging in
 which are not covered in extensive detail in this guide.
 
 If you haven't already, get a home server up and running on 
-``http://localhost:8080``.
+``http://localhost:8008``.
 
 
 Accounts
@@ -23,14 +21,14 @@ Accounts
 Before you can send and receive messages, you must **register** for an account. 
 If you already have an account, you must **login** into it.
 
-**Try out the fiddle: http://jsfiddle.net/jrf1h02d/**
+**Try out the fiddle: http://jsfiddle.net/4q2jyxng/**
 
 Registration
 ------------
 The aim of registration is to get a user ID and access token which you will need
 when accessing other APIs::
 
-    curl -XPOST -d '{"user_id":"example", "password":"wordpass"}' "http://localhost:8080/_matrix/client/api/v1/register"
+    curl -XPOST -d '{"user_id":"example", "password":"wordpass"}' "http://localhost:8008/_matrix/client/api/v1/register"
 
     {
         "access_token": "QGV4YW1wbGU6bG9jYWxob3N0.AqdSzFmFYrLrTmteXc", 
@@ -51,13 +49,17 @@ Login
 -----
 The aim when logging in is to get an access token for your existing user ID::
 
-    curl -XGET "http://localhost:8080/_matrix/client/api/v1/login"
+    curl -XGET "http://localhost:8008/_matrix/client/api/v1/login"
 
     {
-        "type": "m.login.password"
+        "flows": [
+            {
+                "type": "m.login.password"
+            }
+        ]
     }
 
-    curl -XPOST -d '{"type":"m.login.password", "user":"example", "password":"wordpass"}' "http://localhost:8080/_matrix/client/api/v1/login"
+    curl -XPOST -d '{"type":"m.login.password", "user":"example", "password":"wordpass"}' "http://localhost:8008/_matrix/client/api/v1/login"
 
     {
         "access_token": "QGV4YW1wbGU6bG9jYWxob3N0.vRDLTgxefmKWQEtgGd", 
@@ -80,14 +82,14 @@ Communicating
 In order to communicate with another user, you must **create a room** with that 
 user and **send a message** to that room. 
 
-**Try out the fiddle: http://jsfiddle.net/jnwqcshc/**
+**Try out the fiddle: http://jsfiddle.net/zL3zto9g/**
 
 Creating a room
 ---------------
 If you want to send a message to someone, you have to be in a room with them. To
 create a room::
 
-    curl -XPOST -d '{"room_alias_name":"tutorial"}' "http://localhost:8080/_matrix/client/api/v1/rooms?access_token=QGV4YW1wbGU6bG9jYWxob3N0.vRDLTgxefmKWQEtgGd"
+    curl -XPOST -d '{"room_alias_name":"tutorial"}' "http://localhost:8008/_matrix/client/api/v1/createRoom?access_token=YOUR_ACCESS_TOKEN"
 
     {
         "room_alias": "#tutorial:localhost", 
@@ -105,13 +107,19 @@ Sending messages
 ----------------
 You can now send messages to this room::
 
-    curl -XPUT -d '{"msgtype":"m.text", "body":"hello"}' "http://localhost:8080/_matrix/client/api/v1/rooms/%21CvcvRuDYDzTOzfKKgh:localhost/messages/%40example%3Alocalhost/msgid1?access_token=QGV4YW1wbGU6bG9jYWxob3N0.vRDLTgxefmKWQEtgGd"
+    curl -XPOST -d '{"msgtype":"m.text", "body":"hello"}' "http://localhost:8008/_matrix/client/api/v1/rooms/%21CvcvRuDYDzTOzfKKgh%3Alocalhost/send/m.room.message?access_token=YOUR_ACCESS_TOKEN"
+    
+    {
+        "event_id": "YUwRidLecu"
+    }
+    
+The event ID returned is a unique ID which identifies this message.
     
 NB: There are no limitations to the types of messages which can be exchanged.
-The only requirement is that ``"msgtype"`` is specified.
-
-NB: Depending on the room config, users who join the room may be able to see
-message history from before they joined.
+The only requirement is that ``"msgtype"`` is specified. The Matrix 
+specification outlines the following standard types: ``m.text``, ``m.image``,
+``m.audio``, ``m.video``, ``m.location``, ``m.emote``. See the specification for
+more information on these types.
 
 Users and rooms
 ===============
@@ -121,33 +129,32 @@ these rules may specify if you require an **invitation** from someone already in
 the room in order to **join the room**. In addition, you may also be able to 
 join a room **via a room alias** if one was set up.
 
-**Try out the fiddle: http://jsfiddle.net/og1xokcr/**
+**Try out the fiddle: http://jsfiddle.net/7fhotf1b/**
 
 Inviting a user to a room
 -------------------------
 You can directly invite a user to a room like so::
 
-    curl -XPUT -d '{"membership":"invite"}' "http://localhost:8080/_matrix/client/api/v1/rooms/%21CvcvRuDYDzTOzfKKgh:localhost/members/%40myfriend%3Alocalhost/state?access_token=QGV4YW1wbGU6bG9jYWxob3N0.vRDLTgxefmKWQEtgGd"
+    curl -XPOST -d '{"user_id":"@myfriend:localhost"}' "http://localhost:8008/_matrix/client/api/v1/rooms/%21CvcvRuDYDzTOzfKKgh%3Alocalhost/invite?access_token=YOUR_ACCESS_TOKEN"
     
 This informs ``@myfriend:localhost`` of the room ID 
 ``!CvcvRuDYDzTOzfKKgh:localhost`` and allows them to join the room.
 
 Joining a room via an invite
 ----------------------------
-If you receive an invite, you can join the room by changing the membership to
-join::
+If you receive an invite, you can join the room::
 
-    curl -XPUT -d '{"membership":"join"}' "http://localhost:8080/_matrix/client/api/v1/rooms/%21CvcvRuDYDzTOzfKKgh:localhost/members/%40myfriend%3Alocalhost/state?access_token=QG15ZnJpZW5kOmxvY2FsaG9zdA...XKuGdVsovHmwMyDDvK"
+    curl -XPOST -d '{}' "http://localhost:8008/_matrix/client/api/v1/rooms/%21CvcvRuDYDzTOzfKKgh%3Alocalhost/join?access_token=YOUR_ACCESS_TOKEN"
     
 NB: Only the person invited (``@myfriend:localhost``) can change the membership
-state to ``"join"``.
+state to ``"join"``. Repeatedly joining a room does nothing.
 
 Joining a room via an alias
 ---------------------------
 Alternatively, if you know the room alias for this room and the room config 
 allows it, you can directly join a room via the alias::
 
-    curl -XPUT -d '{}' "http://localhost:8080/_matrix/client/api/v1/join/%23tutorial%3Alocalhost?access_token=QG15ZnJpZW5kOmxvY2FsaG9zdA...XKuGdVsovHmwMyDDvK"
+    curl -XPOST -d '{}' "http://localhost:8008/_matrix/client/api/v1/join/%23tutorial%3Alocalhost?access_token=YOUR_ACCESS_TOKEN"
     
     {
         "room_id": "!CvcvRuDYDzTOzfKKgh:localhost"
@@ -166,128 +173,442 @@ An event is some interesting piece of data that a client may be interested in.
 It can be a message in a room, a room invite, etc. There are many different ways
 of getting events, depending on what the client already knows.
 
-**Try out the fiddle: http://jsfiddle.net/5uk4dqe2/**
+**Try out the fiddle: http://jsfiddle.net/vw11mg37/**
 
 Getting all state
 -----------------
 If the client doesn't know any information on the rooms the user is 
 invited/joined on, they can get all the user's state for all rooms::
 
-    curl -XGET "http://localhost:8080/_matrix/client/api/v1/im/sync?access_token=QG15ZnJpZW5kOmxvY2FsaG9zdA...XKuGdVsovHmwMyDDvK"
+    curl -XGET "http://localhost:8008/_matrix/client/api/v1/initialSync?access_token=YOUR_ACCESS_TOKEN"
     
-    [
-        {
-            "membership": "join", 
-            "messages": {
-                "chunk": [
+    {
+        "end": "s39_18_0", 
+        "presence": [
+            {
+                "content": {
+                    "last_active_ago": 1061436, 
+                    "user_id": "@example:localhost"
+                }, 
+                "type": "m.presence"
+            }
+        ], 
+        "rooms": [
+            {
+                "membership": "join", 
+                "messages": {
+                    "chunk": [
+                        {
+                            "content": {
+                                "@example:localhost": 10, 
+                                "default": 0
+                            }, 
+                            "event_id": "wAumPSTsWF", 
+                            "required_power_level": 10, 
+                            "room_id": "!MkDbyRqnvTYnoxjLYx:localhost", 
+                            "state_key": "", 
+                            "ts": 1409665585188, 
+                            "type": "m.room.power_levels", 
+                            "user_id": "@example:localhost"
+                        }, 
+                        {
+                            "content": {
+                                "join_rule": "public"
+                            }, 
+                            "event_id": "jrLVqKHKiI", 
+                            "required_power_level": 10, 
+                            "room_id": "!MkDbyRqnvTYnoxjLYx:localhost", 
+                            "state_key": "", 
+                            "ts": 1409665585188, 
+                            "type": "m.room.join_rules", 
+                            "user_id": "@example:localhost"
+                        }, 
+                        {
+                            "content": {
+                                "level": 10
+                            }, 
+                            "event_id": "WpmTgsNWUZ", 
+                            "required_power_level": 10, 
+                            "room_id": "!MkDbyRqnvTYnoxjLYx:localhost", 
+                            "state_key": "", 
+                            "ts": 1409665585188, 
+                            "type": "m.room.add_state_level", 
+                            "user_id": "@example:localhost"
+                        }, 
+                        {
+                            "content": {
+                                "level": 0
+                            }, 
+                            "event_id": "qUMBJyKsTQ", 
+                            "required_power_level": 10, 
+                            "room_id": "!MkDbyRqnvTYnoxjLYx:localhost", 
+                            "state_key": "", 
+                            "ts": 1409665585188, 
+                            "type": "m.room.send_event_level", 
+                            "user_id": "@example:localhost"
+                        }, 
+                        {
+                            "content": {
+                                "ban_level": 5, 
+                                "kick_level": 5
+                            }, 
+                            "event_id": "YAaDmKvoUW", 
+                            "required_power_level": 10, 
+                            "room_id": "!MkDbyRqnvTYnoxjLYx:localhost", 
+                            "state_key": "", 
+                            "ts": 1409665585188, 
+                            "type": "m.room.ops_levels", 
+                            "user_id": "@example:localhost"
+                        }, 
+                        {
+                            "content": {
+                                "avatar_url": null, 
+                                "displayname": null, 
+                                "membership": "join"
+                            }, 
+                            "event_id": "RJbPMtCutf", 
+                            "membership": "join", 
+                            "room_id": "!MkDbyRqnvTYnoxjLYx:localhost", 
+                            "state_key": "@example:localhost", 
+                            "ts": 1409665586730, 
+                            "type": "m.room.member", 
+                            "user_id": "@example:localhost"
+                        }, 
+                        {
+                            "content": {
+                                "body": "hello", 
+                                "hsob_ts": 1409665660439, 
+                                "msgtype": "m.text"
+                            }, 
+                            "event_id": "YUwRidLecu", 
+                            "room_id": "!MkDbyRqnvTYnoxjLYx:localhost", 
+                            "ts": 1409665660439, 
+                            "type": "m.room.message", 
+                            "user_id": "@example:localhost"
+                        }, 
+                        {
+                            "content": {
+                                "membership": "invite"
+                            }, 
+                            "event_id": "YjNuBKnPsb", 
+                            "membership": "invite", 
+                            "room_id": "!MkDbyRqnvTYnoxjLYx:localhost", 
+                            "state_key": "@myfriend:localhost", 
+                            "ts": 1409666426819, 
+                            "type": "m.room.member", 
+                            "user_id": "@example:localhost"
+                        }, 
+                        {
+                            "content": {
+                                "avatar_url": null, 
+                                "displayname": null, 
+                                "membership": "join", 
+                                "prev": "join"
+                            }, 
+                            "event_id": "KWwdDjNZnm", 
+                            "membership": "join", 
+                            "room_id": "!MkDbyRqnvTYnoxjLYx:localhost", 
+                            "state_key": "@example:localhost", 
+                            "ts": 1409666551582, 
+                            "type": "m.room.member", 
+                            "user_id": "@example:localhost"
+                        }, 
+                        {
+                            "content": {
+                                "avatar_url": null, 
+                                "displayname": null, 
+                                "membership": "join"
+                            }, 
+                            "event_id": "JFLVteSvQc", 
+                            "membership": "join", 
+                            "room_id": "!MkDbyRqnvTYnoxjLYx:localhost", 
+                            "state_key": "@example:localhost", 
+                            "ts": 1409666587265, 
+                            "type": "m.room.member", 
+                            "user_id": "@example:localhost"
+                        }
+                    ], 
+                    "end": "s39_18_0", 
+                    "start": "t1-11_18_0"
+                }, 
+                "room_id": "!MkDbyRqnvTYnoxjLYx:localhost", 
+                "state": [
                     {
                         "content": {
-                            "body": "@example:localhost joined the room.", 
-                            "hsob_ts": 1408444664249, 
-                            "membership": "join", 
-                            "membership_source": "@example:localhost", 
-                            "membership_target": "@example:localhost", 
-                            "msgtype": "m.text"
+                            "creator": "@example:localhost"
                         }, 
-                        "event_id": "lZjmmlrEvo", 
-                        "msg_id": "m1408444664249", 
-                        "room_id": "!CvcvRuDYDzTOzfKKgh:localhost", 
-                        "type": "m.room.message", 
-                        "user_id": "_homeserver_"
+                        "event_id": "dMUoqVTZca", 
+                        "required_power_level": 10, 
+                        "room_id": "!MkDbyRqnvTYnoxjLYx:localhost", 
+                        "state_key": "", 
+                        "ts": 1409665585188, 
+                        "type": "m.room.create", 
+                        "user_id": "@example:localhost"
                     }, 
                     {
                         "content": {
-                            "body": "hello", 
-                            "hsob_ts": 1408445405672, 
-                            "msgtype": "m.text"
+                            "@example:localhost": 10, 
+                            "default": 0
                         }, 
-                        "event_id": "BiBJqamISg", 
-                        "msg_id": "msgid1", 
-                        "room_id": "!CvcvRuDYDzTOzfKKgh:localhost", 
-                        "type": "m.room.message", 
+                        "event_id": "wAumPSTsWF", 
+                        "required_power_level": 10, 
+                        "room_id": "!MkDbyRqnvTYnoxjLYx:localhost", 
+                        "state_key": "", 
+                        "ts": 1409665585188, 
+                        "type": "m.room.power_levels", 
                         "user_id": "@example:localhost"
                     }, 
-                    [...]
                     {
                         "content": {
-                            "body": "@myfriend:localhost joined the room.", 
-                            "hsob_ts": 1408446501661, 
-                            "membership": "join", 
-                            "membership_source": "@myfriend:localhost", 
-                            "membership_target": "@myfriend:localhost", 
-                            "msgtype": "m.text"
+                            "join_rule": "public"
+                        }, 
+                        "event_id": "jrLVqKHKiI", 
+                        "required_power_level": 10, 
+                        "room_id": "!MkDbyRqnvTYnoxjLYx:localhost", 
+                        "state_key": "", 
+                        "ts": 1409665585188, 
+                        "type": "m.room.join_rules", 
+                        "user_id": "@example:localhost"
+                    }, 
+                    {
+                        "content": {
+                            "level": 10
+                        }, 
+                        "event_id": "WpmTgsNWUZ", 
+                        "required_power_level": 10, 
+                        "room_id": "!MkDbyRqnvTYnoxjLYx:localhost", 
+                        "state_key": "", 
+                        "ts": 1409665585188, 
+                        "type": "m.room.add_state_level", 
+                        "user_id": "@example:localhost"
+                    }, 
+                    {
+                        "content": {
+                            "level": 0
+                        }, 
+                        "event_id": "qUMBJyKsTQ", 
+                        "required_power_level": 10, 
+                        "room_id": "!MkDbyRqnvTYnoxjLYx:localhost", 
+                        "state_key": "", 
+                        "ts": 1409665585188, 
+                        "type": "m.room.send_event_level", 
+                        "user_id": "@example:localhost"
+                    }, 
+                    {
+                        "content": {
+                            "ban_level": 5, 
+                            "kick_level": 5
+                        }, 
+                        "event_id": "YAaDmKvoUW", 
+                        "required_power_level": 10, 
+                        "room_id": "!MkDbyRqnvTYnoxjLYx:localhost", 
+                        "state_key": "", 
+                        "ts": 1409665585188, 
+                        "type": "m.room.ops_levels", 
+                        "user_id": "@example:localhost"
+                    }, 
+                    {
+                        "content": {
+                            "membership": "invite"
+                        }, 
+                        "event_id": "YjNuBKnPsb", 
+                        "membership": "invite", 
+                        "room_id": "!MkDbyRqnvTYnoxjLYx:localhost", 
+                        "state_key": "@myfriend:localhost", 
+                        "ts": 1409666426819, 
+                        "type": "m.room.member", 
+                        "user_id": "@example:localhost"
+                    }, 
+                    {
+                        "content": {
+                            "avatar_url": null, 
+                            "displayname": null, 
+                            "membership": "join"
                         }, 
-                        "event_id": "IMmXbOzFAa", 
-                        "msg_id": "m1408446501661", 
-                        "room_id": "!CvcvRuDYDzTOzfKKgh:localhost", 
-                        "type": "m.room.message", 
-                        "user_id": "_homeserver_"
+                        "event_id": "JFLVteSvQc", 
+                        "membership": "join", 
+                        "room_id": "!MkDbyRqnvTYnoxjLYx:localhost", 
+                        "state_key": "@example:localhost", 
+                        "ts": 1409666587265, 
+                        "type": "m.room.member", 
+                        "user_id": "@example:localhost"
                     }
-                ], 
-                "end": "20", 
-                "start": "0"
-            }, 
-            "room_id": "!CvcvRuDYDzTOzfKKgh:localhost"
-        }
-    ]
+                ]
+            }
+        ]
+    }
     
-This returns all the room IDs of rooms the user is invited/joined on, as well as
-all of the messages and feedback for these rooms. This can be a LOT of data. You
-may just want the most recent message for each room. This can be achieved by 
-applying pagination stream parameters to this request::
+This returns all the room information the user is invited/joined on, as well as
+all of the presences relevant for these rooms. This can be a LOT of data. You
+may just want the most recent event for each room. This can be achieved by 
+applying query parameters to ``limit`` this request::
 
-    curl -XGET "http://localhost:8080/_matrix/client/api/v1/im/sync?access_token=QG15ZnJpZW5kOmxvY2FsaG9zdA...XKuGdVsovHmwMyDDvK&from=END&to=START&limit=1"
+    curl -XGET "http://localhost:8008/_matrix/client/api/v1/initialSync?limit=1&access_token=YOUR_ACCESS_TOKEN"
     
-    [
-        {
-            "membership": "join", 
-            "messages": {
-                "chunk": [
+    {
+        "end": "s39_18_0", 
+        "presence": [
+            {
+                "content": {
+                    "last_active_ago": 1279484, 
+                    "user_id": "@example:localhost"
+                }, 
+                "type": "m.presence"
+            }
+        ], 
+        "rooms": [
+            {
+                "membership": "join", 
+                "messages": {
+                    "chunk": [
+                        {
+                            "content": {
+                                "avatar_url": null, 
+                                "displayname": null, 
+                                "membership": "join"
+                            }, 
+                            "event_id": "JFLVteSvQc", 
+                            "membership": "join", 
+                            "room_id": "!MkDbyRqnvTYnoxjLYx:localhost", 
+                            "state_key": "@example:localhost", 
+                            "ts": 1409666587265, 
+                            "type": "m.room.member", 
+                            "user_id": "@example:localhost"
+                        }
+                    ], 
+                    "end": "s39_18_0", 
+                    "start": "t10-30_18_0"
+                }, 
+                "room_id": "!MkDbyRqnvTYnoxjLYx:localhost", 
+                "state": [
                     {
                         "content": {
-                            "body": "@myfriend:localhost joined the room.", 
-                            "hsob_ts": 1408446501661, 
-                            "membership": "join", 
-                            "membership_source": "@myfriend:localhost", 
-                            "membership_target": "@myfriend:localhost", 
-                            "msgtype": "m.text"
+                            "creator": "@example:localhost"
+                        }, 
+                        "event_id": "dMUoqVTZca", 
+                        "required_power_level": 10, 
+                        "room_id": "!MkDbyRqnvTYnoxjLYx:localhost", 
+                        "state_key": "", 
+                        "ts": 1409665585188, 
+                        "type": "m.room.create", 
+                        "user_id": "@example:localhost"
+                    }, 
+                    {
+                        "content": {
+                            "@example:localhost": 10, 
+                            "default": 0
+                        }, 
+                        "event_id": "wAumPSTsWF", 
+                        "required_power_level": 10, 
+                        "room_id": "!MkDbyRqnvTYnoxjLYx:localhost", 
+                        "state_key": "", 
+                        "ts": 1409665585188, 
+                        "type": "m.room.power_levels", 
+                        "user_id": "@example:localhost"
+                    }, 
+                    {
+                        "content": {
+                            "join_rule": "public"
+                        }, 
+                        "event_id": "jrLVqKHKiI", 
+                        "required_power_level": 10, 
+                        "room_id": "!MkDbyRqnvTYnoxjLYx:localhost", 
+                        "state_key": "", 
+                        "ts": 1409665585188, 
+                        "type": "m.room.join_rules", 
+                        "user_id": "@example:localhost"
+                    }, 
+                    {
+                        "content": {
+                            "level": 10
                         }, 
-                        "event_id": "IMmXbOzFAa", 
-                        "msg_id": "m1408446501661", 
-                        "room_id": "!CvcvRuDYDzTOzfKKgh:localhost", 
-                        "type": "m.room.message", 
-                        "user_id": "_homeserver_"
+                        "event_id": "WpmTgsNWUZ", 
+                        "required_power_level": 10, 
+                        "room_id": "!MkDbyRqnvTYnoxjLYx:localhost", 
+                        "state_key": "", 
+                        "ts": 1409665585188, 
+                        "type": "m.room.add_state_level", 
+                        "user_id": "@example:localhost"
+                    }, 
+                    {
+                        "content": {
+                            "level": 0
+                        }, 
+                        "event_id": "qUMBJyKsTQ", 
+                        "required_power_level": 10, 
+                        "room_id": "!MkDbyRqnvTYnoxjLYx:localhost", 
+                        "state_key": "", 
+                        "ts": 1409665585188, 
+                        "type": "m.room.send_event_level", 
+                        "user_id": "@example:localhost"
+                    }, 
+                    {
+                        "content": {
+                            "ban_level": 5, 
+                            "kick_level": 5
+                        }, 
+                        "event_id": "YAaDmKvoUW", 
+                        "required_power_level": 10, 
+                        "room_id": "!MkDbyRqnvTYnoxjLYx:localhost", 
+                        "state_key": "", 
+                        "ts": 1409665585188, 
+                        "type": "m.room.ops_levels", 
+                        "user_id": "@example:localhost"
+                    }, 
+                    {
+                        "content": {
+                            "membership": "invite"
+                        }, 
+                        "event_id": "YjNuBKnPsb", 
+                        "membership": "invite", 
+                        "room_id": "!MkDbyRqnvTYnoxjLYx:localhost", 
+                        "state_key": "@myfriend:localhost", 
+                        "ts": 1409666426819, 
+                        "type": "m.room.member", 
+                        "user_id": "@example:localhost"
+                    }, 
+                    {
+                        "content": {
+                            "avatar_url": null, 
+                            "displayname": null, 
+                            "membership": "join"
+                        }, 
+                        "event_id": "JFLVteSvQc", 
+                        "membership": "join", 
+                        "room_id": "!MkDbyRqnvTYnoxjLYx:localhost", 
+                        "state_key": "@example:localhost", 
+                        "ts": 1409666587265, 
+                        "type": "m.room.member", 
+                        "user_id": "@example:localhost"
                     }
-                ], 
-                "end": "20", 
-                "start": "21"
-            }, 
-            "room_id": "!CvcvRuDYDzTOzfKKgh:localhost"
-        }
-    ]
+                ]
+            }
+        ]
+    }
 
 Getting live state
 ------------------
 Once you know which rooms the client has previously interacted with, you need to
 listen for incoming events. This can be done like so::
 
-    curl -XGET "http://localhost:8080/_matrix/client/api/v1/events?access_token=QG15ZnJpZW5kOmxvY2FsaG9zdA...XKuGdVsovHmwMyDDvK&from=END"
+    curl -XGET "http://localhost:8008/_matrix/client/api/v1/events?access_token=YOUR_ACCESS_TOKEN"
     
     {
         "chunk": [], 
-        "end": "215", 
-        "start": "215"
+        "end": "s39_18_0", 
+        "start": "s39_18_0"
     }
     
 This will block waiting for an incoming event, timing out after several seconds.
 Even if there are no new events (as in the example above), there will be some
 pagination stream response keys. The client should make subsequent requests 
-using the value of the ``"end"`` key (in this case ``215``) as the ``from`` 
-query parameter. This value should be stored so when the client reopens your app
-after a period of inactivity, you can resume from where you got up to in the 
-event stream. If it has been a long period of inactivity, there may be LOTS of 
-events waiting for the user. In this case, you may wish to get all state instead
-and then resume getting live state from a newer end token.
+using the value of the ``"end"`` key (in this case ``s39_18_0``) as the ``from`` 
+query parameter e.g. ``http://localhost:8008/_matrix/client/api/v1/events?access
+_token=YOUR_ACCESS_TOKEN&from=s39_18_0``. This value should be stored so when the 
+client reopens your app after a period of inactivity, you can resume from where 
+you got up to in the event stream. If it has been a long period of inactivity, 
+there may be LOTS of events waiting for the user. In this case, you may wish to 
+get all state instead and then resume getting live state from a newer end token.
 
 NB: The timeout can be changed by adding a ``timeout`` query parameter, which is
 in milliseconds. A timeout of 0 will not block.
@@ -300,4 +621,4 @@ creating and joining rooms, sending messages, getting member lists and getting
 historical messages for a room. This covers most functionality of a messaging
 application.
 
-**Try out the fiddle: http://jsfiddle.net/L8r3o1wr/**
+**Try out the fiddle: http://jsfiddle.net/uztL3yme/**
diff --git a/jsfiddles/create_room_send_msg/demo.html b/jsfiddles/create_room_send_msg/demo.html
index 31c26c7631..088ff7ac0f 100644
--- a/jsfiddles/create_room_send_msg/demo.html
+++ b/jsfiddles/create_room_send_msg/demo.html
@@ -1,5 +1,5 @@
 <div>
-    <p>This room creation / message sending demo requires a home server to be running on http://localhost:8080</p>
+    <p>This room creation / message sending demo requires a home server to be running on http://localhost:8008</p>
 </div>
 <form class="loginForm">
     <input type="text" id="userLogin" placeholder="Username"></input>
diff --git a/jsfiddles/create_room_send_msg/demo.js b/jsfiddles/create_room_send_msg/demo.js
index 61044da743..3dc7263830 100644
--- a/jsfiddles/create_room_send_msg/demo.js
+++ b/jsfiddles/create_room_send_msg/demo.js
@@ -10,7 +10,7 @@ $('.login').live('click', function() {
     var user = $("#userLogin").val();
     var password = $("#passwordLogin").val();
     $.ajax({
-        url: "http://localhost:8080/_matrix/client/api/v1/login",
+        url: "http://localhost:8008/_matrix/client/api/v1/login",
         type: "POST",
         contentType: "application/json; charset=utf-8",
         data: JSON.stringify({ user: user, password: password, type: "m.login.password" }),
@@ -25,7 +25,7 @@ $('.login').live('click', function() {
 });
 
 var getCurrentRoomList = function() {
-    var url = "http://localhost:8080/_matrix/client/api/v1/initialSync?access_token=" + accountInfo.access_token + "&limit=1";
+    var url = "http://localhost:8008/_matrix/client/api/v1/initialSync?access_token=" + accountInfo.access_token + "&limit=1";
     $.getJSON(url, function(data) {
         var rooms = data.rooms;
         for (var i=0; i<rooms.length; ++i) {
@@ -44,7 +44,7 @@ $('.createRoom').live('click', function() {
         data.room_alias_name = roomAlias;   
     }
     $.ajax({
-        url: "http://localhost:8080/_matrix/client/api/v1/createRoom?access_token="+accountInfo.access_token,
+        url: "http://localhost:8008/_matrix/client/api/v1/createRoom?access_token="+accountInfo.access_token,
         type: "POST",
         contentType: "application/json; charset=utf-8",
         data: JSON.stringify(data),
@@ -79,7 +79,7 @@ $('.sendMessage').live('click', function() {
         return;
     }
     
-    var url = "http://localhost:8080/_matrix/client/api/v1/rooms/$roomid/send/m.room.message?access_token=$token";
+    var url = "http://localhost:8008/_matrix/client/api/v1/rooms/$roomid/send/m.room.message?access_token=$token";
     url = url.replace("$token", accountInfo.access_token);
     url = url.replace("$roomid", encodeURIComponent(roomId));
     
diff --git a/jsfiddles/event_stream/demo.html b/jsfiddles/event_stream/demo.html
index ee4fc3ea68..7657780d28 100644
--- a/jsfiddles/event_stream/demo.html
+++ b/jsfiddles/event_stream/demo.html
@@ -1,5 +1,5 @@
 <div>
-    <p>This event stream demo requires a home server to be running on http://localhost:8080</p>
+    <p>This event stream demo requires a home server to be running on http://localhost:8008</p>
 </div>
 <form class="loginForm">
     <input type="text" id="userLogin" placeholder="Username"></input>
diff --git a/jsfiddles/event_stream/demo.js b/jsfiddles/event_stream/demo.js
index 997d1a2240..5c81e08caa 100644
--- a/jsfiddles/event_stream/demo.js
+++ b/jsfiddles/event_stream/demo.js
@@ -7,7 +7,7 @@ var eventStreamInfo = {
 var roomInfo = [];
 
 var longpollEventStream = function() {
-    var url = "http://localhost:8080/_matrix/client/api/v1/events?access_token=$token&from=$from";
+    var url = "http://localhost:8008/_matrix/client/api/v1/events?access_token=$token&from=$from";
     url = url.replace("$token", accountInfo.access_token);
     url = url.replace("$from", eventStreamInfo.from);
     
@@ -48,7 +48,7 @@ $('.login').live('click', function() {
     var user = $("#userLogin").val();
     var password = $("#passwordLogin").val();
     $.ajax({
-        url: "http://localhost:8080/_matrix/client/api/v1/login",
+        url: "http://localhost:8008/_matrix/client/api/v1/login",
         type: "POST",
         contentType: "application/json; charset=utf-8",
         data: JSON.stringify({ user: user, password: password, type: "m.login.password" }),
@@ -65,7 +65,7 @@ $('.login').live('click', function() {
 
 var getCurrentRoomList = function() {
     $("#roomId").val("");
-    var url = "http://localhost:8080/_matrix/client/api/v1/initialSync?access_token=" + accountInfo.access_token + "&limit=1";
+    var url = "http://localhost:8008/_matrix/client/api/v1/initialSync?access_token=" + accountInfo.access_token + "&limit=1";
     $.getJSON(url, function(data) {
         var rooms = data.rooms;
         for (var i=0; i<rooms.length; ++i) {
@@ -98,7 +98,7 @@ var sendMessage = function(roomId) {
         return;
     }
     
-    var url = "http://localhost:8080/_matrix/client/api/v1/rooms/$roomid/send/m.room.message?access_token=$token";
+    var url = "http://localhost:8008/_matrix/client/api/v1/rooms/$roomid/send/m.room.message?access_token=$token";
     url = url.replace("$token", accountInfo.access_token);
     url = url.replace("$roomid", encodeURIComponent(roomId));
     
diff --git a/jsfiddles/example_app/demo.html b/jsfiddles/example_app/demo.html
index 0af946f6bc..7a9dffddd0 100644
--- a/jsfiddles/example_app/demo.html
+++ b/jsfiddles/example_app/demo.html
@@ -1,5 +1,5 @@
 <div class="signUp">
-    <p>Matrix example application: Requires a local home server running at http://localhost:8080</p>
+    <p>Matrix example application: Requires a local home server running at http://localhost:8008</p>
     <form class="registrationForm">
         <p>No account? Register:</p>
         <input type="text" id="userReg" placeholder="Username"></input>
diff --git a/jsfiddles/example_app/demo.js b/jsfiddles/example_app/demo.js
index 958232047f..ad79fcca26 100644
--- a/jsfiddles/example_app/demo.js
+++ b/jsfiddles/example_app/demo.js
@@ -10,7 +10,7 @@ var viewingRoomId;
 
 // ************** Event Streaming **************
 var longpollEventStream = function() {
-    var url = "http://localhost:8080/_matrix/client/api/v1/events?access_token=$token&from=$from";
+    var url = "http://localhost:8008/_matrix/client/api/v1/events?access_token=$token&from=$from";
     url = url.replace("$token", accountInfo.access_token);
     url = url.replace("$from", eventStreamInfo.from);
 
@@ -89,7 +89,7 @@ $('.login').live('click', function() {
     var user = $("#userLogin").val();
     var password = $("#passwordLogin").val();
     $.ajax({
-        url: "http://localhost:8080/_matrix/client/api/v1/login",
+        url: "http://localhost:8008/_matrix/client/api/v1/login",
         type: "POST",
         contentType: "application/json; charset=utf-8",
         data: JSON.stringify({ user: user, password: password, type: "m.login.password" }),
@@ -107,7 +107,7 @@ $('.register').live('click', function() {
     var user = $("#userReg").val();
     var password = $("#passwordReg").val();
     $.ajax({
-        url: "http://localhost:8080/_matrix/client/api/v1/register",
+        url: "http://localhost:8008/_matrix/client/api/v1/register",
         type: "POST",
         contentType: "application/json; charset=utf-8",
         data: JSON.stringify({ user_id: user, password: password }),
@@ -134,7 +134,7 @@ $('.createRoom').live('click', function() {
         data.room_alias_name = roomAlias;   
     }
     $.ajax({
-        url: "http://localhost:8080/_matrix/client/api/v1/createRoom?access_token="+accountInfo.access_token,
+        url: "http://localhost:8008/_matrix/client/api/v1/createRoom?access_token="+accountInfo.access_token,
         type: "POST",
         contentType: "application/json; charset=utf-8",
         data: JSON.stringify(data),
@@ -155,7 +155,7 @@ $('.createRoom').live('click', function() {
 
 // ************** Getting current state **************
 var getCurrentRoomList = function() {
-    var url = "http://localhost:8080/_matrix/client/api/v1/initialSync?access_token=" + accountInfo.access_token + "&limit=1";
+    var url = "http://localhost:8008/_matrix/client/api/v1/initialSync?access_token=" + accountInfo.access_token + "&limit=1";
     $.getJSON(url, function(data) {
         var rooms = data.rooms;
         for (var i=0; i<rooms.length; ++i) {
@@ -181,7 +181,7 @@ var loadRoomContent = function(roomId) {
 
 var getMessages = function(roomId) {
     $("#messages").empty();
-    var url = "http://localhost:8080/_matrix/client/api/v1/rooms/" + 
+    var url = "http://localhost:8008/_matrix/client/api/v1/rooms/" + 
               encodeURIComponent(roomId) + "/messages?access_token=" + accountInfo.access_token + "&from=END&dir=b&limit=10";
     $.getJSON(url, function(data) {
         for (var i=data.chunk.length-1; i>=0; --i) {
@@ -193,7 +193,7 @@ var getMessages = function(roomId) {
 var getMemberList = function(roomId) {
     $("#members").empty();
     memberInfo = [];
-    var url = "http://localhost:8080/_matrix/client/api/v1/rooms/" + 
+    var url = "http://localhost:8008/_matrix/client/api/v1/rooms/" + 
               encodeURIComponent(roomId) + "/members?access_token=" + accountInfo.access_token;
     $.getJSON(url, function(data) {
         for (var i=0; i<data.chunk.length; ++i) {
@@ -216,7 +216,7 @@ $('.sendMessage').live('click', function() {
 var sendMessage = function(roomId, body) {
     var msgId = $.now();
     
-    var url = "http://localhost:8080/_matrix/client/api/v1/rooms/$roomid/send/m.room.message?access_token=$token";
+    var url = "http://localhost:8008/_matrix/client/api/v1/rooms/$roomid/send/m.room.message?access_token=$token";
     url = url.replace("$token", accountInfo.access_token);
     url = url.replace("$roomid", encodeURIComponent(roomId));
     
@@ -262,7 +262,7 @@ var setRooms = function(roomList) {
         var membership = $(this).find('td:eq(1)').text();
         if (membership !== "join") {
             console.log("Joining room " + roomId); 
-            var url = "http://localhost:8080/_matrix/client/api/v1/rooms/$roomid/join?access_token=$token";
+            var url = "http://localhost:8008/_matrix/client/api/v1/rooms/$roomid/join?access_token=$token";
             url = url.replace("$token", accountInfo.access_token);
             url = url.replace("$roomid", encodeURIComponent(roomId));
             $.ajax({
@@ -290,6 +290,9 @@ var addMessage = function(data) {
 
     var msg = data.content.body;
     if (data.type === "m.room.member") {
+        if (data.content.membership === undefined) {
+            return;
+        }
         if (data.content.membership === "invite") {
             msg = "<em>invited " + data.state_key + " to the room</em>";
         }
@@ -299,10 +302,13 @@ var addMessage = function(data) {
         else if (data.content.membership === "leave") {
             msg = "<em>left the room</em>";
         }
-        else {
-            msg = "<em>" + data.content.membership + "</em>";
+        else if (data.content.membership === "ban") {
+            msg = "<em>was banned from the room</em>";
         }
     }
+    if (msg === undefined) {
+        return;
+    }
 
     var row = "<tr>" +
               "<td>"+data.user_id+"</td>" +
diff --git a/jsfiddles/register_login/demo.html b/jsfiddles/register_login/demo.html
index 9cdb161306..fcac453ac2 100644
--- a/jsfiddles/register_login/demo.html
+++ b/jsfiddles/register_login/demo.html
@@ -1,5 +1,5 @@
 <div>
-    <p>This registration/login demo requires a home server to be running on http://localhost:8080</p>
+    <p>This registration/login demo requires a home server to be running on http://localhost:8008</p>
 </div>
 <form class="registrationForm">
     <input type="text" id="user" placeholder="Username"></input>
diff --git a/jsfiddles/register_login/demo.js b/jsfiddles/register_login/demo.js
index 1e68cb91bd..9595039173 100644
--- a/jsfiddles/register_login/demo.js
+++ b/jsfiddles/register_login/demo.js
@@ -11,7 +11,7 @@ $('.register').live('click', function() {
     var user = $("#user").val();
     var password = $("#password").val();
     $.ajax({
-        url: "http://localhost:8080/_matrix/client/api/v1/register",
+        url: "http://localhost:8008/_matrix/client/api/v1/register",
         type: "POST",
         contentType: "application/json; charset=utf-8",
         data: JSON.stringify({ user_id: user, password: password }),
@@ -27,7 +27,7 @@ $('.register').live('click', function() {
 
 var login = function(user, password) {
     $.ajax({
-        url: "http://localhost:8080/_matrix/client/api/v1/login",
+        url: "http://localhost:8008/_matrix/client/api/v1/login",
         type: "POST",
         contentType: "application/json; charset=utf-8",
         data: JSON.stringify({ user: user, password: password, type: "m.login.password" }),
@@ -44,7 +44,7 @@ var login = function(user, password) {
 $('.login').live('click', function() {
     var user = $("#userLogin").val();
     var password = $("#passwordLogin").val();
-    $.getJSON("http://localhost:8080/_matrix/client/api/v1/login", function(data) {
+    $.getJSON("http://localhost:8008/_matrix/client/api/v1/login", function(data) {
         if (data.flows[0].type !== "m.login.password") {
             alert("I don't know how to login with this type: " + data.type);
             return;
@@ -60,7 +60,7 @@ $('.logout').live('click', function() {
 });
 
 $('.testToken').live('click', function() {
-    var url = "http://localhost:8080/_matrix/client/api/v1/initialSync?access_token=" + accountInfo.access_token + "&limit=1";
+    var url = "http://localhost:8008/_matrix/client/api/v1/initialSync?access_token=" + accountInfo.access_token + "&limit=1";
     $.getJSON(url, function(data) {
          $("#imSyncText").text(JSON.stringify(data, undefined, 2));
     }).fail(function(err) {
diff --git a/jsfiddles/room_memberships/demo.html b/jsfiddles/room_memberships/demo.html
index 4c1bf6b4bb..e6f39df5aa 100644
--- a/jsfiddles/room_memberships/demo.html
+++ b/jsfiddles/room_memberships/demo.html
@@ -1,5 +1,5 @@
 <div>
-    <p>This room membership demo requires a home server to be running on http://localhost:8080</p>
+    <p>This room membership demo requires a home server to be running on http://localhost:8008</p>
 </div>
 <form class="loginForm">
     <input type="text" id="userLogin" placeholder="Username"></input>
diff --git a/jsfiddles/room_memberships/demo.js b/jsfiddles/room_memberships/demo.js
index 7e499049ab..64ba767138 100644
--- a/jsfiddles/room_memberships/demo.js
+++ b/jsfiddles/room_memberships/demo.js
@@ -18,7 +18,7 @@ $('.login').live('click', function() {
     var user = $("#userLogin").val();
     var password = $("#passwordLogin").val();
     $.ajax({
-        url: "http://localhost:8080/_matrix/client/api/v1/login",
+        url: "http://localhost:8008/_matrix/client/api/v1/login",
         type: "POST",
         contentType: "application/json; charset=utf-8",
         data: JSON.stringify({ user: user, password: password, type: "m.login.password" }),
@@ -39,7 +39,7 @@ var getCurrentRoomList = function() {
     // solution but that is out of scope of this fiddle.
     $("#rooms").find("tr:gt(0)").remove();
     
-    var url = "http://localhost:8080/_matrix/client/api/v1/initialSync?access_token=" + accountInfo.access_token + "&limit=1";
+    var url = "http://localhost:8008/_matrix/client/api/v1/initialSync?access_token=" + accountInfo.access_token + "&limit=1";
     $.getJSON(url, function(data) {
         var rooms = data.rooms;
         for (var i=0; i<rooms.length; ++i) {
@@ -53,7 +53,7 @@ var getCurrentRoomList = function() {
 $('.createRoom').live('click', function() {
     var data = {};
     $.ajax({
-        url: "http://localhost:8080/_matrix/client/api/v1/createRoom?access_token="+accountInfo.access_token,
+        url: "http://localhost:8008/_matrix/client/api/v1/createRoom?access_token="+accountInfo.access_token,
         type: "POST",
         contentType: "application/json; charset=utf-8",
         data: JSON.stringify(data),
@@ -87,7 +87,7 @@ $('.changeMembership').live('click', function() {
         return;
     }
     
-    var url = "http://localhost:8080/_matrix/client/api/v1/rooms/$roomid/$membership?access_token=$token";
+    var url = "http://localhost:8008/_matrix/client/api/v1/rooms/$roomid/$membership?access_token=$token";
     url = url.replace("$token", accountInfo.access_token);
     url = url.replace("$roomid", encodeURIComponent(roomId));
     url = url.replace("$membership", membership);
@@ -117,7 +117,7 @@ $('.changeMembership').live('click', function() {
 
 $('.joinAlias').live('click', function() {
     var roomAlias = $("#roomAlias").val();
-    var url = "http://localhost:8080/_matrix/client/api/v1/join/$roomalias?access_token=$token";
+    var url = "http://localhost:8008/_matrix/client/api/v1/join/$roomalias?access_token=$token";
     url = url.replace("$token", accountInfo.access_token);
     url = url.replace("$roomalias", encodeURIComponent(roomAlias));
     $.ajax({