With your backend
Use this path when your backend keeps its own sessions. Your backend starts the sign-in and, once the person has sent the message, exchanges a one-time nonce for the verified phone number. Your app only opens WhatsApp and waits. There’s no browser and no OAuth redirect.
Before you start, create the WhatsApp connection and enable it for your
application, as in Set it up in the dashboard.
The application’s id is the APPLICATION_ID below.
1. Get an access token from your backend
Section titled “1. Get an access token from your backend”Use the credentials of a Machine to Machine application that has access to the AuthAction Management API. Keep them on your backend, never in the app.
curl --request POST \ --url https://<tenant>.<region>.authaction.com/oauth2/m2m/token \ --header 'content-type: application/json' \ --data '{ "client_id": "<M2M_CLIENT_ID>", "client_secret": "<M2M_CLIENT_SECRET>", "audience": "https://<tenant>.<region>.authaction.com", "grant_type": "client_credentials" }'2. Start a sign-in
Section titled “2. Start a sign-in”When the person taps Sign in with WhatsApp, your backend starts a sign-in and passes the answer to the app. The code in the message lasts 10 minutes and works once.
curl --request POST \ --url https://<tenant>.<region>.authaction.com/api/v1/whatsapp/<APPLICATION_ID>/transaction \ --header 'Authorization: Bearer <ACCESS_TOKEN>'{ "transactionId": "KQc0LuRcYsvlRNXnVS3keUwYJiuNhUoX", "message": "Sign in to <app>. Send this message as it is.\n\n2CK4…", "whatsappUrl": "whatsapp://send?phone=…&text=…", "webUrl": "https://wa.me/…?text=…", "expiresAt": "2026-10-30T10:10:00.000Z"}3. Open WhatsApp from the app and wait
Section titled “3. Open WhatsApp from the app and wait”Open whatsappUrl to go straight to WhatsApp. Where WhatsApp isn’t installed
(a computer, or a phone whose WhatsApp is on another phone), show webUrl as
a QR code. Whichever account sends the message is the one signed in.
Then poll the status every 2 seconds. It needs no token: the transaction id is the key and never appears in the chat.
curl https://<tenant>.<region>.authaction.com/api/v1/whatsapp-public/<APPLICATION_ID>/transaction/<TRANSACTION_ID>/status{ "status": "pending" }{ "status": "verified", "nonce": "…" }{ "status": "expired" }In React Native:
import { Linking } from "react-native";
// 1. Your backend starts the sign-in (step 2) and returns its answer.const { transactionId, whatsappUrl, webUrl } = await yourApi.startWhatsappSignIn();
// 2. Open WhatsApp with the message filled in, or show webUrl as a QR code.if (await Linking.canOpenURL(whatsappUrl)) await Linking.openURL(whatsappUrl);else showQr(webUrl);
// 3. Wait for the message.const statusUrl = `https://<tenant>.<region>.authaction.com/api/v1/whatsapp-public/<APPLICATION_ID>/transaction/${transactionId}/status`;let nonce: string | undefined;while (!nonce) { await new Promise((r) => setTimeout(r, 2000)); const s = await fetch(statusUrl).then((r) => r.json()); if (s.status === "verified") nonce = s.nonce; else if (s.status !== "pending") throw new Error("Sign-in expired");}
// 4. Your backend verifies the nonce (step 4) and signs the person in.const session = await yourApi.verifyWhatsappSignIn(nonce);The connection’s Quickstart tab in the dashboard has the same code for Swift and Kotlin.
4. Verify the nonce from your backend
Section titled “4. Verify the nonce from your backend”The app sends the nonce to your backend, which exchanges it once, within 10 minutes, for the verified person. Create your own session from the result.
curl --request POST \ --url https://<tenant>.<region>.authaction.com/api/v1/whatsapp/<APPLICATION_ID>/verify \ --header 'Authorization: Bearer <ACCESS_TOKEN>' \ --header 'content-type: application/json' \ --data '{ "nonce": "<NONCE>" }'{ "userId": "6a1b2c3d4e5f60718293a4b5", "phoneNumber": "+919812345678", "name": "Ravi", "verified": true}The same number gives the same userId every time, in every application of
the tenant. A blocked user is refused here.