diff --git a/IPython/frontend/html/notebook/static/js/kernel.js b/IPython/frontend/html/notebook/static/js/kernel.js
index 761402b..e446d5c 100644
--- a/IPython/frontend/html/notebook/static/js/kernel.js
+++ b/IPython/frontend/html/notebook/static/js/kernel.js
@@ -220,19 +220,19 @@ var IPython = (function (IPython) {
         //  'set_next_input': set_next_input_callback
         // }
         //
-        // The execute_reply_callback will be passed the content object of the execute_reply
+        // The execute_reply_callback will be passed the content and metadata objects of the execute_reply
         // message documented here:
         //
         // http://ipython.org/ipython-doc/dev/development/messaging.html#execute
         //
         // The output_callback will be passed msg_type ('stream','display_data','pyout','pyerr')
-        // of the output and the content and header objects of the PUB/SUB channel that contains the
+        // of the output and the content and metadata objects of the PUB/SUB channel that contains the
         // output:
         //
         // http://ipython.org/ipython-doc/dev/development/messaging.html#messages-on-the-pub-sub-socket
         //
         // The clear_output_callback will be passed a content object that contains
-        // stdout, stderr and other fields that are booleans, as well as the header object.
+        // stdout, stderr and other fields that are booleans, as well as the metadata object.
         //
         // The set_next_input_callback will be passed the text that should become the next
         // input cell.
@@ -313,12 +313,13 @@ var IPython = (function (IPython) {
         reply = $.parseJSON(e.data);
         var header = reply.header;
         var content = reply.content;
+        var metadata = reply.metadata;
         var msg_type = header.msg_type;
         var callbacks = this.get_callbacks_for_msg(reply.parent_header.msg_id);
         if (callbacks !== undefined) {
             var cb = callbacks[msg_type];
             if (cb !== undefined) {
-                cb(content);
+                cb(content, metadata);
             }
         };
 
@@ -347,10 +348,10 @@ var IPython = (function (IPython) {
 
 
     Kernel.prototype._handle_iopub_reply = function (e) {
-        reply = $.parseJSON(e.data);
+        var reply = $.parseJSON(e.data);
         var content = reply.content;
         var msg_type = reply.header.msg_type;
-        var header = reply.header;
+        var metadata = reply.metadata;
         var callbacks = this.get_callbacks_for_msg(reply.parent_header.msg_id);
         if (msg_type !== 'status' && callbacks === undefined) {
             // Message not from one of this notebook's cells and there are no
@@ -361,7 +362,7 @@ var IPython = (function (IPython) {
         if (output_types.indexOf(msg_type) >= 0) {
             var cb = callbacks['output'];
             if (cb !== undefined) {
-                cb(msg_type, content, header);
+                cb(msg_type, content, metadata);
             }
         } else if (msg_type === 'status') {
             if (content.execution_state === 'busy') {
@@ -375,7 +376,7 @@ var IPython = (function (IPython) {
         } else if (msg_type === 'clear_output') {
             var cb = callbacks['clear_output'];
             if (cb !== undefined) {
-                cb(content, header);
+                cb(content, metadata);
             }
         };
     };
diff --git a/IPython/zmq/session.py b/IPython/zmq/session.py
index 18cde61..6c16c63 100644
--- a/IPython/zmq/session.py
+++ b/IPython/zmq/session.py
@@ -292,8 +292,8 @@ class Session(Configurable):
     username = Unicode(os.environ.get('USER',u'username'), config=True,
         help="""Username for the Session. Default is your system username.""")
 
-    subheader = Dict({}, config=True,
-        help="""Subheader dictionary, which serves as the default subheader fields for each message.""")
+    metadata = Dict({}, config=True,
+        help="""Metadata dictionary, which serves as the default top-level metadata dict for each message.""")
 
     # message signature related traits:
     
@@ -405,7 +405,7 @@ class Session(Configurable):
     def msg_header(self, msg_type):
         return msg_header(self.msg_id, msg_type, self.username, self.session)
 
-    def msg(self, msg_type, content=None, parent=None, subheader=None, header=None):
+    def msg(self, msg_type, content=None, parent=None, subheader=None, header=None, metadata=None):
         """Return the nested message dict.
 
         This format is different from what is sent over the wire. The
@@ -415,13 +415,17 @@ class Session(Configurable):
         msg = {}
         header = self.msg_header(msg_type) if header is None else header
         msg['header'] = header
+        if subheader is not None:
+            msg['header'].update(subheader)
         msg['msg_id'] = header['msg_id']
         msg['msg_type'] = header['msg_type']
         msg['parent_header'] = {} if parent is None else extract_header(parent)
         msg['content'] = {} if content is None else content
-        msg['header'].update(self.subheader)
-        if subheader is not None:
-            msg['header'].update(subheader)
+        metadata_dict = self.metadata.copy()
+        if metadata is not None:
+            metadata_dict.update(metadata)
+        if metadata_dict:
+            msg['metadata'] = metadata_dict
         return msg
 
     def sign(self, msg_list):
@@ -496,7 +500,7 @@ class Session(Configurable):
         return to_send
 
     def send(self, stream, msg_or_type, content=None, parent=None, ident=None,
-             buffers=None, subheader=None, track=False, header=None):
+             buffers=None, subheader=None, track=False, header=None, metadata=None):
         """Build and send a message via stream or socket.
 
         The message format used by this function internally is as follows:
@@ -520,7 +524,7 @@ class Session(Configurable):
         content : dict or None
             The content of the message (ignored if msg_or_type is a message).
         header : dict or None
-            The header dict for the message (ignores if msg_to_type is a message).
+            The header dict for the message (ignored if msg_to_type is a message).
         parent : Message or dict or None
             The parent or parent header describing the parent of this message
             (ignored if msg_or_type is a message).
@@ -529,11 +533,14 @@ class Session(Configurable):
         subheader : dict or None
             Extra header keys for this message's header (ignored if msg_or_type
             is a message).
+        metadata : dict or None
+            The metadata describing the message
         buffers : list or None
             The already-serialized buffers to be appended to the message.
         track : bool
             Whether to track.  Only for use with Sockets, because ZMQStream
             objects cannot track messages.
+            
 
         Returns
         -------
@@ -557,7 +564,7 @@ class Session(Configurable):
             msg = msg_or_type
         else:
             msg = self.msg(msg_or_type, content=content, parent=parent,
-                           subheader=subheader, header=header)
+                           subheader=subheader, header=header, metadata=metadata)
 
         buffers = [] if buffers is None else buffers
         to_send = self.serialize(msg, ident)
diff --git a/docs/source/development/messaging.txt b/docs/source/development/messaging.txt
index ab45c64..822ad66 100644
--- a/docs/source/development/messaging.txt
+++ b/docs/source/development/messaging.txt
@@ -81,7 +81,7 @@ representation of all the data, we can communicate with such clients.
 General Message Format
 ======================
 
-A message is defined by the following three-dictionary structure::
+A message is defined by the following four-dictionary structure::
 
     {
       # The message header contains a pair of unique identifiers for the
@@ -105,6 +105,9 @@ A message is defined by the following three-dictionary structure::
       # The actual content of the message must be a dict, whose structure
       # depends on the message type.
       'content' : dict,
+      
+      # Any metadata associated with the message; this dictionary is optional.
+      'metadata' : dict,
     }
 
    
@@ -127,6 +130,7 @@ messages upon deserialization to the following form for convenience::
       'msg_type' : str,
       'parent_header' : dict,
       'content' : dict,
+      'metadata' : dict, # optional
     }
 
 All messages sent to or received by any IPython process should have this