Verify Mongolian phone numbers with one SMS
Your user sends an SMS from their own phone to 150073 and pays their operator 150₮. 2fa.mn checks it and tells your server the result with a webhook. It costs you nothing.
Two ways, one SMS
Connect your server
Widget
One button opens a modal: price, code, SMS button, QR. Your server gets the number with one call.
Webhooks & signatures
Receive results as signed webhooks and check they are genuine.
API keys
Test and live API keys, rotation, IP restriction.
Test mode
Try the whole flow without sending a real SMS.
Build your own UI
The required 150₮ notice before the SMS button, with ready-made code.
One request creates the code
curl -X POST https://api.2fa.mn/v1/sessions \
-H "Authorization: Bearer $TWOFA_KEY" \
-H "Content-Type: application/json" \
-d '{ "phone": "99112233", "metadata": { "user_id": "u_42" } }'const res = await fetch('https://api.2fa.mn/v1/sessions', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.TWOFA_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ phone: '99112233', metadata: { user_id: 'u_42' } }),
});
const session = await res.json(); // session.sms_uri, session.codebody := strings.NewReader(`{"phone": "99112233", "metadata": {"user_id": "u_42"}}`)
req, _ := http.NewRequest("POST", "https://api.2fa.mn/v1/sessions", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("TWOFA_KEY"))
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)<?php
$ch = curl_init('https://api.2fa.mn/v1/sessions');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('TWOFA_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(['phone' => '99112233', 'metadata' => ['user_id' => 'u_42']]),
]);
$session = json_decode(curl_exec($ch), true);import os, requests
res = requests.post(
"https://api.2fa.mn/v1/sessions",
headers={"Authorization": f"Bearer {os.environ['TWOFA_KEY']}"},
json={"phone": "99112233", "metadata": {"user_id": "u_42"}},
)
session = res.json() # session["sms_uri"], session["code"]{
"id": "ses_8f3k2m",
"mode": "test",
"code": "482913",
"short_code": "150073",
"sms_uri": "sms:150073?body=482913",
"status": "pending",
"expires_at": "2026-12-01T10:05:00Z"
}- 1. Your server → 2fa.mnRequest a code
- 2. 2fa.mn → Your serverCode + SMS link
- 3. Your server → UserPrice + "Send SMS" button
- 4. User → OperatorSMS → 150073
- 5. Operator → 2fa.mnSMS (from the user)
- 6. 2fa.mn → Your serverVerified (webhook)
How is it different from regular SMS codes?
| Regular SMS code | 2fa.mn | |
|---|---|---|
| How the user verifies | Types in the code they received | Sends an SMS from their own phone (a user-sent SMS, MO SMS) |
| Who pays for the SMS | Your service pays to send it | The user pays their operator 150₮ |
| Your SMS cost | One per verification | None |
| Fraud cost risk | Exposed to SMS pumping | Your system sends no paid SMS |
| Integration | Needs a send-SMS and check-code flow | An API and a webhook |
What is SMS pumping?
An attack in which someone uses your service to trigger large numbers of SMS codes and run up your costs. 2fa.mn never sends SMS from your system, so it reduces that cost risk. It doesn't fully protect you against other fraud or automated requests.
Who pays?
When the user sends the SMS to 150073, they pay their operator 150₮ (VAT included). You pay nothing to send SMS, and you can earn from every successful verification (Earnings and payouts).
Warning
Show the price clearly before the user taps the SMS button. Guide: the price UI
Data privacy
Phone numbers and message content are processed only to provide the verification service. The production environment runs on servers located in Mongolia. For details, see the privacy policy.
Was this page helpful?