--- Source: https://docs.microblink.com/platform/sdk/web/d2d Title: Device-to-device configuration Description: Configure device-to-device verification in Web SDK for cross-device workflows --- # Device-to-device configuration :::info[D2D] Read [more about D2D](/platform/d2d) to understand the functionality. This article handles SDK-related configuration. ::: ## Setup In a device-to-device setup, there are **two** web applications using the browser SDK. One web app starts a verification flow, and generates a QR code which links to the other web app. The second web app continues this verification flow. The **primary** device (the one starting the flow) is usually a computer or something with a comparatively poorer camera. The **secondary** device (the one continuing the flow) is usually a smartphone or something with a better camera. Both apps use the same web SDK, but have different configurations. ## Configuration The web SDK exposes an [`IdvFlowProps`](/platform-web-sdk/types#enabled2d) interface which is used to configure different devices: ```ts {5} export interface IdvFlowProps { // ... mode?: WorkingMode; apiConfig: ProxyConfig | TransactionConfig; enableD2D?: boolean; // ... } ``` Set the following flags: - On the web app run by **the primary device**, set `enableD2D` to `true`. - On the web app run by **the primary device**, set `mode` to `"classic"`. The `apiConfig` object now expects [`ProxyConfig`](/platform-web-sdk/types#proxyconfig) data. - On the web app run by **the secondary device**, set `mode` to `"d2d"`. The `apiConfig` object now expects [`TransactionConfig`](/platform-web-sdk/types#transactionconfig) data. For example: ```js title="Example configuration for primary app" { enableD2D: true, mode: "classic", apiConfig: { url: 'address of your proxy', workflowId: 'ID of your workflow', d2d: { runAddress: 'address of the secondary app' }, }, consentData: { userId: 'unique-user-id', note: "lorem ipsum", givenOn: new Date().toISOString(), isTrainingAllowed: true, isProcessingStoringAllowed: true, } } ``` And: ```js title="Example configuration for secondary app" { mode: "d2d", apiConfig: { apiUrl: 'Edge API URL from QR code', transactionId: 'transaction ID from QR code', ephemeralKey: 'ephemeral ID from QR code', d2d: { joinKey: 'join key from QR code' }, }, } ``` These two minimal configurations are enough to initialize the primary and secondary web apps. You can start verification flows using the `createIdvFlow()` function: ```js createIdvFlow({ // config object here }); ``` Or if you're using the React integration: ```js ``` (In this case you must follow JSX syntax; we'll use plain JavaScript for the remainder of this article.) ## D2D web app You can either self-host or use the default Microblink web app for the secondary device. We explain the tradeoffs in the [Run address](/platform/run-address) article. For the remainder of this article, we will assume that you are also hosting the secondary web app yourself. ## Sequence On the primary device (initiator), the SDK will: 1. Initialize the D2D flow, including sending [user consent](/platform/transaction-consent). 2. Generate a unique session identifier and QR code. 3. Display the QR code to the user. 4. Move to the `d2dIdle` state once connected. 5. Periodically poll session state from the Edge API to notify the user of possible changes (device connected, disconnected, etc.). 6. Complete the flow (end in `d2dFlowEnd` state) when verification is finished on the secondary device. The secondary device executes the standard verification workflow but with the transaction details established by the primary device. ## QR code ### Generation (primary device) The primary device generates a QR code containing the following fields: ```json title="Example D2D payload" { "source": "DeviceToDevice", "apiAddress": "https://api.us-east.platform.microblink.com/edge", "transactionId": "0269368e51db07f3db5c0345a9", "ephemeralKey": "ZG7.VR5M-[...]8=7T", "redirectUrl": null, "verificationLinkId": null, "deviceJoinKey": "FR93=[...]b5V02o6YCA_lnKsp", "deviceJoinKeyExpiresOn": "2025-12-08T08:43:01.8793126Z" } ``` This information is encoded in a base64-encoded string and has a 5-minute validity period. When the QR code is generated, it points to your [Run address](/platform/run-address), and appends this base64-encoded string as a query parameter. For example: ``` https://platform.microblink.com/run?sdk=eyJzb3VyY2UiOiJEZXZpY2VUb0RldmljZSIsImFwaUF... ``` ### Handling (secondary device) The secondary device (web app) must receive the QR code information from the URL, and pass it into its configuration object. This means that you need to create logic that parses the URL and its query parameters. If you've properly configured the secondary web app (see [Configuration](#configuration)), you should be able to pass the URL parameters into your identity verification function directly, and continue with the flow started on the primary device. For example, one way to parse the URL parameters would be: ```js title="Parsing URL parameters" const urlSearchParams = new URLSearchParams(window.location.search); const data = urlSearchParams.get("sdk"); const jsonString = atob(data); const result = JSON.parse(jsonString); ``` In this example we assume that all the parameters are base64-encoded data (a JSON object) passed to an `sdk` **query parameter** (`https://example.com?sdk=...`). ## Examples :::tip[Example D2D web app] We provide a full example implementation [here](https://github.com/MicroblinkPlatform/microblink-platform-browser-sdk/tree/main/example-vanilla-with-d2d). ::: Here are some **minimal** examples that work, even though they should be expanded in production. For example, some things should be wrapped in try/except blocks, and you should generally have checks for presence on certain fields, rather than assume that they are there. We'll keep the examples here minimal for readability, and to focus on the D2D mechanism itself. These examples also assume that you have the rest of the project set up: a bundler/builder like Vite, your dependencies installed, and an index.html file that imports this JavaScript. They also assume that you have your [proxy](/platform/proxy) set up, as well as everything else necessary to complete a full workflow. ### Primary app ```js createIdvFlow({ enableD2D: true, mode: "classic", apiConfig: { url: 'https://your.proxy.example/v1/transaction', workflowId: '68306f141147ea0629a896e8', d2d: { runAddress: 'https://your.2nd.web.app' }, }, consentData: { userId: 'unique ID, must be collected from end user', note: "", givenOn: new Date().toISOString(), isTrainingAllowed: true, isProcessingStoringAllowed: true, } }); ``` ### Secondary app ```js const urlSearchParams = new URLSearchParams(window.location.search); const data = urlSearchParams.get("sdk"); const jsonString = atob(data); const result = JSON.parse(jsonString); createIdvFlow({ mode: "d2d", apiConfig: { apiUrl: result.apiAddress, transactionId: result.transactionId, ephemeralKey: result.ephemeralKey, d2d: { joinKey: result.deviceJoinKey }, }, }); ``` ### Summary To summarize, in the examples above, the primary app will start the identity verification flow, collect the end-user's consent, and send this data to your proxy. The proxy will authenticate the request, and will contact the Microblink back end. It will then return the instructions on how to continue with the transaction to the primary web app. The primary web app will pack this up into a QR code (link). This link will be directed to your `runAddress` (in the example above it's `https://your.2nd.web.app`). The data on how to continue with the transaction will be base64-encoded and passed as a query parameter (`?sdk=`). When the user opens that link, they will open the secondary web app. The secondary web app is configured to read the URL parameters. It will search for an `sdk` query parameter, and extract information from it. It will then pass this information to its own `apiConfig` object, and resume the same transaction. The user will complete the steps that you've defined in your workflow. Upon completion, the user will be redirected back to the primary app, where they will see a message signalling their completion status (success or failure). Last updated on Apr 20, 2026