Approve a login on the phone the user already trusts.
When someone signs in on the web or a new device, SignOps sends a push to the user's trusted phone. They match a number and confirm with Face ID or a fingerprint, and your server gets the answer.
- 100 approvals a day free
- Rs 0.30 an approval after
- Number matching on every request
We sent a sign-in request to Ananya's Pixel 8. Tap this number on it.
Four steps, about four seconds.
- 01
Enrol once
After your own login, the SDK makes a key pair on the phone. The private key stays in the Keystore or Secure Enclave.
- 02
Ask
Someone signs in on the web. Your server creates a request and shows a number, like 47, on the login page.
- 03
Match and confirm
The phone shows who is signing in, from where. The user picks the same number and confirms with Face ID or a fingerprint.
- 04
Signed answer
The phone signs the request. SignOps checks the signature and your server gets approved or denied.
The push rings the bell. The signature opens the door.
A push only wakes the app. The approval itself is a signature from a key that was made on that phone and cannot be copied off it. A stolen push token, or a copy of the push, approves nothing.
- Each request is a fresh challenge, signed once and then spent.
- The biometric unlocks the key on the phone; SignOps never sees a face or a fingerprint.
- Denials reach your server at once, so you can lock the account or alert the user.
apr_51mz0c · 47approve.approvedAsk, wait, sign the user in.
Create a request with a line of context, show the number on your login page, and wait for the answer or take it by webhook. The phone side is one call to enrol and a ready-made screen.
- Logins on a new device, step-up before a sensitive action, approving a payment.
- The context line is yours: "Withdraw Rs 25,000 to HDFC ****4821".
- A ready-made approve screen in your colours, or headless functions for your own.
// Someone is signing in on the web.
// Ask the user's trusted phone.
const challenge = await signops.approve.create({
user_id: "usr_8f21c4",
context: "Sign in from Chrome on Windows",
});
// Show challenge.match ("47") on the login page.
const result = await signops.approve.wait(challenge.id, {
timeout: 120,
});
if (result.status === "approved") startSession(result.user_id);// Once, after your own login.
// Keys are made in the Keystore.
SignOps.approve.enroll(userId = "usr_8f21c4")
// The push opens the approve screen.
SignOps.approve.showPending(activity)import { Approve } from "@signops/react-native";
await Approve.enroll({ userId: "usr_8f21c4" });
// A ready-made screen in your colours,
// or your own with Approve.pending().
<Approve.Screen brandColor="#12a463" />A fallback for every phone.
Some Android phones hold pushes while an app sleeps. When the user opens your app, the SDK fetches any waiting request. If no trusted phone answers in time, your server sends an email OTP instead.
- 1PasskeyNo passkey on this laptop yetSkipped
- 2ApprovePush to the trusted Pixel 8, number 47Approved in 4 s
- 3Email OTPUsed only when no trusted phone answersNot needed
- 4Push"New login on Chrome in Pune" to the other devicesSent
Who approved, on which phone, how fast.
Each request keeps its device, its number, its biometric and its answer. Your security team sees denials the moment they happen.
- 09:41:02approve.createdusr_8f21c4 · Chrome on WindowsSent
- 09:41:04approve.approvedPixel 8 · matched 47 · Face IDApproved
- 09:41:05push.sentNew login on Chrome · 2 devicesSent
- 09:44:19device.removedRedmi Note 12 · token expiredCleaned
Rs 0.30 against about Rs 4.40 on Twilio.
What one lakh approvals a month cost, before GST. An SMS OTP is cheaper per message; it is also the one a caller can talk out of your user.
SignOps: Starter at Rs 499, plus 1,00,000 approvals less the 1,000 it includes, at Rs 0.30.
Details and limits
- SDKs
- Android (Kotlin), iOS (Swift), React Native and Flutter.
- Keys
- A P-256 key pair per device, in the Android Keystore or the iOS Secure Enclave. The private key never leaves the phone.
- Trusted devices per user
- Up to 5.
- Request expiry
- 2 minutes.
- Number matching
- Always on. The phone shows three numbers and the user picks the one on the login screen.
- Confirmation
- Face ID, Touch ID, a fingerprint or the device PIN, every time.
- What the phone shows
- The device, the city from the IP address, the time and a line of your own, like an amount for a payment.
- Getting the answer
- Wait on the request from your server, or receive a signed webhook.
- Push not delivered
- The app picks up pending requests when it opens. Your server can fall back to an email OTP.
- Records
- Every request and answer for 90 days, 1 year on Pro.
- Price
- Free plan: 100 approvals a day. Starter and Pro: 1,000 a month included, then Rs 0.30 each. An approval counts when the user approves or denies; requests that expire are free.
Can Approve sign up a new user?
Why number matching on every request?
Without it, an attacker with a password can send request after request until a tired user taps yes. Picking the number shown on the login screen means only the person in front of that screen can approve.
Why pay more than an SMS OTP?
An SMS OTP costs Rs 0.13 to 0.15; an approval costs Rs 0.30. What the difference buys: nothing to read out to a caller, nothing a SIM swap can catch, no DLT templates to register, and a signature from a key that never leaves the phone. Expired requests are free.
Does it work for payments?
Yes. Send the amount and the payee as the context line, and the phone shows them on the approve screen. In-app notification approval is one of the alternatives to SMS OTP that the RBI named in its 2025 directions on authentication for digital payment transactions. Whether your flow meets those directions is for your compliance team to confirm.
What if the user loses the phone?
Remove the device from your server or the dashboard, and let the user back in with an email OTP or a passkey. They enrol the new phone after that login.
Guides for this product
All guidesReplace your next SMS OTP with an approval.
100 approvals a day on the free plan. On Starter, 1,000 a month included, then Rs 0.30 each. Requests that expire cost nothing.