Functions that allow an Eitri-App to authenticate a user with a WhatsApp Zero-Tap one-time password (OTP).

WhatsApp Zero-Tap delivers an OTP to the app without the user leaving it: after the app performs a handshake with the WhatsApp client, the WhatsApp client broadcasts the code straight to the app, where it is captured natively and returned via awaitCode.

Flow ownership: the Eitri-App drives the flow and talks to its own backend. The native layer only emits the handshake and captures the code — it makes no backend calls and does not send the authentication template. The expected sequence is:

  1. startHandshake — opens Meta's required 10-minute window.
  2. The Eitri-App asks its own backend to send the zero-tap authentication template (via the WhatsApp Cloud API) to the user's phone number.
  3. awaitCode — resolves with the code captured from WhatsApp.

Requirements: this feature must be enabled in the Eitri Shopping App native configuration by setting whatsAppZeroTap: { active: true } in the brand's app configuration file. When disabled, isAvailable returns false and the flow methods throw.

Platform availability: Android only. On iOS, WhatsApp Zero-Tap is not supported — the WhatsApp client falls back to a copy-code button (and, on supported iOS versions, a keyboard autofill suggestion), both handled entirely by the OS and the WhatsApp client. The whatsAppOtp namespace is still registered on iOS so the detection contract is uniform: isAvailable returns false and the flow methods throw. Always check isAvailable before starting the flow.

Methods

  • Suspends until the OTP is captured from the WhatsApp client, then resolves with the code.

    Call this after startHandshake (and after your backend has sent the template). It resolves with the code delivered by the WhatsApp client, or rejects if the WhatsApp client reports an error (e.g. a signature-hash mismatch or a stale handshake).

    No native timeout. This call waits indefinitely until a code arrives or the flow fails. The Eitri-App owns the timeout: race awaitCode() against your own deadline and call cancelAwaitCode to abandon a stuck wait.

    One wait at a time. Only a single awaitCode() may be active concurrently. A second, overlapping call rejects with awaitCode.alreadyInProgress rather than silently discarding the first wait.

    Platform availability: Android only. On iOS this method throws eitri.whatsAppOtp.notSupportedOnIOS.

    Example (with an Eitri-App-owned timeout):

    const modules = await Eitri.modules()
    const awaitCode = modules?.whatsAppOtp?.awaitCode
    const cancelAwaitCode = modules?.whatsAppOtp?.cancelAwaitCode
    if (!awaitCode || !cancelAwaitCode) return

    const timeout = new Promise<never>((_, reject) =>
    setTimeout(() => reject(new Error("otp.timeout")), 60_000)
    )

    try {
    const code = await Promise.race([awaitCode(), timeout])
    await myBackend.verifyOtp({ phone, code })
    } catch (error) {
    // On timeout (or any abandonment), cancel the native wait so it does not linger.
    await cancelAwaitCode()
    console.warn("WhatsApp OTP not received:", error)
    }

    Returns Promise<string>

    The captured one-time password.

    awaitCode.alreadyInProgress if another wait is already active; eitri.whatsAppOtp.notSupportedOnIOS on iOS; or a WhatsApp-reported error (e.g. signature-hash mismatch, stale handshake) when capture fails.

  • Cancels a pending awaitCode wait.

    Use this to abandon the flow — for example when your own timeout fires, the user navigates away, or the user chooses a different authentication method. The pending awaitCode() promise rejects with a cancellation. It is a no-op when there is no active wait.

    Platform availability: Android only. On iOS it is a no-op (there is no flow to cancel).

    Example:

    const modules = await Eitri.modules()
    const cancelAwaitCode = modules?.whatsAppOtp?.cancelAwaitCode
    if (!cancelAwaitCode) return

    // User tapped "use SMS instead"
    await cancelAwaitCode()

    Returns Promise<void>

  • Verifies whether WhatsApp Zero-Tap is available on the current device.

    Android: Returns true only when the feature is enabled for this brand (whatsAppZeroTap: { active: true }) and a WhatsApp client (consumer or business) is installed and advertises support for the OTP handshake.

    iOS: Always returns false — WhatsApp Zero-Tap is not supported on iOS.

    Always call this before startHandshake / awaitCode, and fall back to a conventional OTP delivery channel (e.g. SMS) when it returns false.

    Example:

    const modules = await Eitri.modules()
    const isAvailable = modules?.whatsAppOtp?.isAvailable
    if (!isAvailable) return

    const available = await isAvailable()
    if (!available) {
    // WhatsApp Zero-Tap not available — fall back to SMS OTP, etc.
    return
    }

    Returns Promise<boolean>

    A boolean indicating whether WhatsApp Zero-Tap can be used on this device.

  • Emits the handshake to the WhatsApp client and opens Meta's required 10-minute window.

    Meta only delivers the code if the app performed this handshake within the 10 minutes before the authentication template is sent. Call this first, then ask your backend to send the template, then call awaitCode.

    Platform availability: Android only. On iOS this method throws eitri.whatsAppOtp.notSupportedOnIOS. When the feature is disabled for the brand, it throws an error as well. Always check isAvailable first.

    Example:

    const modules = await Eitri.modules()
    const startHandshake = modules?.whatsAppOtp?.startHandshake
    const awaitCode = modules?.whatsAppOtp?.awaitCode
    if (!startHandshake || !awaitCode) return

    // 1. Open the 10-minute window
    const requestId = await startHandshake()

    // 2. Ask YOUR backend to send the zero-tap authentication template to the user.
    // The native layer never contacts a backend itself.
    await myBackend.sendWhatsAppOtpTemplate({ phone, requestId })

    // 3. Wait for the captured code
    const code = await awaitCode()
    await myBackend.verifyOtp({ phone, code })

    Returns Promise<string>

    The request_id (a UUID) correlated with the handshake. The same id is used by the WhatsApp Cloud API authentication template and to validate the broadcast that delivers the code.

    eitri.whatsAppOtp.notSupportedOnIOS on iOS, or an error when the feature is disabled.