> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dynamic.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Device registration

> Approve a new device when it's required at sign-in, complete the approval redirect, and manage a user's registered devices.

## Prerequisites

Before this page: create and initialize a Dynamic client (see [Creating a Dynamic client](https://docs.dynamic.xyz/docs/javascript/reference/client/create-dynamic-client.md), [Initializing the Dynamic client](https://docs.dynamic.xyz/docs/javascript/reference/client/initialize-dynamic-client.md)), and have a signed-in user (see [Email & SMS OTP sign-in](https://docs.dynamic.xyz/docs/javascript/building-ui/email-sms-otp-sign-in.md)).

## What you'll build

When your project requires device registration, a user signing in on an unrecognized device must approve it before continuing. You'll build:

1. **Detection** — notice when the current device needs approval.
2. **Completion** — finish approval when the user returns from the approval link.
3. **Management** — list registered devices and let the user revoke them.

## Detecting the requirement

After sign-in, `isDeviceRegistrationRequired` tells you whether the current device still needs approval. You don't send the approval email — the backend emails the link automatically when a user signs in on an unrecognized device. `isDeviceRegistrationRequired` just reflects that the current device is still pending, so use it to render a "check your email" message.

> [tab] TypeScript
>
> ```typescript theme={"system"}
> import {
>   getDefaultClient,
>   isDeviceRegistrationRequired,
> } from '@dynamic-labs-sdk/client';
>
> // Returns the message to show, or an empty string when nothing is pending.
> function deviceApprovalMessage(): string {
>   const { user } = getDefaultClient();
>   const needsApproval = Boolean(user && isDeviceRegistrationRequired(user));
>   return needsApproval ? 'Check your email to approve this device.' : '';
> }
> ```

> [tab] React
>
> ```tsx theme={"system"}
> import { useIsDeviceRegistrationRequired } from '@dynamic-labs-sdk/react-hooks';
>
> function DeviceApprovalGate() {
>   const { data: required } = useIsDeviceRegistrationRequired();
>   if (!required) return null;
>   return <p>Check your email to approve this device.</p>;
> }
> ```

## Completing approval on redirect

The approval link brings the user back to your app with a one-time token in the URL. Detect it, extract the token, and complete registration.

> [tab] TypeScript
>
> ```typescript theme={"system"}
> import {
>   detectDeviceRegistrationRedirect,
>   getDeviceRegistrationTokenFromUrl,
>   completeDeviceRegistration,
>   clearDeviceRegistrationRedirectParams,
> } from '@dynamic-labs-sdk/client';
>
> async function handleDeviceRedirect() {
>   const url = window.location.href;
>   if (!detectDeviceRegistrationRedirect({ url })) return;
>
>   const deviceToken = getDeviceRegistrationTokenFromUrl({ url });
>
>   try {
>     await completeDeviceRegistration({ deviceToken });
>     // Remove the one-time token so a refresh doesn't re-trigger registration.
>     clearDeviceRegistrationRedirectParams();
>     // showApproved is your implementation.
>     showApproved();
>   } catch (error) {
>     // handleDeviceError is your implementation — see the error table below.
>     handleDeviceError(error);
>   }
> }
> ```

> [tab] React
>
> ```tsx theme={"system"}
> import { useEffect } from 'react';
> import {
>   detectDeviceRegistrationRedirect,
>   getDeviceRegistrationTokenFromUrl,
>   clearDeviceRegistrationRedirectParams,
> } from '@dynamic-labs-sdk/client';
> import { useCompleteDeviceRegistration } from '@dynamic-labs-sdk/react-hooks';
>
> function useDeviceRedirect() {
>   const { mutate: complete, isIdle } = useCompleteDeviceRegistration();
>
>   useEffect(() => {
>     const url = window.location.href;
>     if (!detectDeviceRegistrationRedirect({ url }) || !isIdle) return;
>
>     const deviceToken = getDeviceRegistrationTokenFromUrl({ url });
>     complete(
>       { deviceToken },
>       // Remove the one-time token so a refresh doesn't re-trigger registration.
>       { onSettled: () => clearDeviceRegistrationRedirectParams() },
>     );
>   }, [complete, isIdle]);
> }
> ```

## Managing registered devices

List the user's devices and let them revoke any — or all — from a settings screen.

> [!WARNING]
>
> Revoking the **current** device — or **all** devices — signs the user out immediately. `revokeAllRegisteredDevices` always logs out; `revokeRegisteredDevice` logs out when the device being revoked is the current one. The SDK fires the `logout` event with reason `device-revoked` or `all-devices-revoked`; listen for it and send the user to your login screen so they aren't left on a dead session.

> [tab] TypeScript
>
> ```typescript theme={"system"}
> import {
>   getRegisteredDevices,
>   revokeRegisteredDevice,
>   revokeAllRegisteredDevices,
>   onEvent,
> } from '@dynamic-labs-sdk/client';
>
> async function listDevices(list: HTMLUListElement) {
>   const devices = await getRegisteredDevices();
>   list.replaceChildren();
>   for (const device of devices) {
>     const item = document.createElement('li');
>     const label = device.isCurrentDevice ? ' (this device)' : '';
>     item.textContent = `${device.userAgent ?? 'Unknown device'}${label}`;
>     list.append(item);
>   }
> }
>
> async function revoke(deviceRegistrationId: string) {
>   await revokeRegisteredDevice({ deviceRegistrationId });
> }
>
> // setStatus is your implementation — e.g. a toast or banner.
> async function revokeAll(setStatus: (message: string) => void) {
>   setStatus('Signing you out of every device…');
>   await revokeAllRegisteredDevices();
>   setStatus('All devices signed out. Redirecting you to sign in…');
> }
>
> // Revoking the current device (or all devices) logs the user out.
> // Send them to the login page when it happens.
> onEvent({
>   event: 'logout',
>   listener: (metadata) => {
>     const revokedCurrentSession =
>       metadata?.reason === 'device-revoked' ||
>       metadata?.reason === 'all-devices-revoked';
>
>     if (revokedCurrentSession) {
>       window.location.href = '/login';
>     }
>   },
> });
> ```

> [tab] React
>
> ```tsx theme={"system"}
> import {
>   useGetRegisteredDevices,
>   useRevokeRegisteredDevice,
>   useOnEvent,
> } from '@dynamic-labs-sdk/react-hooks';
>
> function RegisteredDevices() {
>   const { data: devices } = useGetRegisteredDevices();
>   const { mutateAsync: revoke } = useRevokeRegisteredDevice();
>
>   // Revoking the current device (or all devices) logs the user out.
>   useOnEvent({
>     event: 'logout',
>     listener: (metadata) => {
>       const revokedCurrentSession =
>         metadata?.reason === 'device-revoked' ||
>         metadata?.reason === 'all-devices-revoked';
>
>       if (revokedCurrentSession) {
>         window.location.href = '/login';
>       }
>     },
>   });
>
>   return (
>     <ul>
>       {devices?.map((device) => (
>         <li key={device.id}>
>           {device.userAgent ?? 'Unknown device'}
>           {device.isCurrentDevice ? ' (this device)' : null}
>           <button
>             type="button"
>             onClick={() => revoke({ deviceRegistrationId: device.id })}
>           >
>             Revoke
>           </button>
>         </li>
>       ))}
>     </ul>
>   );
> }
> ```

## Handling errors

| Error | Cause | What to do |
| - | - | - |
| `InvalidDeviceRegistrationRedirectError` | The URL has no valid device token | Don't attempt completion — the link is malformed or expired. |
| `DeviceRegistrationIdentityMismatchError` | The approval was for a different user | Ask the user to sign in with the account that requested approval. |
| Approval never completes | The user hasn't opened the link | Keep the "check your email to approve this device" prompt visible. |

## See also

* [Email & SMS OTP sign-in](https://docs.dynamic.xyz/docs/javascript/building-ui/email-sms-otp-sign-in.md) — the sign-in that can trigger device approval
* [Post-login user setup](https://docs.dynamic.xyz/docs/javascript/building-ui/post-auth-user-setup.md) — detect device registration alongside other post-login steps
* [MFA enrollment & management](https://docs.dynamic.xyz/docs/javascript/building-ui/mfa-enrollment-management.md) — another layer of account security


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.