Баримт бичиг
SMS.link нь хэрэглэгчийн илгээсэн SMS-ээр утасны дугаарыг баталгаажуулна. Таны сервер нэг API дуудлагаар 6 оронтой код авч, хэрэглэгч уг кодыг 151586 дугаарт илгээхэд бид webhook болон шалгах API-аар үр дүнг мэдэгдэнэ.
Хурдан эхлэх
- 1Бүртгүүлж, хяналтын самбарт Сайт нэмнэ. Нэг хөгжүүлэгч олон сайттай байж болно.
- 2Сайтын API keys хэсгээс test key (
slk_test_…) үүсгэнэ. Бүртгэл батлагдсаны дараа live key (slk_live_…) үүсгэнэ. - 3Хэрэглэгч дугаараа оруулахад таны backend
POST /verificationsдуудажid,client_secret-ийг frontend рүү дамжуулна. - 4Frontend дээр JS widget-ээр "SMS илгээх" товч (утсанд) эсвэл QR код (компьютерт) харуулна.
- 5
API key
Бүх хүсэлтэд сайтын нууц API key-г Authorization header-ээр илгээнэ (эсвэл X-Api-Key header).
Authorization: Bearer slk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
| Угтвар | Горим | Тайлбар |
|---|---|---|
slk_live_ | Live | Бодит SMS-ээр баталгаажуулна. Бүртгэл батлагдсаны дараа үүсгэнэ. |
slk_test_ | Test | Бодит SMS ирэхгүй; simulate-ээр туршина. Бүртгүүлмэгц ашиглаж болно. |
Баталгаажуулалт үүсгэх
Хэрэглэгчийн утасны дугаарт зориулж 6 оронтой код үүсгэнэ. Ижил сайт дээр нэг дугаарт шинэ код үүсгэхэд өмнөх хүлээгдэж буй код автоматаар цуцлагдана.
Параметрүүд (JSON body)
| Талбар | Төрөл | Тайлбар |
|---|---|---|
phone заавал | string | 8 оронтой Монгол гар утасны дугаар. +976, 976 угтвар, зай, зураасыг хүлээн авч арилгана. |
ttl | integer | Кодын хүчинтэй хугацаа (секунд). 120–1800 хооронд хязгаарлагдана. Анхдагч нь сайтын тохиргоо (10 мин). |
metadata | object | Таны өөрийн JSON object (жишээ нь user_id), 2000 тэмдэгт хүртэл. API хариу болон webhook-д буцаж ирнэ. |
{
"phone": "99112233",
"ttl": 600,
"metadata": { "user_id": "u_123" }
}Хүсэлтийн жишээ
curl -X POST https://sms.link.mn/api/v1/verifications \ -H "Authorization: Bearer $SMSLINK_KEY" \ -H "Content-Type: application/json" \ -d '{"phone": "99112233"}'
const res = await fetch("https://sms.link.mn/api/v1/verifications", { method: "POST", headers: { Authorization: `Bearer ${process.env.SMSLINK_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ phone: "99112233" }), }); const verification = await res.json(); // verification.id болон verification.client_secret-ийг frontend widget руу дамжуулна
$ch = curl_init("https://sms.link.mn/api/v1/verifications"); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("SMSLINK_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode(["phone" => "99112233"]), ]); $verification = json_decode(curl_exec($ch), true);
import os, requests
res = requests.post(
"https://sms.link.mn/api/v1/verifications",
headers={"Authorization": f"Bearer {os.environ['SMSLINK_KEY']}"},
json={"phone": "99112233"},
)
verification = res.json()
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization =
new("Bearer", Environment.GetEnvironmentVariable("SMSLINK_KEY"));
var res = await http.PostAsJsonAsync("https://sms.link.mn/api/v1/verifications", new { phone = "99112233" });
var verification = await res.Content.ReadFromJsonAsync<JsonElement>();
Хариу
HTTP/1.1 201 Created
{
"id": "ver_7k2mq9xa4d1fz8c3rb6ph0tn",
"object": "verification",
"status": "pending",
"livemode": true,
"phone": "99112233",
"code": "482915",
"short_number": "151586",
"sms_uri": "sms:151586?&body=482915",
"sms_uri_ios": "sms:151586&body=482915",
"sms_uri_android": "sms:151586?body=482915",
"qr_data": "SMSTO:151586:482915",
"client_secret": "vcs_Q3d9XkP2mN7vB4cR8tY1wZ6hL0sJ5fGa",
"created_at": "2026-10-28T10:00:00Z",
"expires_at": "2026-10-28T10:10:00Z",
"verified_at": null,
"sender_phone": null,
"operator": null,
"metadata": { "user_id": "u_123" }
}| Талбар | Тайлбар |
|---|---|
id | Баталгаажуулалтын ID (ver_…). Төлөв шалгах, webhook-ийг тулгахад ашиглана. |
status | pending, verified, expired, cancelled, failed — дэлгэрэнгүй. |
livemode | Live key-ээр үүссэн бол true, test key бол false. |
code, short_number | Хэрэглэгч short_number руу илгээх 6 оронтой код. |
sms_uri, sms_uri_ios, sms_uri_android | SMS апп-ыг бөглөөд нээх холбоос — нэг товчоор SMS. |
qr_data | Компьютер дээр QR болгон харуулах текст (SMSTO: формат). |
client_secret | Зөвхөн үүсгэх хариунд нэг удаа ирнэ. Frontend widget-д энэ баталгаажуулалтын төлөвийг уншихад ашиглана. |
verified_at, sender_phone, operator | Баталгаажсаны дараа бөглөгдөнө: хэзээ, ямар дугаараас, аль оператороор (unitel, skytel, …). |
expires_at | Кодын хүчинтэй хугацаа дуусах цаг (UTC). |
Нэг товчоор SMS илгээх
sms: холбоос дарахад утасны SMS апп нээгдэж, хүлээн авагч (151586) болон текст (код) автоматаар бөглөгдөнө.
Хэрэглэгч зөвхөн Send дарна. iOS болон Android-ийн формат бага зэрэг ялгаатай тул API гурван хувилбарыг буцаана:
| Талбар | Формат | Хэзээ |
|---|---|---|
sms_uri | sms:151586?&body=482915 | iOS болон Android аль алинд ажиллах ерөнхий хувилбар |
sms_uri_ios | sms:151586&body=482915 | iPhone, iPad |
sms_uri_android | sms:151586?body=482915 | Android |
qr_data | SMSTO:151586:482915 | Компьютер: QR код уншуулахад утсанд SMS апп бөглөгдөнө |
<!-- Утсан дээр: SMS апп нээгдэж хүлээн авагч, текст бөглөгдөнө --> <a href="sms:151586?&body=482915" class="btn">SMS илгээх</a> <!-- Платформоор нь сонгох (sms_uri_ios / sms_uri_android) --> <script> const isIOS = /iPhone|iPad|iPod/.test(navigator.userAgent); link.href = isIOS ? verification.sms_uri_ios : verification.sms_uri_android; </script>
Компьютер дээр sms: холбоос ажиллахгүй тул QR код харуулна. Бэлэн widget эдгээрийг бүгдийг автоматаар шийднэ. Ямар ч тохиолдолд кодыг текстээр нь харуулж, гараар илгээх боломж үлдээгээрэй.
JS widget
Хамгийн хялбар арга: backend-ийн буцаасан id, client_secret-ийг widget-д өгнө. Widget нь утсан дээр "SMS илгээх" товч,
компьютер дээр QR код, кодыг том үсгээр, хугацааны тоолуурыг харуулж, төлөвийг 2 секунд тутам шалгана. Баталгаажмагц onVerified дуудагдана.
<div id="smslink"></div> <script src="https://sms.link.mn/js/smslink.js"></script> <script> SmsLink.mount("#smslink", { id: "ver_...", // API-ийн хариунаас clientSecret: "vcs_...", // API-ийн хариунаас onVerified: () => { // Зөвхөн UX дохио. Баталгаажсаныг backend-ээрээ (webhook эсвэл GET) заавал шалгана. location.assign("/signup/next"); }, onExpired: () => alert("Кодын хугацаа дууслаа. Дахин оролдоно уу."), }); </script>
Тохиргоо
| Сонголт | Тайлбар |
|---|---|
id заавал | Баталгаажуулалтын ID |
clientSecret заавал | Үүсгэх хариуны client_secret |
baseUrl | Анхдагчаар script-ийн домэйн (https://sms.link.mn) |
onVerified(v) | Баталгаажсан үед |
onExpired(v) | Кодын хугацаа дууссан үед |
onStatus(v) | Төлөв өөрчлөгдөх бүрт (pending, verified, expired, failed, cancelled) |
SmsLink.mount() нь { destroy() } буцаана. HTML attribute-аар автоматаар суулгаж ч болно:
<div data-smslink data-id="ver_..." data-client-secret="vcs_..."></div> <script src="https://sms.link.mn/js/smslink.js" defer></script>
Төлөв шалгах
curl https://sms.link.mn/api/v1/verifications/ver_7k2mq9xa4d1fz8c3rb6ph0tn \
-H "Authorization: Bearer $SMSLINK_KEY"{
"id": "ver_7k2mq9xa4d1fz8c3rb6ph0tn",
"object": "verification",
"status": "verified",
"livemode": true,
"phone": "99112233",
"code": "482915",
"short_number": "151586",
...
"verified_at": "2026-10-28T10:01:12Z",
"sender_phone": "99112233",
"operator": "unitel",
"metadata": { "user_id": "u_123" }
}Хүчинтэй хугацаа нь дууссан pending баталгаажуулалт expired гэж буцна. Өөр сайт эсвэл өөр горимын (live/test) ID-д 404 not_found буцна.
Жагсаалт
| Query | Тайлбар |
|---|---|
status | pending, verified, expired, cancelled, failed |
phone | Утасны дугаараар шүүх |
limit | 1–100, анхдагч 20 |
starting_after | Хуудаслалт: өмнөх хуудасны сүүлийн id. Шинээс хуучин руу эрэмбэлэгдэнэ. |
curl "https://sms.link.mn/api/v1/verifications?status=verified&limit=20" \ -H "Authorization: Bearer $SMSLINK_KEY"
{
"object": "list",
"data": [ { "id": "ver_...", "status": "verified", ... } ],
"has_more": true
}Цуцлах
Зөвхөн pending төлөвтэйг цуцална; бусад үед 409 not_pending буцна.
curl -X POST https://sms.link.mn/api/v1/verifications/ver_7k2mq9xa4d1fz8c3rb6ph0tn/cancel \
-H "Authorization: Bearer $SMSLINK_KEY"Webhook
Хяналтын самбарт сайтын Webhook хэсэгт HTTPS URL болон хүлээн авах event-үүдээ тохируулна. Баталгаажуулалтын төлөв өөрчлөгдөхөд бид таны URL руу
POST хүсэлт илгээнэ.
| Event | Хэзээ |
|---|---|
verification.verified | Зөв дугаараас зөв код ирж баталгаажсан (анхдагчаар идэвхтэй) |
verification.expired | Кодын хугацаа SMS ирэхээс өмнө дууссан |
verification.failed | Код буруу дугаараас 3 удаа ирсэн |
webhook.test | Хяналтын самбарын "Туршилт илгээх" товч |
Хүсэлт
POST /webhooks/smslink HTTP/1.1
Content-Type: application/json
User-Agent: SmsLink-Webhooks/1.0
SmsLink-Event: verification.verified
SmsLink-Delivery: evt_3h8k2m9q4x7z1c5v0b6n2p4r
SmsLink-Signature: t=1761645672,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
{
"id": "evt_3h8k2m9q4x7z1c5v0b6n2p4r",
"type": "verification.verified",
"created_at": "2026-10-28T10:01:12Z",
"livemode": true,
"data": {
"verification": {
"id": "ver_7k2mq9xa4d1fz8c3rb6ph0tn",
"object": "verification",
"status": "verified",
"phone": "99112233",
"short_number": "151586",
"verified_at": "2026-10-28T10:01:12Z",
"sender_phone": "99112233",
"operator": "unitel",
"metadata": { "user_id": "u_123" },
...
}
}
}| Header | Тайлбар |
|---|---|
SmsLink-Event | Event-ийн төрөл |
SmsLink-Delivery | Event-ийн давтагдашгүй ID (evt_…), body-ийн id-тай ижил. Давтан ирсэн event-ийг ялгахад ашиглана. |
SmsLink-Signature | t=<unix timestamp>,v1=<hex> — доорх гарын үсэг |
Гарын үсэг шалгах
v1 = hex(HMAC_SHA256(webhook_secret, t + "." + raw_body)). Webhook secret (whsec_…)-ийг хяналтын самбарын Webhook хэсгээс авна.
Body-г JSON болгон задлахаас өмнө түүхий (raw) хэлбэрээр нь шалгах, мөн t 5 минутаас хуучин бол татгалзахыг зөвлөж байна.
import crypto from "node:crypto"; import express from "express"; function verifySignature(rawBody, header, secret) { const parts = Object.fromEntries((header || "").split(",").map((p) => p.split("="))); const expected = crypto .createHmac("sha256", secret) .update(`${parts.t}.${rawBody}`) .digest("hex"); const given = parts.v1 || ""; const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300; return fresh && given.length === expected.length && crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected)); } const app = express(); app.post("/webhooks/smslink", express.raw({ type: "application/json" }), (req, res) => { const raw = req.body.toString("utf8"); if (!verifySignature(raw, req.get("SmsLink-Signature"), process.env.SMSLINK_WEBHOOK_SECRET)) { return res.sendStatus(400); } const event = JSON.parse(raw); if (event.type === "verification.verified") { const { phone, metadata } = event.data.verification; // хэрэглэгчийн дугаарыг баталгаажсан гэж тэмдэглэнэ } res.sendStatus(200); });
<?php $raw = file_get_contents('php://input'); $header = $_SERVER['HTTP_SMSLINK_SIGNATURE'] ?? ''; parse_str(str_replace(',', '&', $header), $sig); $secret = getenv('SMSLINK_WEBHOOK_SECRET'); $expected = hash_hmac('sha256', ($sig['t'] ?? '') . '.' . $raw, $secret); if (!hash_equals($expected, $sig['v1'] ?? '') || abs(time() - (int)($sig['t'] ?? 0)) > 300) { http_response_code(400); exit; } $event = json_decode($raw, true); if ($event['type'] === 'verification.verified') { $phone = $event['data']['verification']['phone']; // хэрэглэгчийн дугаарыг баталгаажсан гэж тэмдэглэнэ } http_response_code(200);
import hashlib, hmac, json, os, time
from flask import Flask, request, abort
app = Flask(__name__)
def verify_signature(raw: bytes, header: str, secret: str) -> bool:
parts = dict(p.split("=", 1) for p in header.split(",") if "=" in p)
expected = hmac.new(secret.encode(), f"{parts.get('t', '')}.".encode() + raw, hashlib.sha256).hexdigest()
fresh = abs(time.time() - int(parts.get("t", 0))) < 300
return fresh and hmac.compare_digest(expected, parts.get("v1", ""))
@app.post("/webhooks/smslink")
def smslink_webhook():
raw = request.get_data()
if not verify_signature(raw, request.headers.get("SmsLink-Signature", ""), os.environ["SMSLINK_WEBHOOK_SECRET"]):
abort(400)
event = json.loads(raw)
if event["type"] == "verification.verified":
phone = event["data"]["verification"]["phone"]
# хэрэглэгчийн дугаарыг баталгаажсан гэж тэмдэглэнэ
return "", 200
using System.Security.Cryptography; using System.Text; app.MapPost("/webhooks/smslink", async (HttpRequest request) => { using var reader = new StreamReader(request.Body); var raw = await reader.ReadToEndAsync(); var secret = Environment.GetEnvironmentVariable("SMSLINK_WEBHOOK_SECRET")!; if (!Verify(raw, request.Headers["SmsLink-Signature"].ToString(), secret)) return Results.BadRequest(); using var json = JsonDocument.Parse(raw); if (json.RootElement.GetProperty("type").GetString() == "verification.verified") { var phone = json.RootElement.GetProperty("data").GetProperty("verification").GetProperty("phone").GetString(); // хэрэглэгчийн дугаарыг баталгаажсан гэж тэмдэглэнэ } return Results.Ok(); }); static bool Verify(string rawBody, string header, string secret) { var parts = header.Split(',', StringSplitOptions.RemoveEmptyEntries) .Select(p => p.Split('=', 2)).Where(p => p.Length == 2) .ToDictionary(p => p[0], p => p[1]); if (!parts.TryGetValue("t", out var t) || !long.TryParse(t, out var ts)) return false; var mac = HMACSHA256.HashData(Encoding.UTF8.GetBytes(secret), Encoding.UTF8.GetBytes($"{t}.{rawBody}")); var expected = Encoding.UTF8.GetBytes(Convert.ToHexStringLower(mac)); var given = Encoding.UTF8.GetBytes(parts.GetValueOrDefault("v1", "")); var fresh = Math.Abs(DateTimeOffset.UtcNow.ToUnixTimeSeconds() - ts) < 300; return fresh && CryptographicOperations.FixedTimeEquals(expected, given); }
Хариу ба давтан илгээлт
- 2xx хариуг амжилттай гэж тооцно. Redirect (3xx) дагахгүй тул эцсийн URL-аа шууд тохируулна уу.
- Хариу 10 секунд-ээс удаан бол timeout болно. Хүнд ажлаа queue руу шилжүүлээд шууд 200 буцаахыг зөвлөнө.
- Амжилтгүй бол 1 мин → 5 мин → 30 мин → 2 цаг → 6 цаг → 24 цаг-ийн дараа давтана (нийт 7 оролдлого). Давталт бүр шинэ
t-тэй гарын үсэгтэй. - Хүргэлт бүрийн хүсэлт/хариуг хяналтын самбарын Webhook лог-оос харж, гараар дахин илгээх боломжтой.
- Ижил event давтагдаж ирж болзошгүй тул
id-аар нь idempotent боловсруулна уу.
Төлөв ба тулгах дүрэм
| Төлөв | Утга |
|---|---|
| pending | Код үүссэн, SMS хүлээж байна |
| verified | Оруулсан дугаараас зөв код ирж баталгаажсан |
| expired | Хүчинтэй хугацаанд SMS ирээгүй |
| cancelled | API-аар цуцалсан, эсвэл ижил дугаарт шинэ код үүссэн |
| failed | Код буруу дугаараас 3 удаа ирсэн |
Тулгах дүрэм
- SMS-ийн текстээс 6 оронтой кодыг ялгана.
482915,482 915,Kod: 482915гэх мэт бичлэгийг хүлээн авна. - Кодоор
pendingбаталгаажуулалтыг олж, SMS илгээсэн дугаар нь оруулсан дугаартай яг таарвалverifiedболно. - Өөр дугаараас ирвэл баталгаажихгүй (жишээ нь хоёр SIM-тэй хэрэглэгч өөр SIM-ээс илгээсэн). 3 дахь удаагаас
failedболно. - Нэг код нэг л удаа хүчинтэй. Нэг сайт дээр ижил дугаарт шинэ код үүсгэхэд өмнөх
pendingкодcancelledболно. - Тусгай дугаар бүх операторт ижил: 151586.
Test горим
slk_test_ key-ээр үүсгэсэн баталгаажуулалт бодит SMS-ээр баталгаажихгүй. Оронд нь SMS ирсэн мэт simulate хийнэ — webhook, төлөв, widget бүгд
бодит урсгалтай яг адил ажиллана. Test горим бүртгэл батлагдахаас өмнө ч ажилладаг. Хяналтын самбарын Test горимд баталгаажуулалт бүрийн хажууд "Simulate" товч бий.
Body-гүй бол баталгаажуулалтын дугаараас ирсэн мэт (verified). {"from": "…"} өгвөл тухайн дугаараас ирсэн мэт — mismatch-ийг турших боломжтой. Live key-ээр дуудвал 403 test_mode_only.
# Зөв дугаараас ирсэн мэт (verified болно) curl -X POST https://sms.link.mn/api/v1/verifications/ver_.../simulate \ -H "Authorization: Bearer $SMSLINK_TEST_KEY" # Өөр дугаараас ирсэн мэт (mismatch-ийг турших) curl -X POST https://sms.link.mn/api/v1/verifications/ver_.../simulate \ -H "Authorization: Bearer $SMSLINK_TEST_KEY" \ -H "Content-Type: application/json" \ -d '{"from": "88001122"}'
Алдааны кодууд
Алдааны үед HTTP статус код болон нэг төрлийн JSON хариу буцна. Асуудал шалгуулахдаа request_id-г хавсаргана уу.
HTTP/1.1 400 Bad Request
{
"error": {
"code": "invalid_phone",
"message": "Утасны дугаар буруу байна. 8 оронтой Монгол дугаар оруулна уу.",
"request_id": "0HN7J2K4L5M6:00000001"
}
}| HTTP | code | Тайлбар |
|---|---|---|
| 400 | invalid_request | Body JSON биш, талбар дутуу эсвэл metadata буруу |
| 400 | invalid_phone | Утасны дугаар 8 оронтой Монгол гар утасны дугаар биш |
| 401 | invalid_api_key | API key буруу, байхгүй эсвэл цуцлагдсан |
| 403 | account_pending | Бүртгэл батлагдаагүй байхад live key ашигласан — test key ашиглана уу |
| 403 | account_suspended | Хөгжүүлэгчийн бүртгэл татгалзагдсан эсвэл түр хаагдсан |
| 403 | site_disabled | Сайтын API хандалт хаагдсан |
| 403 | test_mode_only | Simulate-ийг live key-ээр дуудсан |
| 404 | not_found | Баталгаажуулалт олдсонгүй (өөр сайт эсвэл өөр горим) |
| 409 | not_pending | Зөвхөн pending төлөвтэйг цуцлах боломжтой |
| 429 | rate_limited | Хүсэлтийн хязгаар хэтэрсэн — Retry-After header-ийг харна уу |
Хязгаар
| Хязгаар | Утга |
|---|---|
| API хүсэлт | Нэг API key-д минутад 120 хүсэлт |
| Нэг дугаарт | Нэг сайт дээр нэг дугаарт цагт 10 баталгаажуулалт |
| Кодын хугацаа | 120–1800 секунд (анхдагч 600) |
| Буруу дугаараас | 3 оролдлогын дараа failed |
metadata | 2000 тэмдэгт хүртэл JSON object |
| Webhook timeout | 10 секунд, 7 оролдлого |
Асуулт байвал info@sms.link.mn хаягаар холбогдоно уу.