diff --git a/example/index.html b/example/index.html
index 6c822dde4f7cacec015d2ebad3ced592181be7b3..3f50bea7a0de7c89f24147596eafbcbd6e074878 100644
--- a/example/index.html
+++ b/example/index.html
@@ -124,7 +124,7 @@ navigator.id.get(function(assertion) {
         $("#oAssertion").text(assertion);
         checkAssertion(assertion);
       };
-    });
+    }, {persistent: true});
   });
 
   $("#logout").click(function(event) {
diff --git a/resources/static/dialog/controllers/dialog_controller.js b/resources/static/dialog/controllers/dialog_controller.js
index 3386e7f0b4671ed661b1be3eba1a50907186c72f..c69e8a8e172ffbc1adc4105611b7eb4270fe57fc 100644
--- a/resources/static/dialog/controllers/dialog_controller.js
+++ b/resources/static/dialog/controllers/dialog_controller.js
@@ -92,10 +92,19 @@
       },
 
       getVerifiedEmail: function(origin_url, onsuccess, onerror) {
+        return this.get(origin_url, {}, onsuccess, onerror);
+      },
+    
+      get: function(origin_url, params, onsuccess, onerror) {
         var self=this;
         self.onsuccess = onsuccess;
         self.onerror = onerror;
-        self.allowPersistent = true; // XXX We need to get this info from somewhere.
+
+        if (typeof(params) == 'undefined') {
+          params = {};
+        }
+        
+        self.allowPersistent = (params.persistent == true);
 
         if('onLine' in navigator && !navigator.onLine) {
           self.doOffline();
diff --git a/resources/static/dialog/resources/channel.js b/resources/static/dialog/resources/channel.js
index 68caa5235e6c9e8edd8461fbd112c09f756ac2c5..455cd6dd0018ff29bec16877df218794be53fe3b 100644
--- a/resources/static/dialog/resources/channel.js
+++ b/resources/static/dialog/resources/channel.js
@@ -74,22 +74,24 @@
     // (has window.opener) as well as whether the relay function exists.
     // If these conditions are not met, then print an appropriate message.
 
-    function onsuccess(rv) {
-      onCompleteCallback(rv, null);
-    }
-
-    function onerror(error) {
-      onCompleteCallback(null, error);
-    }
-
+    var REGISTERED_METHODS = {
+      'get': function(origin, params, onsuccess, onerror) {
+        // check for old controller methods
+        // FIXME KILL THIS SOON
+        if (controller.get) {
+          return controller.get(origin, params, onsuccess, onerror);          
+        } else {
+          return controller.getVerifiedEmail(origin, onsuccess, onerror);
+        }
+      }
+    };
+    
     // The relay frame will give us the origin and a function to call when 
     // dialog processing is complete.
     var frameWindow = getRelayWindow();
+    
     if (frameWindow) {
-      frameWindow.BrowserID.Relay.registerClient(function(origin, onComplete) {
-        onCompleteCallback = onComplete;
-        controller.getVerifiedEmail(origin, onsuccess, onerror);
-      });
+      frameWindow.BrowserID.Relay.registerClient(REGISTERED_METHODS);
     }
     else {
       throw "relay frame not found";
diff --git a/resources/static/include.js b/resources/static/include.js
index 3fb15233716286dad61c72ae74603f78c9fe3872..c64c128c037eab4339ebc935164a0a62c39124aa 100644
--- a/resources/static/include.js
+++ b/resources/static/include.js
@@ -756,7 +756,8 @@
         // we now have the relay iframe and channel already
         // we just message it.
         relay_chan.call({
-          method: "getVerifiedEmail",
+          method: "get",
+          params: options,
           success: function(rv) {
             if (callback) {
               // return the string representation of the JWT, the client is responsible for unpacking it.
diff --git a/resources/static/relay/relay.js b/resources/static/relay/relay.js
index d919aa8ff2bd51110e6f76a3d82ec75ce9f52b57..28b6441e74171c55959af7f58a7636a3d9a9b4cd 100644
--- a/resources/static/relay/relay.js
+++ b/resources/static/relay/relay.js
@@ -46,15 +46,15 @@
   };
 
   BrowserID.Relay = (function() {
-    var transaction,
-        origin,
-        channel = Channel,
-        win = window,
-        registerCB;
+    var channel = Channel,
+        win = window;
 
+    // captured method calls
+    var METHODS = ["get"];
+    var registeredMethods, pendingCall;
 
     function init(options) {
-      origin = transaction = registerCB = undefined;
+      registeredMethods = pendingCall = undefined;
 
       if(options.window) {
         win = options.window;
@@ -65,6 +65,46 @@
       }
     }
 
+    // try to forward the transaction, otherwise
+    // stash it away for later. Assumes only one
+    // transaction at a time for now.
+    function forwardCall(methodCall) {
+      // forwarding pending call?
+      if (!methodCall) {
+        methodCall = pendingCall;
+        pendingCall = null;
+      }
+
+      // no call still?
+      if (!methodCall)
+        return;
+
+      // ready to go?
+      if (!registeredMethods) {
+        // no, stash it for later
+        pendingCall = methodCall;
+        return;
+      }
+
+      // ok, we can make the call
+      var method = methodCall.method;
+      var transaction = methodCall.transaction;
+      var origin = transaction.origin;
+      var params = methodCall.params;
+
+      // have the call?
+      if (registeredMethods[method]) {
+        registeredMethods[method](
+          origin, params,
+          function(result) {
+            transaction.complete(result);
+          }, function(error) {
+            transaction.error(error, getVerboseMessage(error));
+          }
+        );
+      }
+    }
+    
     function open() {
       var rpc = channel.build({
         window: win,
@@ -72,66 +112,34 @@
         scope: "mozid"
       });
 
-      rpc.bind("getVerifiedEmail", function(trans, s) {
-        trans.delayReturn(true);
-        origin = trans.origin;
-        transaction = trans;
-
-        // If the client has run early and already registered its registration 
-        // callback, call it now.
-        if (registerCB) {
-          registerCB(origin, completionCB);  
-        }
-      });
-    }
-
-    function registerClient(callback) {
-      // If the origin is ready, call the callback immediately.
-      if (origin) {
-        callback(origin, completionCB);
-      }
-      else {
-        registerCB = callback;
+      for (var i=0; i<METHODS.length; i++) {
+        var method = METHODS[i];
+        rpc.bind(method, function(trans, params) {
+          trans.delayReturn(true);
+          forwardCall({method: method, transaction: trans, params: params});
+        });
       }
     }
 
-    function errorOut(code) {
-      function getVerboseMessage(code) {
-        var msgs = {
-          "canceled": "user canceled selection",
-          "notImplemented": "the user tried to invoke behavior that's not yet implemented",
-          "serverError": "a technical problem was encountered while trying to communicate with BrowserID servers."
-        };
-        var msg = msgs[code];
-        if (!msg) {
-          alert("need verbose message for " + code);
-          msg = "unknown error";
-            }
-        return msg;
-      }
-      transaction.error(code, getVerboseMessage(code));
+    function registerClient(methods) {
+      registeredMethods = methods;
+      forwardCall();
     }
 
-    /**
-     * The client calls this to relay a message back to the RP whenever it is 
-     * complete.  This function is passed to the client when the client does 
-     * its registerClient.
-     */
-    function completionCB(status, error) {
-        if(error) {
-          errorOut(error);
-        }
-        else {
-          try {
-            transaction.complete(status);
-          } catch(e) {
-            // The relay function is called a second time after the 
-            // initial success, when the window is closing.
-          }
-        }
+    function getVerboseMessage(code) {
+      var msgs = {
+        "canceled": "user canceled selection",
+        "notImplemented": "the user tried to invoke behavior that's not yet implemented",
+        "serverError": "a technical problem was encountered while trying to communicate with BrowserID servers."
+      };
+      var msg = msgs[code];
+      if (!msg) {
+        alert("need verbose message for " + code);
+        msg = "unknown error";
+      }
+      return msg;
     }
-
-
+    
     return {
       /**
        * Initialize the relay. 
diff --git a/resources/static/test/qunit/relay/relay_unit_test.js b/resources/static/test/qunit/relay/relay_unit_test.js
index 566c5a90fc9dee8a9662ffe2c27d1b97e60338c6..58ea7e93ddfa4dd79e3fcd82004c5e2187afb163 100644
--- a/resources/static/test/qunit/relay/relay_unit_test.js
+++ b/resources/static/test/qunit/relay/relay_unit_test.js
@@ -56,7 +56,7 @@ steal.plugins("jquery").then("/lib/jschannel", "/relay/relay", function() {
     },
 
     // Mock in the receiving of the RPC call from the RP.
-    receiveGetVerifiedEmail: function() {
+    receiveGet: function() {
       // cb is the mock callback that is passed to Channel.bind
       channelMock.cb({
         origin: "Origin",
@@ -102,20 +102,20 @@ steal.plugins("jquery").then("/lib/jschannel", "/relay/relay", function() {
     /**
      * Check to make sure the correct message is bound
      */
-    equal(channelMock.bindMessage, "getVerifiedEmail", "bound to getVerifiedEmail");
+    equal(channelMock.bindMessage, "get", "bound to get");
   });
 
-  test("channel.getVerifiedEmail before registerDialog", function() {
+  test("channel.get before registerDialog", function() {
     relay.open();
 
-    channelMock.receiveGetVerifiedEmail();
+    channelMock.receiveGet();
 
-    relay.registerClient(function(origin, completeCB) {
+    relay.registerClient({'get': function(origin, params, completeCB) {
       equal(origin, "Origin", "Origin set correctly");
       equal(typeof completeCB, "function", "A completion callback is specified");
 
       start();
-    });
+    }});
 
     stop();
   });
@@ -123,14 +123,14 @@ steal.plugins("jquery").then("/lib/jschannel", "/relay/relay", function() {
   test("registerDialog before channel.getVerifiedEmail", function() {
     relay.open();
 
-    relay.registerClient(function(origin, completeCB) {
+    relay.registerClient({'get': function(origin, params, completeCB) {
       equal(origin, "Origin", "Origin set correctly");
       equal(typeof completeCB, "function", "A completion callback is specified");
 
       start();
-    });
+    }});
 
-    channelMock.receiveGetVerifiedEmail();
+    channelMock.receiveGet();
 
     stop();
   });
@@ -138,13 +138,13 @@ steal.plugins("jquery").then("/lib/jschannel", "/relay/relay", function() {
   test("calling the completeCB with assertion", function() {
     relay.open();
 
-    channelMock.receiveGetVerifiedEmail();
+    channelMock.receiveGet();
 
-    relay.registerClient(function(origin, completeCB) {
+    relay.registerClient({'get': function(origin, params, completeCB) {
       completeCB("assertion", null);
       equal(channelMock.status, "assertion", "channel gets the correct assertion");
       start();
-    });
+    }});
 
     stop();
   });
@@ -153,30 +153,30 @@ steal.plugins("jquery").then("/lib/jschannel", "/relay/relay", function() {
   test("calling the completeCB with null assertion", function() {
     relay.open();
 
-    channelMock.receiveGetVerifiedEmail();
+    channelMock.receiveGet();
 
-    relay.registerClient(function(origin, completeCB) {
+    relay.registerClient({'get': function(origin, params, completeCB) {
       completeCB(null, null);
       strictEqual(channelMock.status, null, "channel gets the null assertion");
       start();
-    });
+    }});
 
     stop();
   });
 
-  test("calling the completeCB with error", function() {
+  test("calling the onerror callback", function() {
     relay.open();
 
-    channelMock.receiveGetVerifiedEmail();
+    channelMock.receiveGet();
 
-    relay.registerClient(function(origin, completeCB) {
-      completeCB(null, "canceled");
+    relay.registerClient({'get': function(origin, params, onsuccess, onerror) {
+      onerror("canceled");
 
       equal(channelMock.errorCode, "canceled", "error callback called with error code");
       ok(channelMock.verboseError, "verbose error code set");
 
       start();
-    });
+    }});
 
     stop();
   });
diff --git a/resources/static/test/qunit/resources/channel_unit_test.js b/resources/static/test/qunit/resources/channel_unit_test.js
index 94c2df40addc07c35e08a4c5371f7bd23c6e7d95..58f63786ea068ba6a76b404ea0b2ed8a9622212e 100644
--- a/resources/static/test/qunit/resources/channel_unit_test.js
+++ b/resources/static/test/qunit/resources/channel_unit_test.js
@@ -54,10 +54,11 @@ steal.then("/dialog/resources/channel", function() {
         browserid_relay_1234: {
           BrowserID: {
             Relay: {
-              registerClient: function(callback) {
+              registerClient: function(methods) {
                 // mock of the registerClient function in the BrowserID.Channel.
-                callback("origin", function onComplete(success, error) {
+                methods["get"]("foo.com", {}, function onComplete(success) {
                   winMock.success = success;
+                }, function onerror(error) {
                   winMock.error = error;
                 });
               }
@@ -90,7 +91,6 @@ steal.then("/dialog/resources/channel", function() {
       navigator: navMock
     });
 
-
     channel.open({
       getVerifiedEmail: function(origin, onsuccess, onerror) {
         onsuccess("assertion");