diff --git a/resources/static/common/js/models/interaction_data.js b/resources/static/common/js/models/interaction_data.js
index c1df7522cdaea83ba6545fd4c8b3eec39d7ded75..d40da5139bb7ea9923e14662c365911297b6f6a7 100644
--- a/resources/static/common/js/models/interaction_data.js
+++ b/resources/static/common/js/models/interaction_data.js
@@ -22,7 +22,8 @@ BrowserID.Models.InteractionData = (function() {
         'number_sites_remembered',
         'orphaned',
         'new_account',
-        'email_type'
+        'email_type',
+        'rp_api'
       ];
 
 
diff --git a/resources/static/dialog/js/misc/internal_api.js b/resources/static/dialog/js/misc/internal_api.js
index 5a5e644c0dbd2cd6c1038c7ede4c8f937d25cf1f..ffc1e91ee90664b2f2bb5f66ef59384f385afb0b 100644
--- a/resources/static/dialog/js/misc/internal_api.js
+++ b/resources/static/dialog/js/misc/internal_api.js
@@ -89,6 +89,7 @@
       // options block directly to the dialog.
       var controller = moduleManager.getRunningModule("dialog");
       if(controller) {
+        options.rp_api = "internal";
         controller.get(origin, options, complete, complete);
       }
       else {
diff --git a/resources/static/dialog/js/modules/dialog.js b/resources/static/dialog/js/modules/dialog.js
index 6566944dd2739f4fe43e9d56ad1aa8765189e436..00d6e8f8b6f27f888a3b8555a779f1e4ae94792a 100644
--- a/resources/static/dialog/js/modules/dialog.js
+++ b/resources/static/dialog/js/modules/dialog.js
@@ -110,6 +110,20 @@ BrowserID.Modules.Dialog = (function() {
     throw "must be an absolute path: (" + path + ")";
   }
 
+  function validateRPAPI(rpAPI) {
+    var VALID_RP_API_VALUES = [
+      "watch_without_onready",
+      "watch_with_onready",
+      "get",
+      "getVerifiedEmail",
+      "internal"
+    ];
+
+    if (_.indexOf(VALID_RP_API_VALUES, rpAPI) === -1) {
+      throw "invalid value for rp_api: " + rpAPI;
+    }
+  }
+
   var Dialog = bid.Modules.PageModule.extend({
     start: function(options) {
       var self=this;
@@ -170,6 +184,13 @@ BrowserID.Modules.Dialog = (function() {
 
       // verify params
       try {
+        var rpAPI = paramsFromRP.rp_api;
+        if (rpAPI) {
+          // throws if an invalid rp_api value
+          validateRPAPI(rpAPI);
+          self.publish("kpi_data", { rp_api: rpAPI });
+        }
+
         if (paramsFromRP.requiredEmail) {
           helpers.log("requiredEmail has been deprecated");
         }
diff --git a/resources/static/include_js/include.js b/resources/static/include_js/include.js
index 3b775cca607c2274b5016b0c8d5b8ac0cd07c421..4fd51d0e0e0a670c949bc0c8055fe42f3a6f30ed 100644
--- a/resources/static/include_js/include.js
+++ b/resources/static/include_js/include.js
@@ -1125,6 +1125,17 @@
       _open_hidden_iframe();
     }
 
+    var api_called;
+    function getRPAPI() {
+      var rp_api = api_called;
+      if (rp_api === "request") {
+        if (observers.ready) rp_api = "watch_with_onready";
+        else rp_api = "watch_without_onready";
+      }
+
+      return rp_api;
+    }
+
     function internalRequest(options) {
       checkDeprecated(options, "requiredEmail");
       checkRenamed(options, "tosURL", "termsOfService");
@@ -1138,6 +1149,11 @@
         warn("privacyPolicy ignored unless termsOfService also defined");
       }
 
+      options.rp_api = getRPAPI();
+      // reset the api_called in case the site implementor changes which api
+      // method called the next time around.
+      api_called = null;
+
       // focus an existing window
       if (w) {
         try {
@@ -1216,6 +1232,7 @@
           throw new Error("all navigator.id calls must be made on the navigator.id object");
         options = options || {};
         checkCompat(false);
+        api_called = "request";
         // returnTo is used for post-email-verification redirect
         if (!options.returnTo) options.returnTo = document.location.pathname;
         return internalRequest(options);
@@ -1251,7 +1268,8 @@
         opts.tosURL = passedOptions.tosURL || undefined;
         opts.siteName = passedOptions.siteName || undefined;
         opts.siteLogo = passedOptions.siteLogo || undefined;
-
+        // api_called could have been set to getVerifiedEmail already
+        api_called = api_called || "get";
         if (checkDeprecated(passedOptions, "silent")) {
           // Silent has been deprecated, do nothing.  Placing the check here
           // prevents the callback from being called twice, once with null and
@@ -1283,6 +1301,7 @@
       getVerifiedEmail: function(callback) {
         warn("navigator.id.getVerifiedEmail has been deprecated");
         checkCompat(true);
+        api_called = "getVerifiedEmail";
         navigator.id.get(callback);
       },
       // required for forwards compatibility with native implementations
diff --git a/resources/static/test/cases/common/js/models/interaction_data.js b/resources/static/test/cases/common/js/models/interaction_data.js
index deab52638ea8db94cd15649f2c3410dd09523e84..c0d79f4f4a44fdcdfb0282f0d5e77207ecafd251 100644
--- a/resources/static/test/cases/common/js/models/interaction_data.js
+++ b/resources/static/test/cases/common/js/models/interaction_data.js
@@ -100,7 +100,8 @@
           number_sites_remembered: 3,
           orphaned: false,
           new_account: true,
-          email_type: "assertion"
+          email_type: "assertion",
+          rp_api: "watch_without_onready"
         });
         model.stageCurrent();
 
@@ -123,7 +124,8 @@
             number_sites_remembered: 3,
             orphaned: false,
             new_account: true,
-            email_type: "assertion"
+            email_type: "assertion",
+            rp_api: "watch_without_onready"
           });
 
           testHelpers.testUndefined(mostRecentSessionData.local_timestamp, "non-whitelisted valued stripped");
diff --git a/resources/static/test/cases/dialog/js/modules/dialog.js b/resources/static/test/cases/dialog/js/modules/dialog.js
index 5bb1d19f4636ed9c45f2213713bed1b97efd0a3b..052f05da04812b546db6305620948d3f5df232f7 100644
--- a/resources/static/test/cases/dialog/js/modules/dialog.js
+++ b/resources/static/test/cases/dialog/js/modules/dialog.js
@@ -68,6 +68,35 @@
     controller.start(options);
   }
 
+  function testMessageNotExpected(msg) {
+    mediator.subscribe(msg, function(msg, info) {
+      ok(false, "unexpected message: " + msg);
+    });
+  }
+
+  function testExpectGetFailure(options, expectedErrorMessage) {
+    _.extend(options, {
+      ready: function() {
+        testMessageNotExpected("kpi_data");
+        testMessageNotExpected("start");
+
+        var retval = controller.get(HTTPS_TEST_DOMAIN, options);
+
+        if (expectedErrorMessage) {
+          equal(retval, expectedErrorMessage, "expected error: " + expectedErrorMessage);
+        }
+        else {
+          ok(retval, "error message returned");
+        }
+
+        testErrorVisible();
+        start();
+      }
+    });
+    createController(options);
+  }
+
+
   module("dialog/js/modules/dialog", {
     setup: function() {
       winMock = new WinMock();
@@ -620,5 +649,26 @@
       }
     });
   });
+
+  asyncTest("get with valid rp_api - allowed", function() {
+    createController({
+      ready: function() {
+        mediator.subscribe("kpi_data", function(msg, info) {
+          equal(info.rp_api, "get");
+          start();
+        });
+
+        controller.get(HTTPS_TEST_DOMAIN, {
+          rp_api: "get"
+        });
+      }
+    });
+  });
+
+  asyncTest("get with invalid rp_api - not allowed", function() {
+    testExpectGetFailure({
+      rp_api: "invalid_value"
+    }, "invalid value for rp_api: invalid_value");
+  });
 }());