diff --git a/package.json b/package.json
index ddd08d81..e3a46b42 100644
--- a/package.json
+++ b/package.json
@@ -16,7 +16,7 @@
   "dependencies": {
     "JSONStream": "^1.1.2",
     "blinking": "~0.0.2",
-    "bluebird": "^3.0.0",
+    "bluebird": "^3.5.0",
     "body-parser": "^1.12.0",
     "buffer-equal-constant-time": "^1.0.1",
     "docker-delta": "1.0.1",
@@ -37,7 +37,7 @@
     "pubnub": "^3.7.13",
     "request": "^2.51.0",
     "request-progress": "^2.0.1",
-    "resin-register-device": "^2.0.0",
+    "resin-register-device": "^3.0.0",
     "rimraf": "^2.5.4",
     "rwlock": "^5.0.0",
     "sqlite3": "^3.1.0",
diff --git a/src/app.coffee b/src/app.coffee
index 05da7010..847d813d 100644
--- a/src/app.coffee
+++ b/src/app.coffee
@@ -51,17 +51,17 @@ knex.init.then ->
 					)
 			)
 
+			updateIpAddr = ->
+				utils.gosuper.getAsync('/v1/ipaddr', { json: true })
+				.spread (response, body) ->
+					if response.statusCode == 200 && body.Data.IPAddresses?
+						device.updateState(
+							ip_address: body.Data.IPAddresses.join(' ')
+						)
+				.catch(_.noop)
+			console.log('Starting periodic check for IP addresses..')
+			setInterval(updateIpAddr, 30 * 1000) # Every 30s
+			updateIpAddr()
+
 		console.log('Starting Apps..')
 		application.initialize()
-
-		updateIpAddr = ->
-			utils.gosuper.getAsync('/v1/ipaddr', { json: true })
-			.spread (response, body) ->
-				if response.statusCode == 200 && body.Data.IPAddresses?
-					device.updateState(
-						ip_address: body.Data.IPAddresses.join(' ')
-					)
-			.catch(_.noop)
-		console.log('Starting periodic check for IP addresses..')
-		setInterval(updateIpAddr, 30 * 1000) # Every 30s
-		updateIpAddr()
diff --git a/src/bootstrap.coffee b/src/bootstrap.coffee
index e9a451a1..c14a8640 100644
--- a/src/bootstrap.coffee
+++ b/src/bootstrap.coffee
@@ -2,17 +2,18 @@ Promise = require 'bluebird'
 knex = require './db'
 utils = require './utils'
 deviceRegister = require 'resin-register-device'
-{ resinApi } = require './request'
+{ resinApi, request } = require './request'
 fs = Promise.promisifyAll(require('fs'))
 config = require './config'
 configPath = '/boot/config.json'
 appsPath  = '/boot/apps.json'
 _ = require 'lodash'
 deviceConfig = require './device-config'
+TypedError = require 'typed-error'
 userConfig = {}
 
-DuplicateUuidError = (err) ->
-	return err.message == '"uuid" must be unique.'
+DuplicateUuidError = message: '"uuid" must be unique.'
+exports.ExchangeKeyError = class ExchangeKeyError extends TypedError
 
 bootstrapper = {}
 
@@ -34,27 +35,71 @@ loadPreloadedApps = ->
 	.catch (err) ->
 		utils.mixpanelTrack('Loading preloaded apps failed', { error: err })
 
+exchangeKey = ->
+	resinApi.get
+		resource: 'device'
+		options:
+			filter:
+				uuid: userConfig.uuid
+		customOptions:
+			apikey: userConfig.apiKey
+	.catchReturn([])
+	.timeout(config.apiTimeout)
+	.then ([ device ]) ->
+		if not device?
+			throw new ExchangeKeyError("Couldn't fetch device with provisioning key")
+		# We found the device, we can try to generate a working device key for it
+		request.postAsync("#{config.apiEndpoint}/api-key/device/#{device.id}/device-key")
+		.spread (res, body) ->
+			if res.status != 200
+				throw new ExchangeKeyError("Couldn't generate device key with provisioning key")
+			userConfig.deviceApiKey = body
+		.return(device)
+
 bootstrap = ->
 	Promise.try ->
 		userConfig.deviceType ?= 'raspberry-pi'
 		if userConfig.registered_at?
 			return userConfig
-		deviceRegister.register(resinApi, userConfig)
+
+		deviceRegister.register(
+			userId: userConfig.userId
+			applicationId: userConfig.applicationId
+			uuid: userConfig.uuid
+			deviceType: userConfig.deviceType
+			deviceApiKey: userConfig.deviceApiKey
+			provisioningApiKey: userConfig.apiKey
+			apiEndpoint: config.apiEndpoint
+		)
 		.timeout(config.apiTimeout)
 		.catch DuplicateUuidError, ->
+			console.log('UUID already registered, checking if our device key is valid for it')
 			resinApi.get
 				resource: 'device'
 				options:
 					filter:
 						uuid: userConfig.uuid
 				customOptions:
-					apikey: userConfig.apiKey
+					apikey: userConfig.deviceApiKey
+			.catchReturn([])
 			.timeout(config.apiTimeout)
 			.then ([ device ]) ->
-				return device
-		.then (device) ->
+				if device?
+					console.log('Fetched device, all is good')
+					return device
+				# If we couldn't fetch with the device key then we can try to key exchange in case the provisioning key is an old 'user-api-key'
+				console.log("Couldn't fetch the device, trying to exchange for a valid key")
+				exchangeKey()
+				.tapCatch ExchangeKeyError, (err) ->
+					# If it fails we just have to reregister as a provisioning key doesn't have the ability to change existing devices
+					console.log('Exchanging key failed, having to reregister')
+					generateRegistration(true)
+				.then (device) ->
+		.then ({ id }) ->
 			userConfig.registered_at = Date.now()
-			userConfig.deviceId = device.id
+			userConfig.deviceId = id
+			# Delete the provisioning key now.
+			delete userConfig.apiKey
 			fs.writeFileAsync(configPath, JSON.stringify(userConfig))
 		.return(userConfig)
 	.then (userConfig) ->
@@ -63,7 +108,7 @@ bootstrap = ->
 		.then ->
 			knex('config').insert([
 				{ key: 'uuid', value: userConfig.uuid }
-				{ key: 'apiKey', value: userConfig.apiKey }
+				{ key: 'apiKey', value: userConfig.deviceApiKey }
 				{ key: 'username', value: userConfig.username }
 				{ key: 'userId', value: userConfig.userId }
 				{ key: 'version', value: utils.supervisorVersion }
@@ -75,19 +120,21 @@ readConfig = ->
 	fs.readFileAsync(configPath, 'utf8')
 	.then(JSON.parse)
 
-readConfigAndEnsureUUID = ->
+generateRegistration = (forceReregister = false) ->
 	Promise.try ->
-		return userConfig.uuid if userConfig.uuid?
-		deviceRegister.generateUUID()
-		.then (uuid) ->
-			userConfig.uuid = uuid
-			fs.writeFileAsync(configPath, JSON.stringify(userConfig))
-			.return(uuid)
+		if forceReregister
+			userConfig.uuid = deviceRegister.generateUniqueKey()
+			userConfig.deviceApiKey = deviceRegister.generateUniqueKey()
+		else
+			userConfig.uuid ?= deviceRegister.generateUniqueKey()
+			userConfig.deviceApiKey ?= deviceRegister.generateUniqueKey()
+		fs.writeFileAsync(configPath, JSON.stringify(userConfig))
+		.return(userConfig.uuid)
 	.catch (err) ->
 		console.log('Error generating and saving UUID: ', err)
 		Promise.delay(config.bootstrapRetryDelay)
 		.then ->
-			readConfigAndEnsureUUID()
+			generateRegistration()
 
 bootstrapOrRetry = ->
 	utils.mixpanelTrack('Device bootstrap')
@@ -101,6 +148,15 @@ bootstrapper.done = new Promise (resolve) ->
 	bootstrapper.doneBootstrapping = ->
 		bootstrapper.bootstrapped = true
 		resolve(userConfig)
+		# If we're still using an old api key we can try to exchange it for a valid device key
+		if userConfig.apiKey?
+			exchangeKey()
+			.then ->
+				delete userConfig.apiKey
+				knex('config').update(value: userConfig.deviceApiKey).where(key: 'apiKey')
+				.then ->
+					fs.writeFileAsync(configPath, JSON.stringify(userConfig))
+
 
 bootstrapper.bootstrapped = false
 bootstrapper.startBootstrapping = ->
@@ -115,13 +171,16 @@ bootstrapper.startBootstrapping = ->
 			bootstrapper.doneBootstrapping() if !bootstrapper.offlineMode
 			return uuid.value
 		console.log('New device detected. Bootstrapping..')
-		readConfigAndEnsureUUID()
+
+		generateRegistration()
 		.tap ->
 			loadPreloadedApps()
 		.tap (uuid) ->
 			if bootstrapper.offlineMode
 				return knex('config').insert({ key: 'uuid', value: uuid })
 			else
-				return bootstrapOrRetry()
+				bootstrapOrRetry()
+				# Don't wait on bootstrapping here, bootstrapper.done is for that.
+				return
 
 module.exports = bootstrapper
diff --git a/src/device.coffee b/src/device.coffee
index 16fc2d65..98ee1b00 100644
--- a/src/device.coffee
+++ b/src/device.coffee
@@ -21,10 +21,10 @@ exports.getID = memoizePromise ->
 	bootstrap.done
 	.then ->
 		Promise.all([
-			knex('config').select('value').where(key: 'apiKey')
-			knex('config').select('value').where(key: 'uuid')
+			utils.getConfig('apiKey')
+			utils.getConfig('uuid')
 		])
-	.spread ([{ value: apiKey }], [{ value: uuid }]) ->
+	.spread (apiKey, uuid) ->
 		resinApi.get(
 			resource: 'device'
 			options:
@@ -178,9 +178,9 @@ do ->
 		if _.size(stateDiff) is 0
 			return
 		applyPromise = Promise.join(
-			knex('config').select('value').where(key: 'apiKey')
+			utils.getConfig('apiKey')
 			device.getID()
-			([{ value: apiKey }], deviceID) ->
+			(apiKey, deviceID) ->
 				stateDiff = getStateDiff()
 				if _.size(stateDiff) is 0 || !apiKey?
 					return
diff --git a/src/proxyvisor.coffee b/src/proxyvisor.coffee
index 72b3fb7c..111fed5c 100644
--- a/src/proxyvisor.coffee
+++ b/src/proxyvisor.coffee
@@ -50,9 +50,9 @@ router.post '/v1/devices', (req, res) ->
 		utils.getConfig('apiKey')
 		utils.getConfig('userId')
 		device.getID()
-		deviceRegister.generateUUID()
 		randomHexString.generate()
-		(apiKey, userId, deviceId, uuid, logsChannel) ->
+		(apiKey, userId, deviceId, logsChannel) ->
+			uuid = deviceRegister.generateUniqueKey()
 			d =
 				user: userId
 				application: req.body.appId
diff --git a/tools/dind/vpn-init b/tools/dind/vpn-init
index 394bcd0c..5452807b 100755
--- a/tools/dind/vpn-init
+++ b/tools/dind/vpn-init
@@ -7,7 +7,7 @@ while true; do
 		echo "UUID missing from config file, VPN cannot connect"
 		sleep 2
 	else
-		read uuid api_key <<<$(jq -r '.uuid,.apiKey' $CONFIG_PATH)
+		read uuid api_key <<<$(jq -r '.uuid,.deviceApiKey // .apiKey' $CONFIG_PATH)
 		mkdir -p /var/volatile/
 		echo $uuid > /var/volatile/vpnfile
 		echo $api_key >> /var/volatile/vpnfile