diff --git a/resources/static/shared/modules/interaction_data.js b/resources/static/shared/modules/interaction_data.js
index 16550f949582907ef983c4394a04d02108d11bd4..b08ef54163ce99f2f8760790a89da287e71e44f6 100644
--- a/resources/static/shared/modules/interaction_data.js
+++ b/resources/static/shared/modules/interaction_data.js
@@ -27,38 +27,45 @@ BrowserID.Modules.InteractionData = (function() {
   var bid = BrowserID,
       storage = bid.Storage.interactionData,
       network = bid.Network,
+      complete = bid.Helpers.complete,
       dom = bid.DOM,
       sc;
 
   function onSessionContext(msg, result) {
-    var self=this,
-        currentData = self.currentData;
+    var self=this;
 
     // defend against onSessionContext being called multiple times
-    if (self.sample !== undefined) return;
+    if (self.sessionContextHandled) return;
+    self.sessionContextHandled = true;
+
+    // Publish any outstanding data.  Previous session data must be published
+    // independently of whether the current dialog session is allowed to sample
+    // data. This is because the original dialog session has already decided
+    // whether to collect data.
+    publishStored();
 
     // set the sample rate as defined by the server.  It's a value
     // between 0..1, integer or float, and it specifies the percentage
     // of the time that we should capture
     var sampleRate = result.data_sample_rate || 0;
 
-    // now that we've got sample rate, let's smash it into a boolean
-    // probalistically
-    self.sample = self.forceSample || (Math.random() <= sampleRate);
+    if(typeof self.samplingEnabled === "undefined") {
+      // now that we've got sample rate, let's smash it into a boolean
+      // probalistically
+      self.samplingEnabled = Math.random() <= sampleRate;
+    }
 
     // if we're not going to sample, kick out early.
-    if (!self.sample) {
-      self.currentData = undefined;
+    if (!self.samplingEnabled) {
       return;
     }
 
-    currentData.sample_rate = sampleRate;
-
-    // set current time
-    currentData.timestamp = result.server_time;
-
-    // language
-    currentData.lang = dom.getAttr('html', 'lang');
+    var currentData = {
+      event_stream: self.initialEventStream,
+      sample_rate: sampleRate,
+      timestamp: result.server_time,
+      lang: dom.getAttr('html', 'lang') || null,
+    };
 
     if (window.screen) {
       currentData.screen_size = {
@@ -67,19 +74,15 @@ BrowserID.Modules.InteractionData = (function() {
       };
     }
 
-    // XXX: implement me!
-    currentData.user_agent = {
-      os: null,
-      browser: null,
-      version: null
-    };
-
-    // cool.  now let's persist this data
+    // cool.  now let's persist the initial data.  This data will be published
+    // as soon as the first session_context completes for the next dialog
+    // session.  Use a push because old data *may not* have been correctly
+    // published to a down server or erroring web service.
     storage.push(currentData);
-    currentData = undefined;
 
-    // finally, let's try to publish any old data
-    setTimeout(publish, 10);
+    self.initialEventStream = null;
+
+    self.samplesBeingStored = true;
   }
 
   // At every load, after session_context returns, we'll try to publish
@@ -90,34 +93,34 @@ BrowserID.Modules.InteractionData = (function() {
   // critical to the functioning of the system (and some failure scenarios
   // simply won't resolve with retries - like corrupt data, or too much
   // data)
-  function publish() {
+  function publishStored(oncomplete) {
     var data = storage.get();
 
-    if (data.length === 0) return;
-
-    network.sendInteractionData(data, complete, function() {
-      storage.clear();
-    });
+    if (data && data.length !== 0) {
+      network.sendInteractionData(data, function() {
+        storage.clear();
+        complete(oncomplete, true);
+      });
+    }
+    else {
+      complete(oncomplete, false);
+    }
   }
 
 
   function addEvent(eventName) {
     var self=this;
 
-    if (self.sample === false) return;
+    if (self.samplingEnabled === false) return;
 
     var eventData = [ eventName, new Date() - self.startTime ];
-    if (self.currentData) {
-      self.currentData.event_stream.push(eventData);
-    } else {
-      // @lloyd, how does this bit work?  When can sampling be enabled but
-      // there not be currentData?  It looks like currentData is created as
-      // soon as this module is run, and cleared only when session context
-      // comes in.
-      var d = storage.current();
+    if (self.samplesBeingStored) {
+      var d = storage.current() || {};
       if (!d.event_stream) d.event_stream = [];
       d.event_stream.push(eventData);
       storage.setCurrent(d);
+    } else {
+      self.initialEventStream.push(eventData);
     }
   }
 
@@ -127,21 +130,21 @@ BrowserID.Modules.InteractionData = (function() {
 
       var self = this;
 
-      self.forceSample = options.forceSample;
-
       self.startTime = new Date();
 
-      // sample rate is specified from the server.  it's set at the
-      // first 'context_info' event, which corresponds to the first time
-      // we get 'session_context' from the server.  When sampleRate is
-      // not undefined, then the interaction data is initialized.
-      // sample will be true or false
-      self.sample = undefined;
+      // If samplingEnabled is not specified in the options, it will be decided
+      // on the first "context_info" event, which corresponds to the first time
+      // we get 'session_context' from the server.
+      //
+      // options.samplingEnabled is used for testing purposes.
+      self.samplingEnabled = options.samplingEnabled;
 
-      self.currentData = {
-        event_stream: [
-        ]
-      };
+      // The initialEventStream is used to store events until onSessionContext
+      // is called.  Once onSessionContext is called and it is known whether
+      // the user's data will be saved, initialEventStream will either be
+      // discarded or added to the data set that is saved to localStorage.
+      self.initialEventStream = [];
+      self.samplesBeingStored = false;
 
       // whenever session_context is hit, let's hear about it so we can
       // extract the information that's important to us (like, whether we
@@ -154,19 +157,16 @@ BrowserID.Modules.InteractionData = (function() {
 
     addEvent: addEvent,
 
-    isSampling: function() {
-      return this.sample;
-    },
-
-    getData: function() {
-      return this.currentData;
+    getCurrentStoredData: function() {
+      var und;
+      return this.samplesBeingStored ? storage.current() : und;
     },
 
-    getStream: function() {
-      return this.currentData && this.currentData.event_stream;
+    getEventStream: function() {
+      return this.samplesBeingStored ? storage.current().event_stream : this.initialEventStream;
     },
 
-    publish: publish
+    publishStored: publishStored
   });
 
   sc = Module.sc;
diff --git a/resources/static/shared/storage.js b/resources/static/shared/storage.js
index 9f0ef262417581ef9c821859930d628578a3c886..dd5c8ce0f17105f804b70af5a1a03c3d89e851a9 100644
--- a/resources/static/shared/storage.js
+++ b/resources/static/shared/storage.js
@@ -413,8 +413,7 @@ BrowserID.Storage = (function() {
 
   function getAllInteractionData() {
     try {
-      var id = JSON.parse(storage.interactionData);
-      return id.slice(1);
+      return JSON.parse(storage.interactionData);
     } catch(e) {
       //alert(e);
       return [];
@@ -423,11 +422,10 @@ BrowserID.Storage = (function() {
 
   function clearInteractionData() {
     try {
-      var id = JSON.parse(storage.interactionData);
-      storage.interactionData = JSON.stringify(id.slice(0,1));
+      storage.interactionData = JSON.stringify([]);
     } catch(e) {
-      //alert(e);
-      delete storage.interactionData;
+  //    alert(e);
+    //  delete storage.interactionData;
     }
   }
 
diff --git a/resources/static/test/cases/shared/modules/interaction_data.js b/resources/static/test/cases/shared/modules/interaction_data.js
index 66e47319a1dc725559bbbe1640b4a02b3dfc0e7b..818ca815363b33e2395e38b88e007aa24fcd0a8d 100644
--- a/resources/static/test/cases/shared/modules/interaction_data.js
+++ b/resources/static/test/cases/shared/modules/interaction_data.js
@@ -9,6 +9,7 @@
   var bid = BrowserID,
       testHelpers = bid.TestHelpers,
       network = bid.Network,
+      storage = bid.Storage,
       controller;
 
   module("shared/modules/interaction_data", {
@@ -21,42 +22,99 @@
   });
 
   function createController(config) {
-    config = _.extend({ forceSample: true }, config);
+    config = _.extend({ samplingEnabled: true }, config);
     controller = BrowserID.Modules.InteractionData.create();
     controller.start(config);
   }
 
-  asyncTest("forceSample - data collection starts on context_info", function() {
+  function indexOfEvent(eventStream, eventName) {
+    for(var event, i = 0; event = eventStream[i]; ++i) {
+      if(event[0] === eventName) return i;
+    }
+
+    return -1;
+  }
+
+  asyncTest("samplingEnabled - ensure data collection working as expected", function() {
     createController();
 
+    controller.addEvent("before_session_context");
+
+    var events = controller.getEventStream();
+    ok(indexOfEvent(events, "before_session_context") > -1, "before_session_context correctly saved to event stream");
+    ok(indexOfEvent(events, "after_session_context") === -1, "after_session_context not yet added to current event stream");
+
+    // with context initializes the current stored data.
     network.withContext(function() {
-      equal(controller.isSampling(), true, "sampling has started!");
-      var data = controller.getData();
+      var data = controller.getCurrentStoredData();
+
+      // Make sure expected items are in the current stored data.
+      testHelpers.testKeysInObject(data, ["event_stream", "sample_rate", "timestamp", "lang"]);
+
+      controller.addEvent("after_session_context");
+
+      var events = controller.getEventStream();
+
+      // Make sure both the before_session_context and after_session_context
+      // are both on the event stream.
+      ok(indexOfEvent(events, "before_session_context") > -1, "before_session_context correctly saved to current event stream");
+      ok(indexOfEvent(events, "after_session_context") > -1, "after_session_context correctly saved to current event stream");
+
+
+      // Ensure that the event name as well as relative time are saved for an
+      // event.
+      var index = indexOfEvent(events, "after_session_context");
+      var event = events[index];
 
-      testHelpers.testObjectHasOwnProperties(data, ["sample_rate", "timestamp", "lang", "user_agent"]);
+      ok(index > -1, "after_session_context correctly saved to current event stream");
+      equal(event[0], "after_session_context", "name stored");
+      equal(typeof event[1], "number", "time stored");
 
       start();
     });
+
   });
 
-  asyncTest("addEvent to stream, getEventStream", function() {
+  asyncTest("publish data", function() {
     createController();
 
-    network.withContext(function() {
-      controller.addEvent("something_special");
+    // force saved data to be cleared.
+    storage.interactionData.clear();
+    controller.publishStored(function(status) {
+      equal(status, false, "no data to publish");
 
-      var stream = controller.getStream(),
-          lastItem = stream[stream.length - 1];
+      // session context is required start saving events to localStorage.
+      network.withContext(function() {
 
-      equal(lastItem[0], "something_special", "name stored");
-      equal(typeof lastItem[1], "number", "time stored");
+        // Add an event which should allow us to publish
+        controller.addEvent("something_special");
+        controller.publishStored(function(status) {
+          equal(status, true, "data correctly published");
 
-      start();
+          start();
+        });
+      });
     });
   });
 
-  asyncTest("publish - publish any outstanding data", function() {
+  asyncTest("samplingEnabled set to false - no data collection occurs", function() {
+    createController({ samplingEnabled: false });
+
+    // the initial with_context will send off any stored data, there should be
+    // no stored data.
+    network.withContext(function() {
+      controller.addEvent("after_session_context");
+      var events = controller.getEventStream();
+
+      var index = indexOfEvent(events, "after_session_context");
+      equal(index, -1, "events not being stored");
+
+      equal(typeof controller.getCurrentStoredData(), "undefined", "no stored data");
+
+      start();
+    });
 
   });
 
+
 }());
diff --git a/resources/static/test/cases/shared/network.js b/resources/static/test/cases/shared/network.js
index 7d50cfaff4865df05562ee6e20236ec4079b8cc4..1fc02f083dcd60aa6bec431d9312f8faed97066e 100644
--- a/resources/static/test/cases/shared/network.js
+++ b/resources/static/test/cases/shared/network.js
@@ -614,4 +614,18 @@
     network.prolongSession(testHelpers.unexpectedSuccess, testHelpers.expectedXHRFailure);
   });
 
+  asyncTest("sendInteractionData success - call success", function() {
+    var data = {};
+    network.sendInteractionData(data, function(status) {
+      equal(status, true, "complete with correct status");
+      start();
+    }, testHelpers.unexpectedXHRFailure);
+  });
+
+  asyncTest("sendInteractionData with XHR failure - call failure", function() {
+    var data = {};
+    transport.useResult("ajaxError");
+    network.sendInteractionData(data, testHelpers.unexpectedSuccess, testHelpers.expectedXHRFailure);
+  });
+
 }());
diff --git a/resources/static/test/cases/shared/storage.js b/resources/static/test/cases/shared/storage.js
index 5bab4b78ed7d0164c80d29957d29a63da4da76ba..c6f459e6264d103e8fc589b9479047dc287e0fb6 100644
--- a/resources/static/test/cases/shared/storage.js
+++ b/resources/static/test/cases/shared/storage.js
@@ -189,15 +189,13 @@
           "after clearing, interaction data is zero length");
   });
 
-  test("get interaction data returns everything except current (in-progress) data", function() {
+  test("get interaction data returns all data", function() {
     storage.interactionData.push({ foo: "old2" });
     storage.interactionData.clear();
     storage.interactionData.push({ foo: "old1" });
-    storage.interactionData.push({ foo: "current" });
     var d = storage.interactionData.get();
-    equal(d.length, 2, "get() returns complete unpublished data blobs");
+    equal(d.length, 1, "get() returns complete unpublished data blobs");
     equal(d[0].foo, 'old1', "get() returns complete unpublished data blobs");
-    equal(d[1].foo, 'old2', "get() returns complete unpublished data blobs");
   });
 }());
 
diff --git a/resources/static/test/mocks/xhr.js b/resources/static/test/mocks/xhr.js
index 71ede1adaf652a5dda123fc61092e936d30b5b04..d9048e7bfd80a82e99b6b6f8525c8c0e1c2f7630 100644
--- a/resources/static/test/mocks/xhr.js
+++ b/resources/static/test/mocks/xhr.js
@@ -119,6 +119,8 @@ BrowserID.Mocks.xhr = (function() {
       "post /wsapi/prolong_session valid": { success: true },
       "post /wsapi/prolong_session unauthenticated": 400,
       "post /wsapi/prolong_session ajaxError": undefined,
+      "post /wsapi/interaction_data valid": { success: true },
+      "post /wsapi/interaction_data ajaxError": undefined
     },
 
     setContextInfo: function(field, value) {
diff --git a/resources/static/test/testHelpers/helpers.js b/resources/static/test/testHelpers/helpers.js
index 808f628baff4291c40d9e52b88ea9d568c33fa6c..910db36bd27f94bb2265896c3c2e103077c7a510 100644
--- a/resources/static/test/testHelpers/helpers.js
+++ b/resources/static/test/testHelpers/helpers.js
@@ -199,9 +199,9 @@ BrowserID.TestHelpers = (function() {
       return str;
     },
 
-    testObjectHasOwnProperties: function(objToTest, expected, msg) {
+    testKeysInObject: function(objToTest, expected, msg) {
       for(var i=0, key; key=expected[i]; ++i) {
-        ok(objToTest.hasOwnProperty(key), msg || ("object contains " + key));
+        ok(key in objToTest, msg || ("object contains " + key));
       }
     }