Skip to content

OTP

שולחת קוד חד-פעמי ב-SMS ומאמתת אותו בחזרה. TextMe מייצרת את הקוד, שומרת אותו מול מספר הטלפון, סופרת את הניסיונות ומפקיעה אותו, כך שאינכם צריכים לייצר או לשמור קודים בעצמכם.

http
POST https://my.textme.co.il/api

שתי פעולות: send_otp מנפיקה קוד, ו-validate_otp מאמתת אותו.

מאיפה זה מגיע

שתי הפעולות האלה מתועדות על ידי TextMe בכתובת docs.textme.co.il/otp/, שהיא בילד תיעוד שני על אותו שרת שטבלת הנתיבים של האתר הראשי אינה מכילה. הפרמטרים, גופי הבקשה ונוסח התשובות למטה הם של הספק.

הן גם הסיבה לכך שטבלת קודי הסטטוס שלנו כוללת את 12 במשמעות "When verifying OTP code: Unverified code".

send_otp שליחת קוד חד-פעמי

מייצרת קוד, שולחת אותו ב-SMS, ומתחילה את חלון התוקף.

פרמטרים

שםסוגתיאורחובה
send_otpobjectאלמנט השורש.✔️
userobjectמכיל את אלמנט המשתמש.✔️
usernamestringשם המשתמש של החשבון שבו אתם מזוהים במערכת.✔️
phoneintהיעד, בפורמט 5xxxxxxx או 05xxxxxxx.✔️
sourcestringהשולח שממנו הקוד ייראה כאילו נשלח. חייב להיות שולח מאומת.✔️
app_idintמפריד בין אפליקציות כשחשבון אחד משרת כמה מהן. ברירת המחדל היא 1.
max_triesintכמה ניסיונות אימות הקוד מאפשר, בין 3 ל-5. ברירת המחדל היא 3.
valid_timeintכמה דקות הקוד נשאר בתוקף, בין 1 ל-15. ברירת המחדל היא 5.
textstringנוסח ההודעה שלכם. חייב להכיל [code], שיוחלף בקוד שנוצר. ברירת המחדל היא the code is [code].

דוגמת בקשה

xml
<?xml version="1.0" encoding="UTF-8"?>
<send_otp>
    <user>
        <username>Leeroy</username>
    </user>
    <phone>5xxxxxxxx</phone>
    <source>DemoAPI</source>
    <app_id>1</app_id>
    <max_tries>3</max_tries>
    <valid_time>5</valid_time>
    <text>Your DemoAPI code is [code]</text>
</send_otp>
json
{
  "send_otp": {
    "user": {
      "username": "Leeroy"
    },
    "phone": "5xxxxxxxx",
    "source": "DemoAPI",
    "app_id": "1",
    "max_tries": "3",
    "valid_time": "5",
    "text": "Your DemoAPI code is [code]"
  }
}
bash
curl --location 'https://my.textme.co.il/api' \
--header "Authorization: Bearer $TEXTME_API_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
  "send_otp": {
    "user": {
      "username": "Leeroy"
    },
    "phone": "5xxxxxxxx",
    "source": "DemoAPI",
    "app_id": "1",
    "max_tries": "3",
    "valid_time": "5",
    "text": "Your DemoAPI code is [code]"
  }
}'
js
// Node.js 18+ או כל דפדפן מודרני. ללא תלויות
const response = await fetch('https://my.textme.co.il/api', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.TEXTME_API_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    send_otp: {
      user: {
        username: 'Leeroy',
      },
      phone: '5xxxxxxxx',
      source: 'DemoAPI',
      app_id: '1',
      max_tries: '3',
      valid_time: '5',
      text: 'Your DemoAPI code is [code]',
    },
  }),
})

const result = await response.json()

// גם שגיאה חוזרת כ-HTTP 200, לכן הסטטוס שבגוף התשובה הוא הקובע
if (Number(result.status) !== 0) {
  throw new Error(`TextMe ${result.status}: ${result.message}`)
}

console.log(result)
php
<?php
// composer require guzzlehttp/guzzle

$client = new \GuzzleHttp\Client([
    'headers' => [
        'Authorization' => 'Bearer '.getenv('TEXTME_API_TOKEN'),
        'Accept' => 'application/json',
    ],
]);

$response = $client->post('https://my.textme.co.il/api', [
    'json' => [
        'send_otp' => [
            'user' => [
                'username' => 'Leeroy',
            ],
            'phone' => '5xxxxxxxx',
            'source' => 'DemoAPI',
            'app_id' => '1',
            'max_tries' => '3',
            'valid_time' => '5',
            'text' => 'Your DemoAPI code is [code]',
        ],
    ],
]);

$result = json_decode($response->getBody()->getContents(), true);

// גם שגיאה חוזרת כ-HTTP 200, לכן הסטטוס שבגוף התשובה הוא הקובע
if ((int) $result['status'] !== 0) {
    throw new RuntimeException("TextMe {$result['status']}: {$result['message']}");
}

print_r($result);
php
<?php

use Illuminate\Support\Facades\Http;

$result = Http::withToken(config('services.textme.token'))
    ->acceptJson()
    ->post('https://my.textme.co.il/api', [
        'send_otp' => [
            'user' => [
                'username' => 'Leeroy',
            ],
            'phone' => '5xxxxxxxx',
            'source' => 'DemoAPI',
            'app_id' => '1',
            'max_tries' => '3',
            'valid_time' => '5',
            'text' => 'Your DemoAPI code is [code]',
        ],
    ])
    ->throw()
    ->json();

// גם שגיאה חוזרת כ-HTTP 200, לכן הסטטוס שבגוף התשובה הוא הקובע
throw_if((int) $result['status'] !== 0, RuntimeException::class,
    "TextMe {$result['status']}: {$result['message']}");

logger()->info('TextMe', $result);
python
# pip install httpx
import os

import httpx

response = httpx.post(
    "https://my.textme.co.il/api",
    headers={"Authorization": f"Bearer {os.environ['TEXTME_API_TOKEN']}"},
    json={
        "send_otp": {
            "user": {
                "username": "Leeroy",
            },
            "phone": "5xxxxxxxx",
            "source": "DemoAPI",
            "app_id": "1",
            "max_tries": "3",
            "valid_time": "5",
            "text": "Your DemoAPI code is [code]",
        },
    },
)
response.raise_for_status()
result = response.json()

# גם שגיאה חוזרת כ-HTTP 200, לכן הסטטוס שבגוף התשובה הוא הקובע
if int(result["status"]) != 0:
    raise RuntimeError(f"TextMe {result['status']}: {result['message']}")

print(result)
go
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"os"
)

func main() {
	payload, _ := json.Marshal(map[string]any{
		"send_otp": map[string]any{
			"user": map[string]any{
				"username": "Leeroy",
			},
			"phone": "5xxxxxxxx",
			"source": "DemoAPI",
			"app_id": "1",
			"max_tries": "3",
			"valid_time": "5",
			"text": "Your DemoAPI code is [code]",
		},
	})

	req, _ := http.NewRequest("POST", "https://my.textme.co.il/api", bytes.NewReader(payload))
	req.Header.Set("Authorization", "Bearer "+os.Getenv("TEXTME_API_TOKEN"))
	req.Header.Set("Content-Type", "application/json")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	var result struct {
		Status  json.Number `json:"status"`
		Message string      `json:"message"`
	}
	if err := json.NewDecoder(res.Body).Decode(&result); err != nil {
		panic(err)
	}

	// גם שגיאה חוזרת כ-HTTP 200, לכן הסטטוס שבגוף התשובה הוא הקובע
	if result.Status.String() != "0" {
		panic(fmt.Sprintf("TextMe %s: %s", result.Status, result.Message))
	}

	fmt.Println(result.Message)
}
java
// Java 17+ באמצעות java.net.http. ללא תלויות (פענוח עם Jackson/Gson)
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class TextMeOtpSend {
    public static void main(String[] args) throws Exception {
        String body = """
            {
              "send_otp": {
                "user": {
                  "username": "Leeroy"
                },
                "phone": "5xxxxxxxx",
                "source": "DemoAPI",
                "app_id": "1",
                "max_tries": "3",
                "valid_time": "5",
                "text": "Your DemoAPI code is [code]"
              }
            }
            """;

        HttpRequest request = HttpRequest.newBuilder(URI.create("https://my.textme.co.il/api"))
            .header("Authorization", "Bearer " + System.getenv("TEXTME_API_TOKEN"))
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(body))
            .build();

        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());

        // גם שגיאה חוזרת כ-HTTP 200, לכן הסטטוס שבגוף התשובה הוא הקובע
        System.out.println(response.body());
    }
}
csharp
// .NET 8+ using System.Net.Http
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;

var payload = """
    {
      "send_otp": {
        "user": {
          "username": "Leeroy"
        },
        "phone": "5xxxxxxxx",
        "source": "DemoAPI",
        "app_id": "1",
        "max_tries": "3",
        "valid_time": "5",
        "text": "Your DemoAPI code is [code]"
      }
    }
    """;

using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
    "Bearer", Environment.GetEnvironmentVariable("TEXTME_API_TOKEN"));

var response = await http.PostAsync("https://my.textme.co.il/api",
    new StringContent(payload, Encoding.UTF8, "application/json"));

var result = JsonDocument.Parse(await response.Content.ReadAsStringAsync()).RootElement;
var status = result.GetProperty("status").ToString();

// גם שגיאה חוזרת כ-HTTP 200, לכן הסטטוס שבגוף התשובה הוא הקובע
if (status != "0")
{
    var message = result.GetProperty("message").ToString();
    throw new Exception($"TextMe {status}: {message}");
}

Console.WriteLine(result);
ruby
require "net/http"
require "json"

uri = URI("https://my.textme.co.il/api")

request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer #{ENV.fetch('TEXTME_API_TOKEN')}"
request["Content-Type"] = "application/json"
request.body = JSON.dump({
  "send_otp" => {
    "user" => {
      "username" => "Leeroy",
    },
    "phone" => "5xxxxxxxx",
    "source" => "DemoAPI",
    "app_id" => "1",
    "max_tries" => "3",
    "valid_time" => "5",
    "text" => "Your DemoAPI code is [code]",
  },
})

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

result = JSON.parse(response.body)

# גם שגיאה חוזרת כ-HTTP 200, לכן הסטטוס שבגוף התשובה הוא הקובע
raise "TextMe #{result['status']}: #{result['message']}" unless result["status"].to_i.zero?

pp result
rust
// [dependencies]
// reqwest = { version = "0.12", features = ["json"] }
// tokio = { version = "1", features = ["full"] }
// serde_json = "1"
use serde_json::{json, Value};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let result: Value = reqwest::Client::new()
        .post("https://my.textme.co.il/api")
        .bearer_auth(std::env::var("TEXTME_API_TOKEN")?)
        .json(&json!({
          "send_otp": {
            "user": {
              "username": "Leeroy"
            },
            "phone": "5xxxxxxxx",
            "source": "DemoAPI",
            "app_id": "1",
            "max_tries": "3",
            "valid_time": "5",
            "text": "Your DemoAPI code is [code]"
          }
        }))
        .send()
        .await?
        .json()
        .await?;

    // גם שגיאה חוזרת כ-HTTP 200, לכן הסטטוס שבגוף התשובה הוא הקובע
    if result["status"] != 0 {
        return Err(format!("TextMe {}: {}", result["status"], result["message"]).into());
    }

    println!("{result}");
    Ok(())
}

תשובה

xml
<?xml version="1.0" encoding="utf-8"?>
<sms>
    <status>0</status>
    <code>448431</code>
    <message>The code is : 448431 and is valid for 5 minutes</message>
</sms>
json
{
  "status": 0,
  "code": "448431",
  "message": "The code is : 448431 and is valid for 5 minutes"
}

status בערך 0 אומר שהקוד נוצר וה-SMS נתקבל לשליחה.

שימו לב למבנה התשובה: אלמנט השורש הוא sms ולא send_otp, והקוד שנוצר חוזר בשדה code, עם אותו ערך שחוזר גם בתוך message.

התשובה מכילה את הקוד

send_otp מחזירה לקורא את הקוד בטקסט גלוי. כל דבר שמתעד את גוף התשובה, כלי APM, מדווח שגיאות, לוג דיבאג, מתעד קוד חד-פעמי פעיל עבור אותו מספר טלפון.

הסתירו את code ואת message לפני תיעוד. אינכם צריכים אף אחד מהערכים כדי להשלים את התהליך: המכשיר מקבל את הקוד, ו-validate_otp אומרת לכם אם המשתמש הקליד אותו נכון.

שגיאות

Statusמתי
2חסר אלמנט חובה; ה-message מציין איזה.
4אין מספיק יתרה לשליחת ה-SMS.
9ה-phone קצר או ארוך מדי.
515ה-source אינו שולח מאומת.
989ה-text ריק או עובר את מגבלת אורך ההודעה.

validate_otp אימות קוד חד-פעמי

מאמתת קוד שהמשתמש הקליד מול הקוד השמור עבור אותו טלפון ואותו app_id.

פרמטרים

שםסוגתיאורחובה
validate_otpobjectאלמנט השורש.✔️
userobjectמכיל את אלמנט המשתמש.✔️
usernamestringשם המשתמש של החשבון שבו אתם מזוהים במערכת.✔️
phoneintהמספר שאליו נשלח הקוד, בפורמט 5xxxxxxx או 05xxxxxxx.✔️
codeintהקוד בן שש הספרות לאימות.✔️
app_idintחייב להתאים ל-app_id שנשלח בזמן השליחה. ברירת המחדל היא 1.
service_typestringהערוץ שהנפיק את הקוד, sms או whatsapp. ברירת המחדל היא sms.

דוגמת בקשה

xml
<?xml version="1.0" encoding="UTF-8"?>
<validate_otp>
    <user>
        <username>Leeroy</username>
    </user>
    <phone>5xxxxxxxx</phone>
    <app_id>1</app_id>
    <code>407526</code>
    <service_type>sms</service_type>
</validate_otp>
json
{
  "validate_otp": {
    "user": {
      "username": "Leeroy"
    },
    "phone": "5xxxxxxxx",
    "app_id": "1",
    "code": "407526",
    "service_type": "sms"
  }
}
bash
curl --location 'https://my.textme.co.il/api' \
--header "Authorization: Bearer $TEXTME_API_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
  "validate_otp": {
    "user": {
      "username": "Leeroy"
    },
    "phone": "5xxxxxxxx",
    "app_id": "1",
    "code": "407526",
    "service_type": "sms"
  }
}'
js
// Node.js 18+ או כל דפדפן מודרני. ללא תלויות
const response = await fetch('https://my.textme.co.il/api', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.TEXTME_API_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    validate_otp: {
      user: {
        username: 'Leeroy',
      },
      phone: '5xxxxxxxx',
      app_id: '1',
      code: '407526',
      service_type: 'sms',
    },
  }),
})

const result = await response.json()

// גם שגיאה חוזרת כ-HTTP 200, לכן הסטטוס שבגוף התשובה הוא הקובע
if (Number(result.status) !== 0) {
  throw new Error(`TextMe ${result.status}: ${result.message}`)
}

console.log(result)
php
<?php
// composer require guzzlehttp/guzzle

$client = new \GuzzleHttp\Client([
    'headers' => [
        'Authorization' => 'Bearer '.getenv('TEXTME_API_TOKEN'),
        'Accept' => 'application/json',
    ],
]);

$response = $client->post('https://my.textme.co.il/api', [
    'json' => [
        'validate_otp' => [
            'user' => [
                'username' => 'Leeroy',
            ],
            'phone' => '5xxxxxxxx',
            'app_id' => '1',
            'code' => '407526',
            'service_type' => 'sms',
        ],
    ],
]);

$result = json_decode($response->getBody()->getContents(), true);

// גם שגיאה חוזרת כ-HTTP 200, לכן הסטטוס שבגוף התשובה הוא הקובע
if ((int) $result['status'] !== 0) {
    throw new RuntimeException("TextMe {$result['status']}: {$result['message']}");
}

print_r($result);
php
<?php

use Illuminate\Support\Facades\Http;

$result = Http::withToken(config('services.textme.token'))
    ->acceptJson()
    ->post('https://my.textme.co.il/api', [
        'validate_otp' => [
            'user' => [
                'username' => 'Leeroy',
            ],
            'phone' => '5xxxxxxxx',
            'app_id' => '1',
            'code' => '407526',
            'service_type' => 'sms',
        ],
    ])
    ->throw()
    ->json();

// גם שגיאה חוזרת כ-HTTP 200, לכן הסטטוס שבגוף התשובה הוא הקובע
throw_if((int) $result['status'] !== 0, RuntimeException::class,
    "TextMe {$result['status']}: {$result['message']}");

logger()->info('TextMe', $result);
python
# pip install httpx
import os

import httpx

response = httpx.post(
    "https://my.textme.co.il/api",
    headers={"Authorization": f"Bearer {os.environ['TEXTME_API_TOKEN']}"},
    json={
        "validate_otp": {
            "user": {
                "username": "Leeroy",
            },
            "phone": "5xxxxxxxx",
            "app_id": "1",
            "code": "407526",
            "service_type": "sms",
        },
    },
)
response.raise_for_status()
result = response.json()

# גם שגיאה חוזרת כ-HTTP 200, לכן הסטטוס שבגוף התשובה הוא הקובע
if int(result["status"]) != 0:
    raise RuntimeError(f"TextMe {result['status']}: {result['message']}")

print(result)
go
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"os"
)

func main() {
	payload, _ := json.Marshal(map[string]any{
		"validate_otp": map[string]any{
			"user": map[string]any{
				"username": "Leeroy",
			},
			"phone": "5xxxxxxxx",
			"app_id": "1",
			"code": "407526",
			"service_type": "sms",
		},
	})

	req, _ := http.NewRequest("POST", "https://my.textme.co.il/api", bytes.NewReader(payload))
	req.Header.Set("Authorization", "Bearer "+os.Getenv("TEXTME_API_TOKEN"))
	req.Header.Set("Content-Type", "application/json")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	var result struct {
		Status  json.Number `json:"status"`
		Message string      `json:"message"`
	}
	if err := json.NewDecoder(res.Body).Decode(&result); err != nil {
		panic(err)
	}

	// גם שגיאה חוזרת כ-HTTP 200, לכן הסטטוס שבגוף התשובה הוא הקובע
	if result.Status.String() != "0" {
		panic(fmt.Sprintf("TextMe %s: %s", result.Status, result.Message))
	}

	fmt.Println(result.Message)
}
java
// Java 17+ באמצעות java.net.http. ללא תלויות (פענוח עם Jackson/Gson)
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class TextMeOtpValidate {
    public static void main(String[] args) throws Exception {
        String body = """
            {
              "validate_otp": {
                "user": {
                  "username": "Leeroy"
                },
                "phone": "5xxxxxxxx",
                "app_id": "1",
                "code": "407526",
                "service_type": "sms"
              }
            }
            """;

        HttpRequest request = HttpRequest.newBuilder(URI.create("https://my.textme.co.il/api"))
            .header("Authorization", "Bearer " + System.getenv("TEXTME_API_TOKEN"))
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(body))
            .build();

        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());

        // גם שגיאה חוזרת כ-HTTP 200, לכן הסטטוס שבגוף התשובה הוא הקובע
        System.out.println(response.body());
    }
}
csharp
// .NET 8+ using System.Net.Http
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;

var payload = """
    {
      "validate_otp": {
        "user": {
          "username": "Leeroy"
        },
        "phone": "5xxxxxxxx",
        "app_id": "1",
        "code": "407526",
        "service_type": "sms"
      }
    }
    """;

using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
    "Bearer", Environment.GetEnvironmentVariable("TEXTME_API_TOKEN"));

var response = await http.PostAsync("https://my.textme.co.il/api",
    new StringContent(payload, Encoding.UTF8, "application/json"));

var result = JsonDocument.Parse(await response.Content.ReadAsStringAsync()).RootElement;
var status = result.GetProperty("status").ToString();

// גם שגיאה חוזרת כ-HTTP 200, לכן הסטטוס שבגוף התשובה הוא הקובע
if (status != "0")
{
    var message = result.GetProperty("message").ToString();
    throw new Exception($"TextMe {status}: {message}");
}

Console.WriteLine(result);
ruby
require "net/http"
require "json"

uri = URI("https://my.textme.co.il/api")

request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer #{ENV.fetch('TEXTME_API_TOKEN')}"
request["Content-Type"] = "application/json"
request.body = JSON.dump({
  "validate_otp" => {
    "user" => {
      "username" => "Leeroy",
    },
    "phone" => "5xxxxxxxx",
    "app_id" => "1",
    "code" => "407526",
    "service_type" => "sms",
  },
})

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

result = JSON.parse(response.body)

# גם שגיאה חוזרת כ-HTTP 200, לכן הסטטוס שבגוף התשובה הוא הקובע
raise "TextMe #{result['status']}: #{result['message']}" unless result["status"].to_i.zero?

pp result
rust
// [dependencies]
// reqwest = { version = "0.12", features = ["json"] }
// tokio = { version = "1", features = ["full"] }
// serde_json = "1"
use serde_json::{json, Value};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let result: Value = reqwest::Client::new()
        .post("https://my.textme.co.il/api")
        .bearer_auth(std::env::var("TEXTME_API_TOKEN")?)
        .json(&json!({
          "validate_otp": {
            "user": {
              "username": "Leeroy"
            },
            "phone": "5xxxxxxxx",
            "app_id": "1",
            "code": "407526",
            "service_type": "sms"
          }
        }))
        .send()
        .await?
        .json()
        .await?;

    // גם שגיאה חוזרת כ-HTTP 200, לכן הסטטוס שבגוף התשובה הוא הקובע
    if result["status"] != 0 {
        return Err(format!("TextMe {}: {}", result["status"], result["message"]).into());
    }

    println!("{result}");
    Ok(())
}

תשובה

xml
<?xml version="1.0" encoding="utf-8"?>
<sms>
    <status>0</status>
    <message>The code successfully validated</message>
</sms>
json
{
  "status": 0,
  "message": "The code successfully validated"
}

שגיאות

Statusמתי
12הקוד לא אומת. המשמעות המתועדת של 12 בפעולה הזו היא Unverified code: קוד שגוי, קוד שפג תוקפו, או ניסיונות שנגמרו.
2חסר אלמנט חובה; ה-message מציין איזה.

סטטוס 12 הוא זה שצריך להתנות עליו

ל-12 שתי משמעויות שאינן קשורות, תלוי בפעולה: אין מספיק כסף בשליחת הודעה, וקוד לא מאומת כאן. קראו אותו מול הקריאה שביצעתם, מעולם לא בפני עצמו. ראו קודי סטטוס.

הערות על השדות

app_id מפריד בין האפליקציות שלכם

חשבון TextMe אחד יכול לשרת כמה מוצרים. מתן app_id נפרד לכל אחד שומר על עצמאות הקודים, כך שקוד שהונפק לאפליקציה 1000 אינו ניתן לאימות מול אפליקציה 1001, גם עבור אותו מספר טלפון.

הערך חייב להיות זהה בין send_otp ל-validate_otp. אי-התאמה אינה ניתנת להבחנה מקוד שגוי: תקבלו סטטוס 12.

כוונו את max_tries ואת valid_time יחד

ברירת המחדל היא שלושה ניסיונות בתוך חמש דקות. שניהם מגבילים את אותו סיכון משני כיוונים:

  • valid_time (1 עד 15 דקות) מגביל כמה זמן קוד שדלף נשאר שימושי. קצר יותר בטוח יותר, אבל קצר מדי ומשתמשים שעוברים לאפליקציה אחרת לקרוא את ה-SMS יחזרו לקוד שפג.
  • max_tries (3 עד 5) מגביל ניחושים. קוד בן שש ספרות עם חמישה ניסיונות בטוח בהחלט.

הניסיונות נספרים בצד השרת, כך שאינכם צריכים לעקוב אחריהם.

כתבו את ההודעה כך שהקוד יימצא

text חייב להכיל [code]. אם תשמיטו אותו, הנמען יקבל הודעה בלי קוד בתוכה.

שימו את הקוד בתחילת ההודעה ושמרו על נוסח פשוט, כי מכשירים ומנהלי סיסמאות מזהים קודים טוב יותר בהודעות קצרות. אם אתם משתמשים ב-SMS Retriever של Google, שדה tag בשליחת sms רגילה הוא המנגנון לתחילית <#>; ל-send_otp אין מקבילה, ולכן תהליך שזקוק לזה צריך לשלוח את הקוד עם sms ולבצע אימות עצמאי.

מה זה מחליף

בלי שתי הפעולות האלה הייתם שולחים קוד עם sms, שומרים אותו בעצמכם, מפקיעים אותו בעצמכם וסופרים ניסיונות בעצמכם. send_otp מעבירה את כל זה לצד השרת: יצירה, שמירה, הפקעה וספירת ניסיונות. אתם עדיין מקבלים את הקוד בתשובה, כך שכלי תמיכה יכול להציג אותו אם עמדת הרגולציה שלכם מאפשרת, אבל התייחסו לזה כבחירה מודעת ולא כברירת מחדל.

מה לא מכוסה כאן

TextMe מתעדת גם קריאת OTP ב-WhatsApp בנקודת קצה נפרדת (whatsapp-api/send-otp-whatsapp, טוקן bearer, JSON בלבד, בלי XML), לצד API מלא ל-WhatsApp. שניהם עדיין אינם מכוסים באתר הזה. שניהם נמצאים מחוץ ל-API של ה-SMS שהעמוד הזה שייך לו, והשדה service_type שלמעלה הוא מה שמקשר בין שני ערוצי ה-OTP.