Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/matrix-org/matrix-js-sdk/llms.txt

Use this file to discover all available pages before exploring further.

Interactive key verification lets two devices (or users) confirm each other’s cryptographic identity by comparing short authentication strings (SAS / emojis) or scanning a QR code. The flow is modelled by the VerificationRequest interface.

How to Start Verification

Obtain the CryptoApi instance first:
const crypto = client.getCrypto();
if (!crypto) throw new Error("Crypto not available");

Verify another user via DM

// requestVerificationDM(userId, roomId)
const request = await crypto.requestVerificationDM(
  "@bob:matrix.org",
  "!dmRoomId:matrix.org"
);

Verify our own devices (self-verification)

// Sends a to-device verification request to all of our own devices.
const request = await crypto.requestOwnUserVerification();

Verify a specific device directly

const request = await crypto.requestDeviceVerification(
  "@alice:matrix.org",
  "DEVICEID"
);

Handle incoming requests

Listen for CryptoEvent.VerificationRequestReceived on the MatrixClient:
import { CryptoEvent } from "matrix-js-sdk";

client.on(CryptoEvent.VerificationRequestReceived, (request) => {
  console.log("Incoming verification from:", request.otherUserId);

  // Check if we should auto-accept or prompt the user
  if (request.isSelfVerification) {
    handleSelfVerification(request);
  } else {
    promptUserToAccept(request);
  }
});

VerificationRequest Interface

A VerificationRequest represents one pending or in-progress verification flow and extends TypedEventEmitter. Its state is exposed through read-only getters.

Properties

phase
VerificationPhase
required
The current phase of the verification request. See VerificationPhase below.
transactionId
string | undefined
A unique ID for this verification flow. Not assigned until the first message is sent, so may be undefined in early phases.
roomId
string | undefined
For in-room (DM) verifications, the ID of the room used. undefined for to-device verifications.
otherUserId
string
required
The user ID of the other party in this verification.
otherDeviceId
string | undefined
For to-device verifications, the ID of the other device. undefined for in-room verifications.
isSelfVerification
boolean
required
true if the other party is one of the local user’s own devices.
initiatedByMe
boolean
required
true if the local device initiated this verification request.
pending
boolean
required
true if the request is still in progress (phases Requested, Ready, or Started).
accepting
boolean
required
true if we have started sending an m.key.verification.ready but have not yet received the remote echo.
declining
boolean
required
true if we have started sending an m.key.verification.cancel but have not yet received the remote echo.
timeout
number | null
required
Milliseconds remaining before the request times out automatically. null means no timeout applies.
methods
string[]
required
The verification methods supported by both parties. Only populated after the request reaches the Ready or Started phase.
chosenMethod
string | null
required
The method selected in the m.key.verification.start event. null before the Started phase.
verifier
Verifier | undefined
The active Verifier object once the Started phase is reached.
cancellationCode
string | null
required
If the request was cancelled, the cancellation code (e.g. m.user). Otherwise null.
cancellingUserId
string | undefined
The user ID that cancelled the request. Only defined in the Cancelled phase.

Methods

accept()

accept(): Promise<void>
Accepts an incoming verification request by sending an m.key.verification.ready event. Transitions the phase from Requested to Ready.

cancel(params?)

cancel(params?: { reason?: string; code?: string }): Promise<void>
Cancels the request, sending an m.key.verification.cancel to the other party. The params argument is accepted for compatibility but is ignored by the Rust crypto backend.

startVerification(method)

startVerification(method: string): Promise<Verifier>
Sends an m.key.verification.start event for the given method (e.g. "m.sas.v1"). Returns a Verifier to drive the actual verification.

scanQRCode(qrCodeData)

scanQRCode(qrCodeData: Uint8ClampedArray): Promise<Verifier>
For QR-code verification: validates the scanned QR data and sends an m.key.verification.start with method: m.reciprocate.v1. Returns a Verifier; call .verify() on it to wait for the other party.

generateQRCode()

generateQRCode(): Promise<Uint8ClampedArray | undefined>
Generates the raw QR code data to display to the other party. Only returns data once the request is in Ready phase and the other party indicated support for scanning.

otherPartySupportsMethod(method)

otherPartySupportsMethod(method: string): boolean
Returns true if the other party advertised support for the given method. Useful for deciding which UI elements to show (e.g. whether to display a QR code).

VerificationPhase Enum

The state machine of a VerificationRequest:
ValueNumberDescription
VerificationPhase.Unsent1No event has been exchanged yet.
VerificationPhase.Requested2An m.key.verification.request event was sent or received.
VerificationPhase.Ready3An m.key.verification.ready event was sent or received; request is accepted.
VerificationPhase.Started4An m.key.verification.start event was sent or received; method is chosen.
VerificationPhase.Cancelled5An m.key.verification.cancel event was sent or received.
VerificationPhase.Done6Verification is complete (m.key.verification.done exchanged).

Verifier Interface

A Verifier drives the actual cryptographic verification after the method is chosen.

Methods

verify()

verify(): Promise<void>
Starts and awaits the verification. Resolves when verification completes successfully; rejects if it is cancelled or times out.

cancel(e)

cancel(e: Error): void
Cancels the in-progress verification and emits VerifierEvent.Cancel.

getShowSasCallbacks()

getShowSasCallbacks(): ShowSasCallbacks | null
Returns the ShowSasCallbacks when an SAS comparison is ready to be shown to the user. Returns null at all other times.

getReciprocateQrCodeCallbacks()

getReciprocateQrCodeCallbacks(): ShowQrCodeCallbacks | null
For QR code reciprocation: returns callbacks to confirm or cancel after the other party has scanned our QR code. Returns null at all other times.

VerifierEvent Enum

ValueStringDescription
VerifierEvent.Cancel"cancel"Verification was cancelled. Payload: Error | MatrixEvent.
VerifierEvent.ShowSas"show_sas"SAS data is ready for display. Payload: ShowSasCallbacks.
VerifierEvent.ShowReciprocateQr"show_reciprocate_qr"User should confirm the other side scanned the QR code. Payload: ShowQrCodeCallbacks.

VerificationRequestEvent Enum

ValueStringDescription
VerificationRequestEvent.Change"change"The state of the VerificationRequest changed. No payload.

Complete Verification Flow Example

The example below shows a complete SAS (emoji) verification from the initiator’s side:
import {
  CryptoEvent,
  VerificationPhase,
  VerificationRequestEvent,
  VerifierEvent,
} from "matrix-js-sdk";

async function verifySas(
  client: MatrixClient,
  targetUserId: string,
  dmRoomId: string
) {
  const crypto = client.getCrypto();
  if (!crypto) throw new Error("Crypto unavailable");

  // 1. Send the verification request
  const request = await crypto.requestVerificationDM(targetUserId, dmRoomId);
  console.log("Verification requested. Phase:", request.phase);

  // 2. Wait for the other side to accept (transition to Ready)
  await new Promise<void>((resolve) => {
    request.on(VerificationRequestEvent.Change, () => {
      if (request.phase === VerificationPhase.Ready) resolve();
      if (request.phase === VerificationPhase.Cancelled) {
        throw new Error("Request cancelled by " + request.cancellingUserId);
      }
    });
  });

  // 3. Start SAS verification
  const verifier = await request.startVerification("m.sas.v1");

  // 4. Wait for SAS data to be ready
  const sasCallbacks = await new Promise<ShowSasCallbacks>((resolve) => {
    verifier.on(VerifierEvent.ShowSas, resolve);
    verifier.verify().catch(console.error); // start the exchange
  });

  // 5. Display the emojis to the user
  const emojis = sasCallbacks.sas.emoji ?? [];
  console.log("Compare these emojis with the other device:");
  for (const [emoji, name] of emojis) {
    console.log(`  ${emoji} ${name}`);
  }

  // 6. User confirms or mismatches
  const userConfirmed = await promptUser("Do the emojis match?");
  if (userConfirmed) {
    await sasCallbacks.confirm();
  } else {
    sasCallbacks.mismatch();
    return;
  }

  // 7. Await completion
  await new Promise<void>((resolve) => {
    request.on(VerificationRequestEvent.Change, () => {
      if (request.phase === VerificationPhase.Done) resolve();
    });
  });

  console.log("Verification complete!");
}

// --- Receiving side ---

client.on(CryptoEvent.VerificationRequestReceived, async (request) => {
  console.log("Incoming verification from:", request.otherUserId);

  // Accept the request
  await request.accept();

  // Wait for the initiator to choose a method
  await new Promise<void>((resolve) => {
    request.on(VerificationRequestEvent.Change, () => {
      if (request.phase >= VerificationPhase.Started) resolve();
    });
  });

  const verifier = request.verifier!;

  verifier.on(VerifierEvent.ShowSas, async (sasCallbacks) => {
    const emojis = sasCallbacks.sas.emoji ?? [];
    console.log("Emojis:", emojis.map(([e]) => e).join(" "));

    const ok = await promptUser("Do the emojis match?");
    if (ok) {
      await sasCallbacks.confirm();
    } else {
      sasCallbacks.mismatch();
    }
  });

  await verifier.verify();
  console.log("Verification complete!");
});