אימות
כל קריאה ל-API של TextMe מאומתת באמצעות טוקן API שנשלח כטוקן bearer בכותרת Authorization. אין session, אין החלפת טוקן בזמן הקריאה, ואין סיסמה בשום מקום בבקשה.
POST /api HTTP/1.1
Host: my.textme.co.il
Authorization: Bearer 0zrjDjZ26Dll5dqdTBmuwwtQg8
Content-Type: application/jsonהגדרות אבטחה מחייבות
שני דברים חייבים להיות מוגדרים עוד לפני שה-API יעבוד עבורכם בכלל:
- TLS בגרסה 1.2 ומעלה. גרסאות פרוטוקול ישנות יותר נדחות.
- חובה להפעיל את שירות ה-IP המורשה. בממשק הניהול, פתחו החשבון שלי בלשונית הגדרות, הפעילו את האפשרות בדיקת כתובת IP מורשית, ורשמו לפחות כתובת IP אחת. חשבון שבו הבדיקה מופעלת ולא הוגדרה בו אף כתובת אינו יכול לקרוא ל-API.
שתי הזהויות בבקשה
| מה | איפה | מה זה אומר |
|---|---|---|
| טוקן API | כותרת Authorization: Bearer … | מוכיח שהקורא רשאי להשתמש ב-API. |
user.username | גוף הבקשה | קובע עבור איזה חשבון הקריאה מתבצעת. |
בחשבון בודד שני אלה מתארים את אותו לקוח, וה-username בגוף הבקשה הוא פשוט שלכם. הם נפרדים כאשר משווק מנהל חשבונות משנה: הטוקן של המשווק מאמת, ו-user.username בוחר על איזה חשבון משנה הפעולה מתבצעת.
טוקן קשור ל-username. הצגת טוקן תקף עם username שהוא לא הונפק עבורו נכשלת בסטטוס 11.
מאיפה מגיעים הטוקנים
שם משתמש וסיסמה אינם מקובלים יותר
יצירת טוקן משם משתמש וסיסמה אינה נתמכת. הטוקן הראשון של חשבון חייב להיווצר בממשק הניהול; אחרי זה ה-API יכול ליצור נוספים.
עדכון אבטחה: יצירת טוקנים באמצעות שם משתמש וסיסמה אינה נתמכת יותר. טוקנים נוצרים רק דרך ממשק הווב, או בקריאת API עם טוקן קיים בתוקף לצד שם המשתמש.
הטוקן הראשון — בממשק הניהול.
- פתחו את הלשונית הגדרות / Settings ובחרו ניהול Token API / API Token Management.
- לחצו על יצירת טוקן חדש / Create New Token.
- הטוקן מוצג פעם אחת בלבד. שמרו אותו במקום מאובטח — לא ניתן לשלוף אותו לאחר מכן.
כל טוקן אחרי זה — דרך ה-API. כשיש בידיכם טוקן תקף אחד, אפשר ליצור נוספים בקריאה לgetApiToken עם action בערך new, או לקרוא את הטוקן העדכני ביותר עם action בערך current.
כללי הטוקנים
| כלל | פירוט |
|---|---|
| טוקנים במקביל | עד 5 פעילים לכל משתמש. כולם עובדים בו-זמנית; יצירת טוקן אינה מבטלת אחר. |
| שמות | לכל טוקן נדרש שם ייחודי. טוקנים שנוצרים דרך ה-API מקבלים שם אוטומטי לפי תאריך ושעת היצירה. |
| תוקף | קבוע לכל טוקן, ומוחזר בשדה expiration_date. יצירת טוקן חדש אינה מארכת ואינה מחדשת טוקן קיים. |
| ביטול | מחיקת טוקן בממשק הניהול מפסיקה את פעולתו מיד. |
| הרשאות | כל טוקן תקף מתאים לכל בקשה שהחשבון רשאי לבצע. |
מכיוון שטוקנים חופפים, החלפה אינה דורשת השבתה: צרו את הטוקן המחליף, פרסו אותו, ורק אז מחקו את הישן.
מקרי כישלון
כשלי אימות חוזרים כ-HTTP 200 עם status שאינו אפס, בדיוק כמו כל שגיאה אחרת.
| Status | message | מתי |
|---|---|---|
3 | Username or password is incorrect and API token is invalid | הטוקן אינו מזוהה. |
10 | Username or password is incorrect and Expired API token | הטוקן היה תקף אבל עבר את ה-expiration_date שלו. |
11 | API token is valid but doesn't match username or if you have newer token you should use it instead | הטוקן שייך ל-username אחר, או שטוקן חדש יותר החליף אותו. |
504 | current token not found | התבקש action: current עבור חשבון שאין לו טוקן כלל. |
511 | you have not permission for this function | האימות הצליח, אבל החשבון אינו רשאי לבצע את הפעולה. |
{
"status": 11,
"message": "API token is valid but doesn't match username or if you have newer token you should use it instead"
}דוגמה מלאה
קריאת הטוקן הנוכחי של חשבון. הפעולה אינה משנה דבר, ולכן זו דרך בטוחה לוודא שהטוקן, ה-username ורשימת ה-IP המורשים מסתדרים ביניהם:
<?xml version="1.0" encoding="UTF-8"?>
<getApiToken>
<user>
<username>admin_username</username>
</user>
<username>username_for_token</username>
<action>current</action>
</getApiToken>{
"getApiToken": {
"user": {
"username": "admin_username"
},
"username": "username_for_token",
"action": "current"
}
}curl --location 'https://my.textme.co.il/api' \
--header "Authorization: Bearer $TEXTME_API_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
"getApiToken": {
"user": {
"username": "admin_username"
},
"username": "username_for_token",
"action": "current"
}
}'// 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({
getApiToken: {
user: {
username: 'admin_username',
},
username: 'username_for_token',
action: 'current',
},
}),
})
const result = await response.json()
// גם שגיאה חוזרת כ-HTTP 200 — הסטטוס שבגוף התשובה הוא הקובע
if (Number(result.status) !== 0) {
throw new Error(`TextMe ${result.status}: ${result.message}`)
}
console.log(result)<?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' => [
'getApiToken' => [
'user' => [
'username' => 'admin_username',
],
'username' => 'username_for_token',
'action' => 'current',
],
],
]);
$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
use Illuminate\Support\Facades\Http;
$result = Http::withToken(config('services.textme.token'))
->acceptJson()
->post('https://my.textme.co.il/api', [
'getApiToken' => [
'user' => [
'username' => 'admin_username',
],
'username' => 'username_for_token',
'action' => 'current',
],
])
->throw()
->json();
// גם שגיאה חוזרת כ-HTTP 200 — הסטטוס שבגוף התשובה הוא הקובע
throw_if((int) $result['status'] !== 0, RuntimeException::class,
"TextMe {$result['status']}: {$result['message']}");
logger()->info('TextMe', $result);# 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={
"getApiToken": {
"user": {
"username": "admin_username",
},
"username": "username_for_token",
"action": "current",
},
},
)
response.raise_for_status()
result = response.json()
# גם שגיאה חוזרת כ-HTTP 200 — הסטטוס שבגוף התשובה הוא הקובע
if int(result["status"]) != 0:
raise RuntimeError(f"TextMe {result['status']}: {result['message']}")
print(result)package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
)
func main() {
payload, _ := json.Marshal(map[string]any{
"getApiToken": map[string]any{
"user": map[string]any{
"username": "admin_username",
},
"username": "username_for_token",
"action": "current",
},
})
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 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 TextMeTokenCurrent {
public static void main(String[] args) throws Exception {
String body = """
{
"getApiToken": {
"user": {
"username": "admin_username"
},
"username": "username_for_token",
"action": "current"
}
}
""";
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());
}
}// .NET 8+ — System.Net.Http
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
var payload = """
{
"getApiToken": {
"user": {
"username": "admin_username"
},
"username": "username_for_token",
"action": "current"
}
}
""";
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);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({
"getApiToken" => {
"user" => {
"username" => "admin_username",
},
"username" => "username_for_token",
"action" => "current",
},
})
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// [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!({
"getApiToken": {
"user": {
"username": "admin_username"
},
"username": "username_for_token",
"action": "current"
}
}))
.send()
.await?
.json()
.await?;
// גם שגיאה חוזרת כ-HTTP 200 — הסטטוס שבגוף התשובה הוא הקובע
if result["status"] != 0 {
return Err(format!("TextMe {}: {}", result["status"], result["message"]).into());
}
println!("{result}");
Ok(())
}חשבון תקין עונה עם הטוקן ומועד התפוגה שלו:
<?xml version="1.0" encoding="utf-8"?>
<getApiToken>
<status>0</status>
<message>0zrjDjZ26Dll5dqdTBmuwwtQg8</message>
<expiration_date>07/06/2025 09:17:34</expiration_date>
</getApiToken>{
"status": 0,
"message": "0zrjDjZ26Dll5dqdTBmuwwtQg8",
"expiration_date": "07/06/2025 09:17:34"
}ראו getApiToken לרשימת הפרמטרים המלאה, כולל אופן יצירת טוקן מחליף.

