Meser 10 Verify: SMS verification codes for your site
What the service does and how to turn it on, and below everything for your developer: ready code, and what to do when something does not work. Every field and error is in the reference.
What it is, in plain words
When someone signs up on your site, places an order or logs in, you can make sure the phone number they gave is really theirs: they get a text message with a 6-digit code and type it on your site. Meser 10 Verify does all of this for you: it creates the code, sends it and checks it was typed correctly.
Why it helps
Fewer fake sign-ups and fake orders, and real phone numbers on your list.
What it costs
Each code is a regular SMS from your SMS package. There is no extra charge.
Is your site on WordPress?
You do not need a developer or this page. A ready plugin does it in a few clicks: OTP plugin for WordPress.
Test without paying
Before going live you also get a test token from us. With it you can try everything without any SMS being sent and without any charge.
The code fills itself in
On iPhone and Android the phone offers the code from the SMS and puts it into your site with one tap, without the user typing it. This works on a site whose address is registered with us.
What you need to do
- Contact support and ask to enable Verify for your account. If you want the code to fill itself in on the phone, also send us your site address.
- Make sure the account has an approved sender name and SMS balance. Support will help if something is missing.
- Send this page to whoever built your site or app. Everything they need is below, including testing without SMS and code autofill.
1. Before you start
- A Meser 10 account and an API key, issued in the account's advanced settings.
- A verified sender name in Latin characters (2 to 11 letters, digits or spaces, no Hebrew) or a verified sender number: sender name.
- SMS balance in the account: buying a package.
- Your own server to make the calls. The key and the token must never reach a browser or an app.
2. Get an access token
Write to support with your account number and ask to enable Verify. You get a token that starts with vt_. Keep it on your server in an environment variable such as MESER10_VERIFY_TOKEN, next to MESER10_API_KEY.
If the account's key is refused at login, sends pause for 15 minutes and you get key_refused; checks keep working, and sends resume by themselves once the key is fixed. So before integrating, check that the key works with the test call in the API quick start. If the token was disabled, fix the key and ask support to enable it again.
3. How it works on your site
- The user types a mobile number.
- Your server calls
/v1/sendand keeps the returnedid, for example in the user's session. - The user types the code they received by SMS.
- Your server calls
/v1/checkwith the id and the code. Ifverifiedis true, the number is verified.
Node.js:
// Node.js 18+, ES module (.mjs)
const H = {
"Content-Type": "application/json",
"ApiKey": process.env.MESER10_API_KEY,
"X-Verify-Token": process.env.MESER10_VERIFY_TOKEN
};
async function verifyCall(path, body) {
const r = await fetch("https://verify.mesereser.com/v1/" + path, { method: "POST", headers: H, body: JSON.stringify(body) });
return r.json();
}
// 1. Send a code
const s = await verifyCall("send", { phone: "0501234567", from: "MyShop", lang: "he" });
if (!s.ok && s.error !== "upstream_uncertain") throw new Error("send failed: " + s.error);
const requestId = s.id; // keep it with the user's session
// 2. Check what the user typed
// use the same ApiKey and token as the send
const c = await verifyCall("check", { id: requestId, code: userInput, phone: "0501234567" });
if (c.verified === true) {
// mark the number as verified
} else {
// not verified: c.error tells why (wrong_code, expired ...), c.attempts_left how many tries remain
}
PHP:
<?php
// PHP 7.4+ with the curl extension
function verify_call(string $path, array $body): array {
$ch = curl_init("https://verify.mesereser.com/v1/" . $path);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 15,
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"ApiKey: " . getenv("MESER10_API_KEY"),
"X-Verify-Token: " . getenv("MESER10_VERIFY_TOKEN"),
],
CURLOPT_POSTFIELDS => json_encode($body),
]);
$data = json_decode((string) curl_exec($ch), true);
curl_close($ch);
return is_array($data) ? $data : [];
}
// 1. Send a code
$s = verify_call("send", ["phone" => "0501234567", "from" => "MyShop", "lang" => "he"]);
if (empty($s["ok"]) && ($s["error"] ?? "") !== "upstream_uncertain") {
throw new RuntimeException("send failed: " . ($s["error"] ?? "no response"));
}
session_start();
$_SESSION["verify_id"] = $s["id"];
// 2. Check what the user typed
// use the same ApiKey and token as the send
$c = verify_call("check", ["id" => $_SESSION["verify_id"], "code" => $_POST["code"], "phone" => "0501234567"]);
if (($c["verified"] ?? false) === true) {
// mark the number as verified
} else {
// not verified: $c["error"] tells why (wrong_code, expired ...), $c["attempts_left"] how many tries remain
}
Python:
# Python 3.8+ (pip install requests)
import os
import requests
API = "https://verify.mesereser.com/v1/"
HEADERS = {
"ApiKey": os.environ["MESER10_API_KEY"],
"X-Verify-Token": os.environ["MESER10_VERIFY_TOKEN"],
}
def verify_call(path, body):
r = requests.post(API + path, json=body, headers=HEADERS, timeout=15)
return r.json()
# 1. Send a code
s = verify_call("send", {"phone": "0501234567", "from": "MyShop", "lang": "he"})
if not s.get("ok") and s.get("error") != "upstream_uncertain":
raise RuntimeError("send failed: " + str(s.get("error")))
request_id = s["id"] # keep it with the user's session
# 2. Check what the user typed
# use the same ApiKey and token as the send
c = verify_call("check", {"id": request_id, "code": user_input, "phone": "0501234567"})
if c.get("verified") is True:
pass # mark the number as verified
else:
pass # not verified: c.get("error") tells why (wrong_code, expired ...), c.get("attempts_left") how many tries remain
cURL (command line):
# 1. Send a code
curl -sS -X POST "https://verify.mesereser.com/v1/send" \
-H "Content-Type: application/json" \
-H "ApiKey: $MESER10_API_KEY" \
-H "X-Verify-Token: $MESER10_VERIFY_TOKEN" \
-d '{"phone":"0501234567","from":"MyShop","lang":"he"}'
# {"ok":true,"id":"<request-id>","expires_in":300,"phone":"050*****67"}
# 2. Check the code the user typed (same ApiKey and token)
curl -sS -X POST "https://verify.mesereser.com/v1/check" \
-H "Content-Type: application/json" \
-H "ApiKey: $MESER10_API_KEY" \
-H "X-Verify-Token: $MESER10_VERIFY_TOKEN" \
-d '{"id":"<request-id>","code":"<code>","phone":"0501234567"}'
# {"ok":true,"verified":true,"phone":"050*****67"}
C# (.NET):
// .NET 6+ console app (top-level statements)
using System.Net.Http.Json;
using System.Text.Json;
var http = new HttpClient { BaseAddress = new Uri("https://verify.mesereser.com/v1/"), Timeout = TimeSpan.FromSeconds(15) };
http.DefaultRequestHeaders.Add("ApiKey", Environment.GetEnvironmentVariable("MESER10_API_KEY"));
http.DefaultRequestHeaders.Add("X-Verify-Token", Environment.GetEnvironmentVariable("MESER10_VERIFY_TOKEN"));
async Task<JsonElement> VerifyCall(string path, object body)
{
var r = await http.PostAsJsonAsync(path, body);
return await r.Content.ReadFromJsonAsync<JsonElement>();
}
static bool IsTrue(JsonElement e, string name) => e.TryGetProperty(name, out var v) && v.ValueKind == JsonValueKind.True;
static string? Text(JsonElement e, string name) => e.TryGetProperty(name, out var v) && v.ValueKind == JsonValueKind.String ? v.GetString() : null;
// 1. Send a code
var s = await VerifyCall("send", new { phone = "0501234567", from = "MyShop", lang = "he" });
if (!IsTrue(s, "ok") && Text(s, "error") != "upstream_uncertain")
throw new Exception("send failed: " + Text(s, "error"));
var requestId = Text(s, "id"); // keep it with the user's session
// 2. Check what the user typed
// use the same ApiKey and token as the send
var c = await VerifyCall("check", new { id = requestId, code = userInput, phone = "0501234567" });
if (IsTrue(c, "verified"))
{
// mark the number as verified
}
else
{
// not verified: Text(c, "error") tells why (wrong_code, expired ...); c.GetProperty("attempts_left") how many tries remain
}
OpenAPI: the full API description is at openapi.json. Load it into Postman, Swagger UI or an AI coding tool to get every field and error.
Test without sending SMS
Ask support for a test token (it starts with vt_test_). Put it in X-Verify-Token instead of the real one. Nothing is sent and nothing is charged. The send answer also returns the code and the exact SMS text, so you can run the whole flow in your tests:
{"ok":true,"id":"<request-id>","expires_in":300,"phone":"050*****67","test":true,"test_code":"481902","message":"481902 הוא קוד האימות שלך"}
A code from a test token can only be checked with a test token, and a real code never with a test token, so a test left in production cannot verify a real user. The same input checks and per-number limits apply, up to 200 test sends a day. In test mode the ApiKey is not checked, but send the same one in send and check.
Code filled in automatically
Add "autofill":"web" to the send. The SMS then ends with a line that phones use to fill in the code on your site by themselves (Safari on iPhone and Mac, Chrome on Android):
{"phone":"0501234567","from":"MyShop","lang":"he","autofill":"web"}
481902 הוא קוד האימות שלך @shop.co.il #481902
Support sets your domain on your token (send us the domain your users see in the address bar). The request cannot change it, so nobody can put another site's name in your SMS. On the page, mark the field as a one-time code:
<input name="code" autocomplete="one-time-code" inputmode="numeric">
<script>
// Chrome on Android (WebOTP); Safari uses autocomplete="one-time-code" by itself
if ("OTPCredential" in window) {
navigator.credentials.get({ otp: { transport: ["sms"] } })
.then(otp => { document.querySelector("[name=code]").value = otp.code; })
.catch(() => {});
}
</script>
For an Android app that uses the SMS Retriever API, send "autofill":"android" and give support your app's 11-character hash. The extra line counts toward the one-SMS limit (70 characters with Hebrew), so keep your own text short.
4. Troubleshooting
The code does not arrive
Look at what /v1/send returned. send_failed: on a key's first send this one answer covers three causes, a refused key, an unverified sender or no balance. no_credits: the SMS balance ran out. sender_not_verified: the sender is not verified. too_soon: wait 30 seconds. phone_rate_limited: too many sends to that number. "ok":true means the SMS was accepted for sending; if it did not arrive, make sure the number is a valid Israeli mobile.
key_refused
The Meser 10 API refused the account's key. Sends pause for 15 minutes (longer if it repeats) and resume by themselves; checks keep working. Fix the key in the account settings.
not_enabled
The token is missing, wrong or disabled. Check the X-Verify-Token header, and contact support if needed.
temporarily_unavailable
The service holds sends to protect all accounts, from 1 second up to 15 minutes. Wait the number of seconds in retry_after and try again.
upstream_uncertain
Not certain the SMS went out. Keep the id from the response and let the user type the code. Do not send a new code right away.
The code is refused
wrong_code comes with attempts_left. After 5 attempts the request is blocked (too_many_attempts or blocked); send a new code. expired: timed out or a newer code was sent. phone_mismatch: the number in the check differs from the one that got the code. already_used: already verified. not_found: make sure the check uses the same key as the send.
Your account restricts API IPs
Verify calls from Cloudflare addresses, so an account that limits its API to specific IPs cannot use it. The sign: the first send fails with key_refused. Contact support.
Questions
How long is a code valid?
Five minutes by default. You can set 1 to 15 minutes with the ttl field on send.
Can I send to numbers abroad?
No. Verify sends to Israeli mobile numbers only.
What does it cost?
The SMS is sent through your own Meser 10 account and charged there like any SMS.
My token was exposed. What now?
Contact support. The old token is disabled and a new one issued.