---
url: https://textme-docs.matat.io/he/endpoints/reports.md
description: >-
  dlr, dlrByDate ו-incoming: דוחות מסירה לפי מזהה חיצוני או לפי חלון זמן,
  וההודעות שאנשים שלחו למספרים שלכם.
---

# דוחות

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

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

כל שלושתן הן פעולות תשאול (polling). כדי לקבל את אותם נתונים ב-Push ברגע שהם נוצרים, רשמו [כתובת Push](./push.md).

## `dlr` דוחות מסירה לפי מזהה חיצוני

מחזירה מה קרה להודעות ספציפיות. אלה שסימנתם בתכונת `id` על `<phone>` בזמן [השליחה](./send.md#external-ids-and-delivery-reports).

### פרמטרים

| שם | סוג | תיאור | חובה |
|---|---|---|---|
| `dlr` | object | מכיל את כל האלמנטים האחרים. | ✔️ |
| `user` | object | מכיל את אלמנט המשתמש. | ✔️ |
| `username` | string | שם המשתמש של החשבון שבו אתם מזוהים במערכת. | ✔️ |
| `transactions` | object | מכיל את המזהים שאתם רוצים דוחות עבורם. יכול להחזיק כמה אלמנטי `external_id`. | ✔️ |
| `external_id` | string | המזהה שנשלח כתכונה בזמן שליחת ה-SMS. | ✔️ |
| `from` | string | תחילת החלון, בפורמט `dd/mm/yy hh:mm`. | ✔️ |
| `to` | string | סוף החלון, בפורמט `dd/mm/yy hh:mm`. | ✔️ |

::: warning שתי מגבלות חלות

* החלון בין `from` ל-`to` לא יעלה על **שבוע אחד**.
* עד **1,000** ערכי `external_id` לכל בקשה.
  :::

### דוגמת בקשה

::: code-group

```xml [XML]
<?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>
```

```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"
  }
}
```

```bash [cURL]
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"
  }
}'
```

```js [JavaScript]
// 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({
    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()

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

console.log(result)
```

```php [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' => [
        '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);

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

print_r($result);
```

```php [Laravel]
<?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();

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

logger()->info('TextMe', $result);
```

```python [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={
        "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()

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

print(result)
```

```go [Go]
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)
	}

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

	fmt.Println(result.Message)
}
```

```java [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 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());

        // גם שגיאה חוזרת כ-HTTP 200, לכן הסטטוס שבגוף התשובה הוא הקובע
        System.out.println(response.body());
    }
}
```

```csharp [C#]
// .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();

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

Console.WriteLine(result);
```

```ruby [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({
  "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)

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

pp result
```

```rust [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!({
          "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?;

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

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

:::

### תשובה

::: code-group

```xml [XML]
<?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>
```

```json [JSON]
{
  "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"
    }
  ]
}
```

:::

| שדה | סוג | תיאור |
|---|---|---|
| `status` | int | `0`. הדוח הופק. לא אומר דבר על המסירה. |
| `message` | string | הערה על הבקשה עצמה. |
| `transactions` | array | רשומה אחת לכל הודעה, כמתואר למטה. |

**`transactions[]`**

| שדה | סוג | תיאור |
|---|---|---|
| `external_id` | string | המזהה שנשלח בזמן השליחה. |
| `source` | string | השולח שההודעה יצאה בשמו. |
| `phone` | string | היעד. |
| `status` | string | סטטוס המסירה. [אוצר המילים של סטטוסי מסירה](../reference/dlr-statuses.md), לא קודי סטטוס הבקשה. |
| `he_message` / `message_he` | string | הסטטוס בעברית. ראו את ההערה על השמות למטה. |
| `en_message` | string | הסטטוס באנגלית, לדוגמה `Delivered`. |
| `date` | string | מועד רישום הסטטוס, `dd/mm/yy hh:mm`. |
| `shipment_id` | string | הקמפיין שההודעה שייכת לו. |
| `operator` | string | המפעיל שטיפל בה, לדוגמה `Telzar`. |

::: warning השדה בעברית נקרא אחרת בכל פורמט
XML מחזיר `<he_message>`; JSON מחזיר `message_he`. השדה באנגלית הוא `en_message` בשניהם. קראו את מה שהפרסר שלכם באמת מחפש. קליינט שנכתב לפי מבנה ה-XML לא ימצא דבר במבנה ה-JSON.
:::

### שגיאות

| Status | מתי |
|---|---|
| `2` | חסר `from`, `to` או `transactions`. |
| `3`, `10`, `11` | טוקן לא תקף, פג תוקף, או שייך ל-username אחר. |
| `511` | החשבון אינו רשאי לבצע את הפעולה. |

## `dlrByDate` דוחות מסירה לפי תאריך

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

### פרמטרים

| שם | סוג | תיאור | חובה |
|---|---|---|---|
| `dlrByDate` | object | מכיל את כל האלמנטים האחרים. | ✔️ |
| `user` | object | מכיל את אלמנט המשתמש. | ✔️ |
| `username` | string | שם המשתמש של החשבון שבו אתם מזוהים במערכת. | ✔️ |
| `transactions` | object | קיים אך אינו משמש לסינון. | ✔️ |
| `external_id` | string | שלחו את המחרוזת `null` כמות שהיא. | ✔️ |
| `from` | string | תחילת החלון, `dd/mm/yy hh:mm`. | ✔️ |
| `to` | string | סוף החלון, `dd/mm/yy hh:mm`. | ✔️ |

::: warning מגבלה אחת חלה
החלון בין `from` ל-`to` לא יעלה על **שבוע אחד**.
:::

### דוגמת בקשה

::: code-group

```xml [XML]
<?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>
```

```json [JSON]
{
  "dlrByDate": {
    "user": {
      "username": "Leeroy"
    },
    "transactions": {
      "external_id": "null"
    },
    "from": "04/09/22 00:00",
    "to": "04/09/22 23:59"
  }
}
```

```bash [cURL]
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"
  }
}'
```

```js [JavaScript]
// 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({
    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()

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

console.log(result)
```

```php [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' => [
        '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);

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

print_r($result);
```

```php [Laravel]
<?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();

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

logger()->info('TextMe', $result);
```

```python [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={
        "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()

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

print(result)
```

```go [Go]
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)
	}

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

	fmt.Println(result.Message)
}
```

```java [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 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());

        // גם שגיאה חוזרת כ-HTTP 200, לכן הסטטוס שבגוף התשובה הוא הקובע
        System.out.println(response.body());
    }
}
```

```csharp [C#]
// .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();

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

Console.WriteLine(result);
```

```ruby [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({
  "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)

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

pp result
```

```rust [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!({
          "dlrByDate": {
            "user": {
              "username": "Leeroy"
            },
            "transactions": {
              "external_id": "null"
            },
            "from": "04/09/22 00:00",
            "to": "04/09/22 23:59"
          }
        }))
        .send()
        .await?
        .json()
        .await?;

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

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

:::

### תשובה

::: code-group

```xml [XML]
<?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>
```

```json [JSON]
{
  "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"
    }
  ]
}
```

:::

זהה במבנה ל-`dlr`. כולל אלמנט השורש, שחוזר כ-`<dlr>` ולא כ-`<dlrByDate>`. הרשומות נושאות את אותם שדות.

### שגיאות

כמו ב-`dlr`, בתוספת `2` כשהחלון עובר שבוע.

## `incoming` הודעות נכנסות

הודעות שנשלחו *אל* המספרים שלכם. תשובות לקמפיין, מילות הסרה, או כל דבר אחר שנמען שולח בחזרה.

### פרמטרים

| שם | סוג | תיאור | חובה |
|---|---|---|---|
| `incoming` | object | מכיל את כל האלמנטים האחרים. | ✔️ |
| `user` | object | מכיל את אלמנט המשתמש. | ✔️ |
| `username` | string | שם המשתמש של החשבון שבו אתם מזוהים במערכת. | ✔️ |
| `from` | string | תחילת החלון, `dd/mm/yy hh:mm`. | ✔️ |
| `to` | string | סוף החלון, `dd/mm/yy hh:mm`. | ✔️ |

::: warning מגבלות החלון
טווח של **שבוע אחד** לכל בקשה, שמגיע עד **שנה** אחורה.
:::

### דוגמת בקשה

::: code-group

```xml [XML]
<?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>
```

```json [JSON]
{
  "incoming": {
    "user": {
      "username": "Leeroy"
    },
    "from": "01/11/22 00:00",
    "to": "01/11/22 23:59"
  }
}
```

```bash [cURL]
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"
  }
}'
```

```js [JavaScript]
// 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({
    incoming: {
      user: {
        username: 'Leeroy',
      },
      from: '01/11/22 00:00',
      to: '01/11/22 23:59',
    },
  }),
})

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]
<?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);

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

print_r($result);
```

```php [Laravel]
<?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();

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

logger()->info('TextMe', $result);
```

```python [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={
        "incoming": {
            "user": {
                "username": "Leeroy",
            },
            "from": "01/11/22 00:00",
            "to": "01/11/22 23:59",
        },
    },
)
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 [Go]
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)
	}

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

	fmt.Println(result.Message)
}
```

```java [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 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());

        // גם שגיאה חוזרת כ-HTTP 200, לכן הסטטוס שבגוף התשובה הוא הקובע
        System.out.println(response.body());
    }
}
```

```csharp [C#]
// .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();

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

Console.WriteLine(result);
```

```ruby [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({
  "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)

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

pp result
```

```rust [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!({
          "incoming": {
            "user": {
              "username": "Leeroy"
            },
            "from": "01/11/22 00:00",
            "to": "01/11/22 23:59"
          }
        }))
        .send()
        .await?
        .json()
        .await?;

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

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

:::

### תשובה

::: code-group

```xml [XML]
<?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>
```

```json [JSON]
{
  "status": 0,
  "message": "",
  "transactions": [
    {
      "source": "test",
      "destination": "9725XXXXXXX",
      "message": "test",
      "date": "01/05/23 00:00"
    }
  ]
}
```

:::

**`transactions[]`**

| שדה | סוג | תיאור |
|---|---|---|
| `source` | string | המספר ששלח את ההודעה. |
| `destination` | string | המספר שלכם שאליו היא הגיעה. |
| `message` | string | הטקסט שהתקבל. |
| `date` | string | מועד ההגעה, `dd/mm/yy hh:mm`. |

### שגיאות

כמו ב-`dlr`.

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

### בחירה בין `dlr` ל-`dlrByDate`

| | `dlr` | `dlrByDate` |
|---|---|---|
| דורש מזהים חיצוניים | כן | לא |
| עונה על "מה קרה להודעה *הזו*" | כן | רק בסריקה |
| עונה על "איך עבר אתמול" | מסורבל | כן |
| תקרה לכל בקשה | 1,000 מזהים | מה שהחלון מכיל |

לתעבורה טרנזקציונית, הגדירו מזהה על כל יעד והשתמשו ב-`dlr`. לדיווח על קמפיינים, `dlrByDate` על החלון שבו הקמפיין רץ פשוט יותר.

### הדוחות אינם מיידיים

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

תשאלו לפי לוח זמנים על חלון שנגרר אחרי ההווה, או השתמשו ב-[Push API](./push.md) והפסיקו לתשאל.

### עימוד

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