## Deliver MFA codes over WhatsApp You can let users receive their MFA code over WhatsApp as an alternative to SMS, by combining your own login UI with the `SEND_SMS` [prehook](/ciam/guides/integrations/prehooks). Frontegg generates the code and passes it to your `SEND_SMS` prehook, together with the channel the user selected. Your application lets the user choose WhatsApp, requests the code, and sends it through your WhatsApp provider. br Prerequisites - Frontegg's embedded login. - An account with a WhatsApp messaging provider, such as the WhatsApp Business Platform or Twilio. - Phone number (SMS) MFA enabled for your environment, with users enrolled with a phone number. ### How it works 1. The user signs in with their email and password. The response tells you that MFA is required. 2. Your MFA screen lets the user choose how to receive the code, for example **Text message** or **WhatsApp**. 3. Your app asks Frontegg for a code and passes `channel: 'whatsapp'`. 4. Frontegg generates the code and calls your `SEND_SMS` prehook with `channel: 'whatsapp'` and the code. 5. Your prehook sends the code through your WhatsApp provider and tells Frontegg not to send an SMS. 6. The user enters the code, and your app verifies it with Frontegg. The user's phone number is the same one used for SMS MFA. No separate enrollment is needed for WhatsApp. br ### Step 1: Deliver the code from the SEND_SMS prehook Create a prehook for the **Send SMS** (`SEND_SMS`) event, as described in the [prehooks guide](/ciam/guides/integrations/prehooks). The event payload carries a `channel` field set to `'sms'` or `'whatsapp'`, next to the user's phone number, the code, and the SMS template `type` (`0` for MFA): ```json { "eventKey": "SEND_SMS", "data": { "channel": "whatsapp", "type": 0, "user": { "id": "8f2c1a7e-3d4b-4c9a-9e61-0b7d2f5a1c3e", "email": "jane@example.com", "phoneNumber": "+15551234567" }, "body": "Your login code is: 123456. Do not share it with anyone.", "data": { "code": "123456" } } } ``` br When `channel` is `'whatsapp'`, send the code yourself and return `useFronteggSms: false`, so Frontegg does not send an SMS as well. For any other channel, return `useFronteggSms: true` and Frontegg sends the SMS as usual: ```javascript async function onEvent(eventData) { const { channel, user, data } = eventData.data; if (channel !== 'whatsapp') { return { verdict: 'allow', response: { useFronteggSms: true } }; } await sendWhatsAppMessage(user.phoneNumber, data.code); return { verdict: 'allow', response: { useFronteggSms: false } }; } exports.onEvent = onEvent; ``` br `sendWhatsAppMessage` stands for the call to your WhatsApp provider. `channel` is always present and defaults to `'sms'`, so a `SEND_SMS` prehook that ignores it keeps working exactly as before. br ### Step 2: Sign the user in Authenticate the user with their email and password. When MFA is required, the response includes an `mfaToken` and the user's enrolled phone devices: ```bash curl -X POST https://{your-domain}/identity/resources/auth/v1/user \ -H "Content-Type: application/json" \ -d '{"email": "jane@example.com", "password": "user-password"}' ``` br ```json { "mfaRequired": true, "mfaToken": "eyJhbGciOi...", "mfaDevices": { "phones": [{ "id": "c4b7e2d9-5a16-4f3e-8b20-7d9e1f6a3b58", "phoneNumber": "+1555****567" }] } } ``` br Keep the `mfaToken` and the phone device `id` for the next steps. br ### Step 3: Request the code over WhatsApp When the user chooses WhatsApp, request a code for the phone device and pass `channel: 'whatsapp'`. Omit `channel`, or pass `'sms'`, to deliver the code over SMS: ```bash curl -X POST https://{your-domain}/identity/resources/auth/v1/user/mfa/sms/{deviceId} \ -H "Content-Type: application/json" \ -d '{"mfaToken": "eyJhbGciOi...", "channel": "whatsapp"}' ``` br The response returns an `otcToken`. Each request generates a new code and a new `otcToken`, so when the user asks to resend, or switches between SMS and WhatsApp, call this endpoint again and keep the latest `otcToken`. The endpoint is limited to 10 requests per hour per IP address. br ### Step 4: Verify the code Send the code the user entered, along with the `mfaToken` and the latest `otcToken`: ```bash curl -X POST https://{your-domain}/identity/resources/auth/v1/user/mfa/sms/{deviceId}/verify \ -H "Content-Type: application/json" \ -d '{"mfaToken": "eyJhbGciOi...", "otcToken": "a1b2c3...", "code": "123456"}' ``` br A successful verification returns the user's tokens, the same as any other MFA login. The code length and expiration follow your existing SMS MFA settings. br ### When delivery fails Frontegg has no WhatsApp-specific error. How a failure is handled depends on what your prehook returns: | Situation | Result | | --- | --- | | The prehook returns `useFronteggSms: false` | Frontegg sends no SMS. The code reaches the user only through your WhatsApp message. | | The prehook returns `useFronteggSms: true` | Frontegg sends the code over SMS. | | The prehook fails, times out, or returns no usable response | Frontegg sends the code over SMS, whatever channel the user asked for. | | No `SEND_SMS` prehook is configured | Frontegg sends the code over SMS. | | The prehook returns a `block` verdict | The request fails with `410` and error `ER-01037`, for example `Failed to send sms to +1555****567`. The prehook's own error message is not returned. | | The prehook is set to [fail close](/ciam/guides/integrations/prehooks#fail-methodology) and the call fails | The request fails. | br Avoid duplicate messages If your prehook sends the WhatsApp message but then fails or times out, Frontegg also sends an SMS, and the user receives two codes. Return `useFronteggSms: false` as soon as the WhatsApp message is sent, and keep your provider call well within the 5-second prehook timeout.