Skip to content

TextMe APISMS, הודעות קוליות וקמפיינים בנקודת קצה אחת

שליחת הודעות, פרסונליזציה מתוך רשימות תפוצה שמורות בשרת, תזמון וביטול קמפיינים, והתאמת נתוני מסירה. הכול דרך בקשת POST אחת מאומתת שמדברת גם XML וגם JSON.

TextMeTextMe

קריאה אחת עד ההודעה הראשונה

xml
<?xml version="1.0" encoding="UTF-8"?>
<sms>
    <user>
        <username>Leeroy</username>
    </user>
    <source>DemoAPI</source>
    <destinations>
        <phone>5xxxxxxxx</phone>
    </destinations>
    <message>This is a sample message</message>
</sms>
json
{
  "sms": {
    "user": {
      "username": "Leeroy"
    },
    "source": "DemoAPI",
    "destinations": {
      "phone": "5xxxxxxxx"
    },
    "message": "This is a sample message"
  }
}
bash
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": {
      "phone": "5xxxxxxxx"
    },
    "message": "This is a sample message"
  }
}'
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({
    sms: {
      user: {
        username: 'Leeroy',
      },
      source: 'DemoAPI',
      destinations: {
        phone: '5xxxxxxxx',
      },
      message: 'This is a sample message',
    },
  }),
})

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' => [
        'sms' => [
            'user' => [
                'username' => 'Leeroy',
            ],
            'source' => 'DemoAPI',
            'destinations' => [
                'phone' => '5xxxxxxxx',
            ],
            'message' => 'This is a sample message',
        ],
    ],
]);

$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', [
        'sms' => [
            'user' => [
                'username' => 'Leeroy',
            ],
            'source' => 'DemoAPI',
            'destinations' => [
                'phone' => '5xxxxxxxx',
            ],
            'message' => 'This is a sample message',
        ],
    ])
    ->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={
        "sms": {
            "user": {
                "username": "Leeroy",
            },
            "source": "DemoAPI",
            "destinations": {
                "phone": "5xxxxxxxx",
            },
            "message": "This is a sample message",
        },
    },
)
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{
		"sms": map[string]any{
			"user": map[string]any{
				"username": "Leeroy",
			},
			"source": "DemoAPI",
			"destinations": map[string]any{
				"phone": "5xxxxxxxx",
			},
			"message": "This is a sample message",
		},
	})

	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 TextMeSendMinimal {
    public static void main(String[] args) throws Exception {
        String body = """
            {
              "sms": {
                "user": {
                  "username": "Leeroy"
                },
                "source": "DemoAPI",
                "destinations": {
                  "phone": "5xxxxxxxx"
                },
                "message": "This is a sample message"
              }
            }
            """;

        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 = """
    {
      "sms": {
        "user": {
          "username": "Leeroy"
        },
        "source": "DemoAPI",
        "destinations": {
          "phone": "5xxxxxxxx"
        },
        "message": "This is a sample message"
      }
    }
    """;

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({
  "sms" => {
    "user" => {
      "username" => "Leeroy",
    },
    "source" => "DemoAPI",
    "destinations" => {
      "phone" => "5xxxxxxxx",
    },
    "message" => "This is a sample message",
  },
})

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!({
          "sms": {
            "user": {
              "username": "Leeroy"
            },
            "source": "DemoAPI",
            "destinations": {
              "phone": "5xxxxxxxx"
            },
            "message": "This is a sample message"
          }
        }))
        .send()
        .await?
        .json()
        .await?;

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

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

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

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