Reports
Three read operations: delivery reports by your own external id, delivery reports by date window, and messages people sent to you.
POST https://my.textme.co.il/apiAll three are polling operations. To have the same data pushed to you as it arrives instead, register a push URL.
dlr Delivery reports by external id
Returns what happened to specific messages. The ones you tagged with an id attribute on <phone> when sending.
Parameters
| Name | Type | Description | Required |
|---|---|---|---|
dlr | object | Contains all other elements. | ✔️ |
user | object | Contains the user element. | ✔️ |
username | string | The username of the account by which you are recognized in the system. | ✔️ |
transactions | object | Contains the ids you want reports for. May hold multiple external_id elements. | ✔️ |
external_id | string | The id you sent as an attribute when submitting the SMS. | ✔️ |
from | string | Start of the window, formatted dd/mm/yy hh:mm. | ✔️ |
to | string | End of the window, formatted dd/mm/yy hh:mm. | ✔️ |
Two limits apply
- The window between
fromandtomay not exceed one week. - At most 1,000
external_idvalues per request.
Request example
<?xml version="1.0" encoding="UTF-8"?>
<dlr>
<user>
<username>Leeroy</username>
</user>
<transactions>
<external_id>some id 1</external_id>
<external_id>some id 2</external_id>
</transactions>
<from>01/01/14 00:00</from>
<to>01/01/14 23:59</to>
</dlr>{
"dlr": {
"user": {
"username": "Leeroy"
},
"transactions": {
"external_id": [
"some id 1",
"some id 2"
]
},
"from": "01/01/14 00:00",
"to": "01/01/14 23:59"
}
}curl --location 'https://my.textme.co.il/api' \
--header "Authorization: Bearer $TEXTME_API_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
"dlr": {
"user": {
"username": "Leeroy"
},
"transactions": {
"external_id": [
"some id 1",
"some id 2"
]
},
"from": "01/01/14 00:00",
"to": "01/01/14 23:59"
}
}'// Node.js 18+ or any modern browser. No dependencies
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({
dlr: {
user: {
username: 'Leeroy',
},
transactions: {
external_id: [
'some id 1',
'some id 2',
],
},
from: '01/01/14 00:00',
to: '01/01/14 23:59',
},
}),
})
const result = await response.json()
// Errors arrive as HTTP 200 too, so the payload status is what counts
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' => [
'dlr' => [
'user' => [
'username' => 'Leeroy',
],
'transactions' => [
'external_id' => [
'some id 1',
'some id 2',
],
],
'from' => '01/01/14 00:00',
'to' => '01/01/14 23:59',
],
],
]);
$result = json_decode($response->getBody()->getContents(), true);
// Errors arrive as HTTP 200 too, so the payload status is what counts
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', [
'dlr' => [
'user' => [
'username' => 'Leeroy',
],
'transactions' => [
'external_id' => [
'some id 1',
'some id 2',
],
],
'from' => '01/01/14 00:00',
'to' => '01/01/14 23:59',
],
])
->throw()
->json();
// Errors arrive as HTTP 200 too, so the payload status is what counts
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={
"dlr": {
"user": {
"username": "Leeroy",
},
"transactions": {
"external_id": [
"some id 1",
"some id 2",
],
},
"from": "01/01/14 00:00",
"to": "01/01/14 23:59",
},
},
)
response.raise_for_status()
result = response.json()
# Errors arrive as HTTP 200 too, so the payload status is what counts
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{
"dlr": map[string]any{
"user": map[string]any{
"username": "Leeroy",
},
"transactions": map[string]any{
"external_id": []any{
"some id 1",
"some id 2",
},
},
"from": "01/01/14 00:00",
"to": "01/01/14 23:59",
},
})
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)
}
// Errors arrive as HTTP 200 too, so the payload status is what counts
if result.Status.String() != "0" {
panic(fmt.Sprintf("TextMe %s: %s", result.Status, result.Message))
}
fmt.Println(result.Message)
}// Java 17+ using java.net.http. No dependencies (parse with Jackson/Gson)
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class TextMeDlr {
public static void main(String[] args) throws Exception {
String body = """
{
"dlr": {
"user": {
"username": "Leeroy"
},
"transactions": {
"external_id": [
"some id 1",
"some id 2"
]
},
"from": "01/01/14 00:00",
"to": "01/01/14 23:59"
}
}
""";
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());
// Errors arrive as HTTP 200 too, so the payload status is what counts
System.out.println(response.body());
}
}// .NET 8+ using System.Net.Http
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
var payload = """
{
"dlr": {
"user": {
"username": "Leeroy"
},
"transactions": {
"external_id": [
"some id 1",
"some id 2"
]
},
"from": "01/01/14 00:00",
"to": "01/01/14 23:59"
}
}
""";
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();
// Errors arrive as HTTP 200 too, so the payload status is what counts
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({
"dlr" => {
"user" => {
"username" => "Leeroy",
},
"transactions" => {
"external_id" => [
"some id 1",
"some id 2",
],
},
"from" => "01/01/14 00:00",
"to" => "01/01/14 23:59",
},
})
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
result = JSON.parse(response.body)
# Errors arrive as HTTP 200 too, so the payload status is what counts
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!({
"dlr": {
"user": {
"username": "Leeroy"
},
"transactions": {
"external_id": [
"some id 1",
"some id 2"
]
},
"from": "01/01/14 00:00",
"to": "01/01/14 23:59"
}
}))
.send()
.await?
.json()
.await?;
// Errors arrive as HTTP 200 too, so the payload status is what counts
if result["status"] != 0 {
return Err(format!("TextMe {}: {}", result["status"], result["message"]).into());
}
println!("{result}");
Ok(())
}Response
<?xml version="1.0" encoding="UTF-8"?>
<dlr>
<status>0</status>
<message></message>
<transactions>
<transaction>
<external_id>1391438285</external_id>
<status>102</status>
<he_message>הגיע ליעד</he_message>
<en_message>Delivered</en_message>
<date>03/02/14 16:38</date>
<shipment_id>12345678</shipment_id>
</transaction>
</transactions>
</dlr>{
"status": 0,
"message": "all is well!",
"transactions": [
{
"external_id": "1391438285",
"source": "Test",
"phone": "5XXXXXXXX",
"status": "102",
"message_he": "הגיע ליעד",
"en_message": "Delivered",
"shipment_id": "XXXXXXX",
"date": "02/05/23 10:22",
"operator": "Telzar"
}
]
}| Field | Type | Description |
|---|---|---|
status | int | 0. The report was produced. Says nothing about delivery. |
message | string | Note about the request itself. |
transactions | array | One entry per message, described below. |
transactions[]
| Field | Type | Description |
|---|---|---|
external_id | string | The id you supplied when sending. |
source | string | The sender the message went out as. |
phone | string | The destination. |
status | string | The delivery status. The DLR status vocabulary, not the request status codes. |
he_message / message_he | string | The status in Hebrew. See the naming note below. |
en_message | string | The status in English, e.g. Delivered. |
date | string | When the status was recorded, dd/mm/yy hh:mm. |
shipment_id | string | The campaign the message belonged to. |
operator | string | The carrier that handled it, e.g. Telzar. |
The Hebrew field is named differently in each encoding
XML returns <he_message>; JSON returns message_he. The English one is en_message in both. Read whichever your parser is actually looking at. A client written against the XML shape will silently find nothing in the JSON one.
Errors
| Status | When |
|---|---|
2 | from, to or transactions is missing. |
3, 10, 11 | Token invalid, expired, or belonging to another username. |
511 | The account is not entitled to this operation. |
dlrByDate Delivery reports by date
The same report without needing ids: everything the account sent inside a time window. Use it when you did not set external ids, or when reconciling in bulk rather than looking up a specific message.
Parameters
| Name | Type | Description | Required |
|---|---|---|---|
dlrByDate | object | Contains all other elements. | ✔️ |
user | object | Contains the user element. | ✔️ |
username | string | The username of the account by which you are recognized in the system. | ✔️ |
transactions | object | Present but not used for filtering. | ✔️ |
external_id | string | Send the literal string null. | ✔️ |
from | string | Start of the window, dd/mm/yy hh:mm. | ✔️ |
to | string | End of the window, dd/mm/yy hh:mm. | ✔️ |
One limit applies
The window between from and to may not exceed one week.
Request example
<?xml version="1.0" encoding="UTF-8"?>
<dlrByDate>
<user>
<username>Leeroy</username>
</user>
<transactions>
<external_id>null</external_id>
</transactions>
<from>04/09/22 00:00</from>
<to>04/09/22 23:59</to>
</dlrByDate>{
"dlrByDate": {
"user": {
"username": "Leeroy"
},
"transactions": {
"external_id": "null"
},
"from": "04/09/22 00:00",
"to": "04/09/22 23:59"
}
}curl --location 'https://my.textme.co.il/api' \
--header "Authorization: Bearer $TEXTME_API_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
"dlrByDate": {
"user": {
"username": "Leeroy"
},
"transactions": {
"external_id": "null"
},
"from": "04/09/22 00:00",
"to": "04/09/22 23:59"
}
}'// Node.js 18+ or any modern browser. No dependencies
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({
dlrByDate: {
user: {
username: 'Leeroy',
},
transactions: {
external_id: 'null',
},
from: '04/09/22 00:00',
to: '04/09/22 23:59',
},
}),
})
const result = await response.json()
// Errors arrive as HTTP 200 too, so the payload status is what counts
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' => [
'dlrByDate' => [
'user' => [
'username' => 'Leeroy',
],
'transactions' => [
'external_id' => 'null',
],
'from' => '04/09/22 00:00',
'to' => '04/09/22 23:59',
],
],
]);
$result = json_decode($response->getBody()->getContents(), true);
// Errors arrive as HTTP 200 too, so the payload status is what counts
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', [
'dlrByDate' => [
'user' => [
'username' => 'Leeroy',
],
'transactions' => [
'external_id' => 'null',
],
'from' => '04/09/22 00:00',
'to' => '04/09/22 23:59',
],
])
->throw()
->json();
// Errors arrive as HTTP 200 too, so the payload status is what counts
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={
"dlrByDate": {
"user": {
"username": "Leeroy",
},
"transactions": {
"external_id": "null",
},
"from": "04/09/22 00:00",
"to": "04/09/22 23:59",
},
},
)
response.raise_for_status()
result = response.json()
# Errors arrive as HTTP 200 too, so the payload status is what counts
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{
"dlrByDate": map[string]any{
"user": map[string]any{
"username": "Leeroy",
},
"transactions": map[string]any{
"external_id": "null",
},
"from": "04/09/22 00:00",
"to": "04/09/22 23:59",
},
})
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)
}
// Errors arrive as HTTP 200 too, so the payload status is what counts
if result.Status.String() != "0" {
panic(fmt.Sprintf("TextMe %s: %s", result.Status, result.Message))
}
fmt.Println(result.Message)
}// Java 17+ using java.net.http. No dependencies (parse with Jackson/Gson)
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class TextMeDlrByDate {
public static void main(String[] args) throws Exception {
String body = """
{
"dlrByDate": {
"user": {
"username": "Leeroy"
},
"transactions": {
"external_id": "null"
},
"from": "04/09/22 00:00",
"to": "04/09/22 23:59"
}
}
""";
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());
// Errors arrive as HTTP 200 too, so the payload status is what counts
System.out.println(response.body());
}
}// .NET 8+ using System.Net.Http
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
var payload = """
{
"dlrByDate": {
"user": {
"username": "Leeroy"
},
"transactions": {
"external_id": "null"
},
"from": "04/09/22 00:00",
"to": "04/09/22 23:59"
}
}
""";
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();
// Errors arrive as HTTP 200 too, so the payload status is what counts
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({
"dlrByDate" => {
"user" => {
"username" => "Leeroy",
},
"transactions" => {
"external_id" => "null",
},
"from" => "04/09/22 00:00",
"to" => "04/09/22 23:59",
},
})
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
result = JSON.parse(response.body)
# Errors arrive as HTTP 200 too, so the payload status is what counts
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!({
"dlrByDate": {
"user": {
"username": "Leeroy"
},
"transactions": {
"external_id": "null"
},
"from": "04/09/22 00:00",
"to": "04/09/22 23:59"
}
}))
.send()
.await?
.json()
.await?;
// Errors arrive as HTTP 200 too, so the payload status is what counts
if result["status"] != 0 {
return Err(format!("TextMe {}: {}", result["status"], result["message"]).into());
}
println!("{result}");
Ok(())
}Response
<?xml version="1.0" encoding="UTF-8"?>
<dlr>
<status>0</status>
<message></message>
<transactions>
<transaction>
<external_id>1391438285</external_id>
<status>102</status>
<he_message>הגיע ליעד</he_message>
<en_message>Delivered</en_message>
<date>03/02/14 16:38</date>
<shipment_id>12345678</shipment_id>
</transaction>
</transactions>
</dlr>{
"status": 0,
"message": "all is well!",
"transactions": [
{
"external_id": "1391438285",
"source": "Test",
"phone": "5XXXXXXXX",
"status": "102",
"message_he": "הגיע ליעד",
"en_message": "Delivered",
"shipment_id": "XXXXXXX",
"date": "02/05/23 10:22",
"operator": "Telzar"
}
]
}Identical in shape to dlr, including the root element, which comes back as <dlr> rather than <dlrByDate>. Transactions carry the same fields.
Errors
Same as dlr, plus 2 when the window exceeds a week.
incoming Incoming messages
Messages sent to your numbers. Replies to a campaign, opt-out keywords, or anything else a recipient texts back.
Parameters
| Name | Type | Description | Required |
|---|---|---|---|
incoming | object | Contains all other elements. | ✔️ |
user | object | Contains the user element. | ✔️ |
username | string | The username of the account by which you are recognized in the system. | ✔️ |
from | string | Start of the window, dd/mm/yy hh:mm. | ✔️ |
to | string | End of the window, dd/mm/yy hh:mm. | ✔️ |
Window limits
A range of one week per request, reaching up to one year back.
Request example
<?xml version="1.0" encoding="UTF-8"?>
<incoming>
<user>
<username>Leeroy</username>
</user>
<from>01/11/22 00:00</from>
<to>01/11/22 23:59</to>
</incoming>{
"incoming": {
"user": {
"username": "Leeroy"
},
"from": "01/11/22 00:00",
"to": "01/11/22 23:59"
}
}curl --location 'https://my.textme.co.il/api' \
--header "Authorization: Bearer $TEXTME_API_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
"incoming": {
"user": {
"username": "Leeroy"
},
"from": "01/11/22 00:00",
"to": "01/11/22 23:59"
}
}'// Node.js 18+ or any modern browser. No dependencies
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({
incoming: {
user: {
username: 'Leeroy',
},
from: '01/11/22 00:00',
to: '01/11/22 23:59',
},
}),
})
const result = await response.json()
// Errors arrive as HTTP 200 too, so the payload status is what counts
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' => [
'incoming' => [
'user' => [
'username' => 'Leeroy',
],
'from' => '01/11/22 00:00',
'to' => '01/11/22 23:59',
],
],
]);
$result = json_decode($response->getBody()->getContents(), true);
// Errors arrive as HTTP 200 too, so the payload status is what counts
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', [
'incoming' => [
'user' => [
'username' => 'Leeroy',
],
'from' => '01/11/22 00:00',
'to' => '01/11/22 23:59',
],
])
->throw()
->json();
// Errors arrive as HTTP 200 too, so the payload status is what counts
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={
"incoming": {
"user": {
"username": "Leeroy",
},
"from": "01/11/22 00:00",
"to": "01/11/22 23:59",
},
},
)
response.raise_for_status()
result = response.json()
# Errors arrive as HTTP 200 too, so the payload status is what counts
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{
"incoming": map[string]any{
"user": map[string]any{
"username": "Leeroy",
},
"from": "01/11/22 00:00",
"to": "01/11/22 23:59",
},
})
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)
}
// Errors arrive as HTTP 200 too, so the payload status is what counts
if result.Status.String() != "0" {
panic(fmt.Sprintf("TextMe %s: %s", result.Status, result.Message))
}
fmt.Println(result.Message)
}// Java 17+ using java.net.http. No dependencies (parse with Jackson/Gson)
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class TextMeIncoming {
public static void main(String[] args) throws Exception {
String body = """
{
"incoming": {
"user": {
"username": "Leeroy"
},
"from": "01/11/22 00:00",
"to": "01/11/22 23:59"
}
}
""";
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());
// Errors arrive as HTTP 200 too, so the payload status is what counts
System.out.println(response.body());
}
}// .NET 8+ using System.Net.Http
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
var payload = """
{
"incoming": {
"user": {
"username": "Leeroy"
},
"from": "01/11/22 00:00",
"to": "01/11/22 23:59"
}
}
""";
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();
// Errors arrive as HTTP 200 too, so the payload status is what counts
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({
"incoming" => {
"user" => {
"username" => "Leeroy",
},
"from" => "01/11/22 00:00",
"to" => "01/11/22 23:59",
},
})
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
result = JSON.parse(response.body)
# Errors arrive as HTTP 200 too, so the payload status is what counts
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!({
"incoming": {
"user": {
"username": "Leeroy"
},
"from": "01/11/22 00:00",
"to": "01/11/22 23:59"
}
}))
.send()
.await?
.json()
.await?;
// Errors arrive as HTTP 200 too, so the payload status is what counts
if result["status"] != 0 {
return Err(format!("TextMe {}: {}", result["status"], result["message"]).into());
}
println!("{result}");
Ok(())
}Response
<?xml version="1.0" encoding="UTF-8"?>
<incoming>
<status>0</status>
<message></message>
<transactions>
<transaction>
<source>05********</source>
<destination>05********</destination>
<message>This is a sample message</message>
<date>03/12/14 16:38</date>
</transaction>
</transactions>
</incoming>{
"status": 0,
"message": "",
"transactions": [
{
"source": "test",
"destination": "9725XXXXXXX",
"message": "test",
"date": "01/05/23 00:00"
}
]
}transactions[]
| Field | Type | Description |
|---|---|---|
source | string | The number that sent the message. |
destination | string | The number of yours it arrived on. |
message | string | The text received. |
date | string | When it arrived, dd/mm/yy hh:mm. |
Errors
Same as dlr.
Field notes
Choosing between dlr and dlrByDate
dlr | dlrByDate | |
|---|---|---|
| Needs external ids | yes | no |
| Answers "what happened to this message" | yes | only by scanning |
| Answers "how did yesterday go" | awkward | yes |
| Per-request ceiling | 1,000 ids | whatever the window holds |
For transactional traffic, set an id on every destination and use dlr. For campaign reporting, dlrByDate over the window the campaign ran is simpler.
Reports are not immediate
A delivery status is produced when the carrier reports back, which can be seconds or minutes after the send, and for an unreachable handset, considerably longer. Polling one second after sending will mostly return nothing, or an unconfirmed status.
Poll on a schedule over a window that trails the present, or use the push API and stop polling.
Paging
There is no page cursor. The window is the pagination: to walk a month of history under the one-week ceiling, issue four or five sequential requests, each covering its own slice. Keep the slices from overlapping and you will not have to deduplicate.

