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.
The Dynamic client emits events for authentication state changes, wallet activity, MFA, device registration, checkout transactions, and project settings. Subscribe to any event with onEvent (or offEvent to remove a listener). React apps can use useEvent from @dynamic-labs-sdk/react-hooks for a hook with built-in cleanup.
Subscribing
import { onEvent } from '@dynamic-labs-sdk/client';
const unsubscribe = onEvent({
event: 'userChanged',
listener: ({ user }) => {
console.log('User changed:', user);
},
});
// Later — remove the listener
unsubscribe();
The listener is fully typed against the event name: hover over event in your editor to see the payload TypeScript expects.
Authentication state
State-change events fire whenever a top-level field on the DynamicClient changes. They are the primary way to react to login, logout, and JWT refreshes.
| Event | Payload | When it fires |
|---|
initStatusChanged | { initStatus: 'uninitialized' | 'in-progress' | 'finished' | 'failed' } | Each time client initialization advances. Use 'finished' to know the client is ready. |
userChanged | { user: User | null } | The authenticated user object is set, updated, or cleared (e.g. on logout). |
tokenChanged | { token: string | null } | The JWT changes — on sign in, refresh, or logout. |
sessionExpiresAtChanged | { sessionExpiresAt: Date | null } | Session expiry timestamp updates. |
projectSettingsChanged | { projectSettings: ProjectSettings | null } | Dashboard settings are fetched or refreshed. |
logout | { reason: LogoutReason } (optional) | logout is called. reason is one of 'user-intent', 'token-expired', 'user-deleted', 'device-revoked', 'all-devices-revoked', 'session-refresh-unauthorized', 'user-refresh-failed', 'keychain-migration-failed', 'keychain-key-missing'. |
import { onEvent } from '@dynamic-labs-sdk/client';
onEvent({
event: 'initStatusChanged',
listener: ({ initStatus }) => {
if (initStatus === 'finished') {
// Safe to call SDK methods that depend on initialization.
}
},
});
See Initializing the Dynamic Client for init-status semantics.
Wallet accounts and providers
| Event | Payload | When it fires |
|---|
walletAccountsChanged | { walletAccounts: WalletAccount[] } | A wallet is added, removed, verified, or its primary status changes. |
walletProviderRegistered | { walletProvider: WalletProvider } | A wallet provider is registered (typically when a chain extension loads). |
walletProviderUnregistered | { walletProviderKey: string } | A wallet provider is unregistered. |
walletProviderChanged | { walletProviderKey: string } | A registered provider’s metadata or capabilities change. |
For per-provider events (account changes, chain switches inside a single provider), see Wallet Provider Events.
import { onEvent } from '@dynamic-labs-sdk/client';
onEvent({
event: 'walletAccountsChanged',
listener: ({ walletAccounts }) => {
console.log(`Connected: ${walletAccounts.length} wallet(s)`);
},
});
MFA
| Event | Payload | When it fires |
|---|
mfaTokenChanged | { mfaToken: string | null } | The MFA token is issued, refreshed, or cleared. |
mfaCompletionSuccess | { deviceId?: string; mfaToken?: string } | A user successfully completes an MFA challenge. deviceId is undefined if a recovery code was used. |
mfaCompletionFailure | { deviceId?: string; error: unknown } | An MFA challenge fails verification. |
See the MFA overview for how these surface in the broader flow.
Device registration
| Event | Payload | When it fires |
|---|
deviceRegistrationCompleted | (no args) | Device registration completes successfully in the current tab. |
deviceRegistrationCompletedInAnotherTab | (no args) | Device registration completed in another tab — broadcast cross-tab so every tab can refresh state. |
See Device Registration.
Checkout
| Event | Payload | When it fires |
|---|
checkoutTransactionExecutionStateChanged | { transactionId: string; previousState: CheckoutExecutionState; newState: CheckoutExecutionState; timestamp: string } | A checkout transaction’s execution state advances (queued, executing, executed, etc.). |
checkoutTransactionSettlementStateChanged | { transactionId: string; previousState: CheckoutSettlementState; newState: CheckoutSettlementState; timestamp: string } | A checkout transaction’s settlement state advances. |
These let you build a transaction status UI without polling. See the Checkout flow guide.
React patterns
Use useEvent from @dynamic-labs-sdk/react-hooks — it subscribes on mount and cleans up on unmount:
import { useEvent } from '@dynamic-labs-sdk/react-hooks';
function LogoutLogger() {
useEvent({
event: 'logout',
listener: ({ reason } = {}) => {
console.log('User logged out:', reason);
},
});
return null;
}
For state that should drive re-renders (user, wallet accounts, init status, session), prefer the dedicated hooks (useUser, useWalletAccounts, useInitStatus) — they subscribe to the relevant events internally.
One-time listeners
Use onceEvent for a listener that auto-removes after firing once — useful for waiting on initialization or a single MFA completion.
import { onceEvent } from '@dynamic-labs-sdk/client';
onceEvent({
event: 'initStatusChanged',
listener: ({ initStatus }) => {
if (initStatus === 'finished') {
// runs at most once
}
},
});
Type safety
The full event surface is declared globally as DynamicEvents, augmented by each module that emits events. onEvent, offEvent, and onceEvent are generic over keyof DynamicEvents, so the listener signature is inferred from the event name — there is no central enum to keep in sync.