הודעות קוליות (TTS)
הופכת הודעת טקסט לשיחת טלפון מוקלטת. זו פעולת sms עם בלוק tts מצורף: אותו אלמנט שורש, אותם יעדים, אותו הכול — בתוספת הוראות איך להקריא.
POST https://my.textme.co.il/apiהשימוש העיקרי הוא הגעה למכשירים שאינם יכולים לקבל SMS רגיל. מכשירים כשרים (מסוננים) נפוצים בישראל ומחזירים סטטוס מסירה 15 כשנשלח אליהם SMS; tts.type בערך 2 מנתב את המספרים האלה לשיחה קולית אוטומטית, בעוד כל השאר מקבלים SMS.
sms שליחת הודעה קולית
פרמטרים
כל מה ש-sms מקבלת, בתוספת:
| שם | סוג | תיאור | חובה |
|---|---|---|---|
tts | object | מכיל את כל ההגדרות לשליחת שיחות. עצם קיומו הוא מה שהופך את השליחה לשליחה קולית. | ✔️ |
tts.type | int | איזה ערוץ כל יעד מקבל — ראו הטבלה למטה. | ✔️ |
tts.rate | int | מהירות הדיבור, מ--10 עד 10. 0 הוא רגיל. | ➖ |
tts.repeat | int | כמה פעמים ההודעה תושמע בשיחה. ברירת המחדל היא פעמיים. | ➖ |
tts.voice | string | ymMale או ymFemale. ברירת המחדל היא ymMale. | ➖ |
והשדות המשותפים לשליחת טקסט:
| שם | סוג | תיאור | חובה |
|---|---|---|---|
user.username | string | שם המשתמש של החשבון שבו אתם מזוהים במערכת. | ✔️ |
source | string | השולח עבור חלק ה-SMS של השליחה. עד 11 תווים. שיחות יוצאות תמיד ממספר מערכת — source אינו חל עליהן. | ✔️ |
destinations | object | מכיל את כל היעדים. יכול להחזיק כמה אלמנטי phone ו-cl_id. | ✔️ |
phone | string | מספר יעד, בפורמט 5xxxxxxx או 05xxxxxxx. | ✔️ |
message | string | הטקסט שיוקרא, וגם יישלח כ-SMS כשה-type דורש זאת. עד 1005 תווים. | ✔️ |
tts.type
| ערך | התנהגות |
|---|---|
2 | שליחת הודעה רגילה לטלפון רגיל, ושיחה קולית לטלפון כשר. |
3 | שליחת גם הודעה רגילה וגם שיחה קולית לכל המספרים. |
4 | שליחת שיחה קולית בלבד לכל המספרים. |
2 הוא בדרך כלל מה שרוצים: הוא מוציא עלות שיחה רק שם שבו SMS היה נכשל. 3 מכפיל במכוון — שימושי להתראות דחופות. 4 מדלג על SMS לגמרי.
דוגמת בקשה
<?xml version="1.0" encoding="UTF-8"?>
<sms>
<user>
<username>Leeroy</username>
</user>
<source>DemoAPI</source>
<destinations>
<cl_id>21518</cl_id>
<cl_id>21500</cl_id>
<phone id="external id1">5xxxxxxxx</phone>
<phone id="external id2">5xxxxxxxx</phone>
<phone>5xxxxxxxx</phone>
<phone id="">5xxxxxxxx</phone>
</destinations>
<message>This is a sample tts message</message>
<tts>
<type>4</type>
<rate>-1</rate>
<repeat>1</repeat>
<voice>ymMale</voice>
</tts>
</sms>{
"sms": {
"user": {
"username": "Leeroy"
},
"source": "DemoAPI",
"destinations": {
"cl_id": [
"21518",
"21500"
],
"phone": [
{
"$": {
"id": "external id1"
},
"_": "5xxxxxxxx"
},
{
"$": {
"id": "external id2"
},
"_": "5xxxxxxxx"
},
{
"_": "5xxxxxxxx"
},
{
"$": {
"id": ""
},
"_": "5xxxxxxxx"
}
]
},
"message": "This is a sample tts message",
"tts": {
"type": 4,
"rate": -1,
"repeat": 1,
"voice": "ymMale"
}
}
}curl --location 'https://my.textme.co.il/api' \
--header "Authorization: Bearer $TEXTME_API_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
"sms": {
"user": {
"username": "Leeroy"
},
"source": "DemoAPI",
"destinations": {
"cl_id": [
"21518",
"21500"
],
"phone": [
{
"$": {
"id": "external id1"
},
"_": "5xxxxxxxx"
},
{
"$": {
"id": "external id2"
},
"_": "5xxxxxxxx"
},
{
"_": "5xxxxxxxx"
},
{
"$": {
"id": ""
},
"_": "5xxxxxxxx"
}
]
},
"message": "This is a sample tts message",
"tts": {
"type": 4,
"rate": -1,
"repeat": 1,
"voice": "ymMale"
}
}
}'// 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({
sms: {
user: {
username: 'Leeroy',
},
source: 'DemoAPI',
destinations: {
cl_id: [
'21518',
'21500',
],
phone: [
{
$: {
id: 'external id1',
},
_: '5xxxxxxxx',
},
{
$: {
id: 'external id2',
},
_: '5xxxxxxxx',
},
{
_: '5xxxxxxxx',
},
{
$: {
id: '',
},
_: '5xxxxxxxx',
},
],
},
message: 'This is a sample tts message',
tts: {
type: 4,
rate: -1,
repeat: 1,
voice: 'ymMale',
},
},
}),
})
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' => [
'sms' => [
'user' => [
'username' => 'Leeroy',
],
'source' => 'DemoAPI',
'destinations' => [
'cl_id' => [
'21518',
'21500',
],
'phone' => [
[
'$' => [
'id' => 'external id1',
],
'_' => '5xxxxxxxx',
],
[
'$' => [
'id' => 'external id2',
],
'_' => '5xxxxxxxx',
],
[
'_' => '5xxxxxxxx',
],
[
'$' => [
'id' => '',
],
'_' => '5xxxxxxxx',
],
],
],
'message' => 'This is a sample tts message',
'tts' => [
'type' => 4,
'rate' => -1,
'repeat' => 1,
'voice' => 'ymMale',
],
],
],
]);
$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', [
'sms' => [
'user' => [
'username' => 'Leeroy',
],
'source' => 'DemoAPI',
'destinations' => [
'cl_id' => [
'21518',
'21500',
],
'phone' => [
[
'$' => [
'id' => 'external id1',
],
'_' => '5xxxxxxxx',
],
[
'$' => [
'id' => 'external id2',
],
'_' => '5xxxxxxxx',
],
[
'_' => '5xxxxxxxx',
],
[
'$' => [
'id' => '',
],
'_' => '5xxxxxxxx',
],
],
],
'message' => 'This is a sample tts message',
'tts' => [
'type' => 4,
'rate' => -1,
'repeat' => 1,
'voice' => 'ymMale',
],
],
])
->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={
"sms": {
"user": {
"username": "Leeroy",
},
"source": "DemoAPI",
"destinations": {
"cl_id": [
"21518",
"21500",
],
"phone": [
{
"$": {
"id": "external id1",
},
"_": "5xxxxxxxx",
},
{
"$": {
"id": "external id2",
},
"_": "5xxxxxxxx",
},
{
"_": "5xxxxxxxx",
},
{
"$": {
"id": "",
},
"_": "5xxxxxxxx",
},
],
},
"message": "This is a sample tts message",
"tts": {
"type": 4,
"rate": -1,
"repeat": 1,
"voice": "ymMale",
},
},
},
)
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{
"sms": map[string]any{
"user": map[string]any{
"username": "Leeroy",
},
"source": "DemoAPI",
"destinations": map[string]any{
"cl_id": []any{
"21518",
"21500",
},
"phone": []any{
map[string]any{
"$": map[string]any{
"id": "external id1",
},
"_": "5xxxxxxxx",
},
map[string]any{
"$": map[string]any{
"id": "external id2",
},
"_": "5xxxxxxxx",
},
map[string]any{
"_": "5xxxxxxxx",
},
map[string]any{
"$": map[string]any{
"id": "",
},
"_": "5xxxxxxxx",
},
},
},
"message": "This is a sample tts message",
"tts": map[string]any{
"type": 4,
"rate": -1,
"repeat": 1,
"voice": "ymMale",
},
},
})
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 TextMeSendTts {
public static void main(String[] args) throws Exception {
String body = """
{
"sms": {
"user": {
"username": "Leeroy"
},
"source": "DemoAPI",
"destinations": {
"cl_id": [
"21518",
"21500"
],
"phone": [
{
"$": {
"id": "external id1"
},
"_": "5xxxxxxxx"
},
{
"$": {
"id": "external id2"
},
"_": "5xxxxxxxx"
},
{
"_": "5xxxxxxxx"
},
{
"$": {
"id": ""
},
"_": "5xxxxxxxx"
}
]
},
"message": "This is a sample tts message",
"tts": {
"type": 4,
"rate": -1,
"repeat": 1,
"voice": "ymMale"
}
}
}
""";
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 = """
{
"sms": {
"user": {
"username": "Leeroy"
},
"source": "DemoAPI",
"destinations": {
"cl_id": [
"21518",
"21500"
],
"phone": [
{
"$": {
"id": "external id1"
},
"_": "5xxxxxxxx"
},
{
"$": {
"id": "external id2"
},
"_": "5xxxxxxxx"
},
{
"_": "5xxxxxxxx"
},
{
"$": {
"id": ""
},
"_": "5xxxxxxxx"
}
]
},
"message": "This is a sample tts message",
"tts": {
"type": 4,
"rate": -1,
"repeat": 1,
"voice": "ymMale"
}
}
}
""";
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({
"sms" => {
"user" => {
"username" => "Leeroy",
},
"source" => "DemoAPI",
"destinations" => {
"cl_id" => [
"21518",
"21500",
],
"phone" => [
{
"$" => {
"id" => "external id1",
},
"_" => "5xxxxxxxx",
},
{
"$" => {
"id" => "external id2",
},
"_" => "5xxxxxxxx",
},
{
"_" => "5xxxxxxxx",
},
{
"$" => {
"id" => "",
},
"_" => "5xxxxxxxx",
},
],
},
"message" => "This is a sample tts message",
"tts" => {
"type" => 4,
"rate" => -1,
"repeat" => 1,
"voice" => "ymMale",
},
},
})
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!({
"sms": {
"user": {
"username": "Leeroy"
},
"source": "DemoAPI",
"destinations": {
"cl_id": [
"21518",
"21500"
],
"phone": [
{
"$": {
"id": "external id1"
},
"_": "5xxxxxxxx"
},
{
"$": {
"id": "external id2"
},
"_": "5xxxxxxxx"
},
{
"_": "5xxxxxxxx"
},
{
"$": {
"id": ""
},
"_": "5xxxxxxxx"
}
]
},
"message": "This is a sample tts message",
"tts": {
"type": 4,
"rate": -1,
"repeat": 1,
"voice": "ymMale"
}
}
}))
.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"?>
<sms>
<status>0</status>
<message>SMS will be sent</message>
<shipment_id>xxxxxxx</shipment_id>
</sms>{
"status": 0,
"message": "SMS will be sent",
"shipment_id": "XXXXXXX"
}זהה לשליחת טקסט: status בערך 0, ו-shipment_id שמזהה את הקמפיין.
שגיאות
אותה קבוצה כמו ב-sms. דחיות ספציפיות לשליחה קולית מגיעות דרך קודי הוולידציה הרגילים — 2 כשחסר tts.type, 989 על גוף הודעה מחוץ למגבלת האורך.
הערות על השדות
מזהה המתקשר אינו שלכם
לא משנה איך source מוגדר, שיחת TTS מגיעה ממספר מערכת של TextMe. הנמענים לא יכולים לחזור אליו בשיחה, והוא לא יתאים למספר שאימתתם. אמרו מי מתקשר במשפט הראשון של ההודעה — האודיו הוא כל הזיהוי שהנמען מקבל.
כתיבה לאוזן
ההודעה מוקראת מילה במילה, ולכן טקסט שנקרא טוב על מסך לא בהכרח נשמע טוב:
- אייתו את מה שחשוב.
ORD-10052מוקרא כרצף תווים; "הזמנה אחת אפס אפס חמש שתיים" ברור יותר. - השמיטו כתובות URL ומצייני
[link-…]— אף אחד לא יכול להקליד כתובת ששמע, ומנגנון הקישורים המקוצרים חסר משמעות באודיו. - שמרו על קיצור.
repeatהוא פעמיים כברירת מחדל, ולכן הודעה ארוכה יוצרת שיחה ארוכה. - השתמשו ב-
rateבמידה. ערכים שליליים מאיטים את ההקראה, מה שעוזר עם מספרים ושמות לא מוכרים.
עלות ודיווח
שיחה קולית ו-SMS מחויבים בנפרד, ולכן type בערך 3 עולה על שניהם. יתרה מדווחת על היתרה בחשבון; דוחות מסירה מכסים את חלק ה-SMS של השליחה כרגיל.
איך מגלים אילו מספרים צריכים קול
אינכם צריכים לדעת מראש. שלחו כרגיל, ואז קראו את דוחות המסירה: יעדים שחוזרים עם 15 הם מכשירים מסוננים. שמרו את המידע הזה על איש הקשר והשתמשו ב-type בערך 4 עבורו בפעם הבאה — או פשוט השתמשו ב-type בערך 2 לכל הקהל ותנו לניתוב לעשות את העבודה.

