---
url: https://textme-docs.matat.io/endpoints/blacklist.md
description: >-
  blacklist, addNumBL and rmNumBL: list, block and unblock numbers. Note that
  946 and 944 are success codes, not failures.
---

# Blocklist

The account's list of numbers that must not be messaged. Numbers land on it when a recipient opts out, and you can add or remove entries yourself.

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

::: danger These two operations report success with a non-zero status
`addNumBL` answers **`946`** when it worked, and `rmNumBL` answers **`944`** when some of the given numbers were not on the list. Neither is `0`. Client code that treats "status ≠ 0" as failure will report working calls as broken, special-case them.
:::

## `blacklist` List blocked numbers

Returns the numbers blocked within a date range, with the date each was blocked.

### Parameters

| Name | Type | Description | Required |
|---|---|---|---|
| `blacklist` | 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 range, formatted `dd/mm/yy hh:mm`. | ✔️ |
| `to` | string | End of the range, formatted `dd/mm/yy hh:mm`. | ✔️ |

### Request example

::: code-group

```xml [XML]
<?xml version="1.0" encoding="UTF-8"?>
<blacklist>
    <user>
        <username>XXXXXX</username>
    </user>
    <from>02/06/22 00:00</from>
    <to>15/12/22 23:59</to>
</blacklist>
```

```json [JSON]
{
  "blacklist": {
    "user": {
      "username": "XXXXXX"
    },
    "from": "02/06/22 00:00",
    "to": "15/12/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 '{
  "blacklist": {
    "user": {
      "username": "XXXXXX"
    },
    "from": "02/06/22 00:00",
    "to": "15/12/22 23:59"
  }
}'
```

```js [JavaScript]
// 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({
    blacklist: {
      user: {
        username: 'XXXXXX',
      },
      from: '02/06/22 00:00',
      to: '15/12/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 [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' => [
        'blacklist' => [
            'user' => [
                'username' => 'XXXXXX',
            ],
            'from' => '02/06/22 00:00',
            'to' => '15/12/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 [Laravel]
<?php

use Illuminate\Support\Facades\Http;

$result = Http::withToken(config('services.textme.token'))
    ->acceptJson()
    ->post('https://my.textme.co.il/api', [
        'blacklist' => [
            'user' => [
                'username' => 'XXXXXX',
            ],
            'from' => '02/06/22 00:00',
            'to' => '15/12/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);
```

```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={
        "blacklist": {
            "user": {
                "username": "XXXXXX",
            },
            "from": "02/06/22 00:00",
            "to": "15/12/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)
```

```go [Go]
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"os"
)

func main() {
	payload, _ := json.Marshal(map[string]any{
		"blacklist": map[string]any{
			"user": map[string]any{
				"username": "XXXXXX",
			},
			"from": "02/06/22 00:00",
			"to": "15/12/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 [Java]
// 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 TextMeBlacklistGet {
    public static void main(String[] args) throws Exception {
        String body = """
            {
              "blacklist": {
                "user": {
                  "username": "XXXXXX"
                },
                "from": "02/06/22 00:00",
                "to": "15/12/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());
    }
}
```

```csharp [C#]
// .NET 8+ using System.Net.Http
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;

var payload = """
    {
      "blacklist": {
        "user": {
          "username": "XXXXXX"
        },
        "from": "02/06/22 00:00",
        "to": "15/12/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);
```

```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({
  "blacklist" => {
    "user" => {
      "username" => "XXXXXX",
    },
    "from" => "02/06/22 00:00",
    "to" => "15/12/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
```

```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!({
          "blacklist": {
            "user": {
              "username": "XXXXXX"
            },
            "from": "02/06/22 00:00",
            "to": "15/12/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

::: code-group

```xml [XML]
<?xml version="1.0" encoding="UTF-8"?>
<blacklist>
    <status>0</status>
    <message></message>
    <transactions>
        <transaction>
            <phone>5********</phone>
            <date>03/12/14 16:38</date>
        </transaction>
    </transactions>
</blacklist>
```

```json [JSON]
{
  "status": 0,
  "message": "",
  "transactions": [
    {
      "phone": "5********",
      "date": "22/08/22 11:00"
    },
    {
      "phone": "5********",
      "date": "23/11/22 12:37"
    }
  ]
}
```

:::

| Field | Type | Description |
|---|---|---|
| `status` | int | `0` on success. |
| `transactions` | array | One entry per blocked number. |
| `transactions[].phone` | string | The blocked number, partly masked. |
| `transactions[].date` | string | When it was blocked, `dd/mm/yy hh:mm`. |

::: tip Numbers come back masked
Entries are returned as `5********` rather than in full. The list is auditable (how many opted out, and when) but it is not a source you can diff against your own contact database. Rely on the API to enforce the blocklist at send time instead.
:::

### Errors

| Status | When |
|---|---|
| `2` | `from` or `to` missing. |
| `3`, `10`, `11` | Token invalid, expired, or belonging to another username. |
| `511` | The account is not entitled to this operation. |

## `addNumBL` Block numbers

Adds numbers to the blocklist. Sends to them are suppressed from that point on.

### Parameters

| Name | Type | Description | Required |
|---|---|---|---|
| `addNumBL` | 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. | ✔️ |
| `phones` | object | Contains all phone elements. | ✔️ |
| `phone` | string | A number to block. Repeatable. | ✔️ |

### Request example

::: code-group

```xml [XML]
<?xml version="1.0" encoding="UTF-8"?>
<addNumBL>
    <user>
        <username>xxxxxx</username>
    </user>
    <phones>
        <phone>5********</phone>
        <phone>05********</phone>
    </phones>
</addNumBL>
```

```json [JSON]
{
  "addNumBL": {
    "user": {
      "username": "xxxxxx"
    },
    "phones": {
      "phone": [
        "5********",
        "05********"
      ]
    }
  }
}
```

```bash [cURL]
curl --location 'https://my.textme.co.il/api' \
--header "Authorization: Bearer $TEXTME_API_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
  "addNumBL": {
    "user": {
      "username": "xxxxxx"
    },
    "phones": {
      "phone": [
        "5********",
        "05********"
      ]
    }
  }
}'
```

```js [JavaScript]
// 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({
    addNumBL: {
      user: {
        username: 'xxxxxx',
      },
      phones: {
        phone: [
          '5********',
          '05********',
        ],
      },
    },
  }),
})

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 [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' => [
        'addNumBL' => [
            'user' => [
                'username' => 'xxxxxx',
            ],
            'phones' => [
                'phone' => [
                    '5********',
                    '05********',
                ],
            ],
        ],
    ],
]);

$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 [Laravel]
<?php

use Illuminate\Support\Facades\Http;

$result = Http::withToken(config('services.textme.token'))
    ->acceptJson()
    ->post('https://my.textme.co.il/api', [
        'addNumBL' => [
            'user' => [
                'username' => 'xxxxxx',
            ],
            'phones' => [
                'phone' => [
                    '5********',
                    '05********',
                ],
            ],
        ],
    ])
    ->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);
```

```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={
        "addNumBL": {
            "user": {
                "username": "xxxxxx",
            },
            "phones": {
                "phone": [
                    "5********",
                    "05********",
                ],
            },
        },
    },
)
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)
```

```go [Go]
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"os"
)

func main() {
	payload, _ := json.Marshal(map[string]any{
		"addNumBL": map[string]any{
			"user": map[string]any{
				"username": "xxxxxx",
			},
			"phones": map[string]any{
				"phone": []any{
					"5********",
					"05********",
				},
			},
		},
	})

	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 [Java]
// 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 TextMeBlacklistAdd {
    public static void main(String[] args) throws Exception {
        String body = """
            {
              "addNumBL": {
                "user": {
                  "username": "xxxxxx"
                },
                "phones": {
                  "phone": [
                    "5********",
                    "05********"
                  ]
                }
              }
            }
            """;

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

```csharp [C#]
// .NET 8+ using System.Net.Http
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;

var payload = """
    {
      "addNumBL": {
        "user": {
          "username": "xxxxxx"
        },
        "phones": {
          "phone": [
            "5********",
            "05********"
          ]
        }
      }
    }
    """;

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);
```

```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({
  "addNumBL" => {
    "user" => {
      "username" => "xxxxxx",
    },
    "phones" => {
      "phone" => [
        "5********",
        "05********",
      ],
    },
  },
})

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

```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!({
          "addNumBL": {
            "user": {
              "username": "xxxxxx"
            },
            "phones": {
              "phone": [
                "5********",
                "05********"
              ]
            }
          }
        }))
        .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

::: code-group

```xml [XML]
<?xml version="1.0" encoding="UTF-8"?>
<addNumBL>
    <status>946</status>
    <message>number has successfully added to blacklist</message>
</addNumBL>
```

```json [JSON]
{
  "status": 946,
  "message": "number has successfully added to blacklist"
}
```

:::

| Status | Meaning |
|---|---|
| `946` | **Success.** The numbers were added. |

### Errors

| Status | When |
|---|---|
| `2` | `phones` is missing or empty. |
| `9` | A number is too short or too long. |
| `511` | The account is not entitled to this operation. |

## `rmNumBL` Unblock numbers

Removes numbers from the blocklist. A `reason` is mandatory. This operation re-opens a channel someone previously closed, so the audit trail is required rather than optional.

### Parameters

| Name | Type | Description | Required |
|---|---|---|---|
| `rmNumBL` | 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. | ✔️ |
| `phones` | object | Contains all phone elements. | ✔️ |
| `phone` | string | A number to unblock. Repeatable. | ✔️ |
| `reason` | string | Why the number is being unblocked. | ✔️ |

### Request example

::: code-group

```xml [XML]
<?xml version="1.0" encoding="UTF-8"?>
<rmNumBL>
    <user>
        <username>xxxxxx</username>
    </user>
    <phones>
        <phone>5********</phone>
        <phone>05********</phone>
    </phones>
    <reason>Customer opted back in by phone</reason>
</rmNumBL>
```

```json [JSON]
{
  "rmNumBL": {
    "user": {
      "username": "xxxxxx"
    },
    "phones": {
      "phone": [
        "5********",
        "05********"
      ]
    },
    "reason": "Customer opted back in by phone"
  }
}
```

```bash [cURL]
curl --location 'https://my.textme.co.il/api' \
--header "Authorization: Bearer $TEXTME_API_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
  "rmNumBL": {
    "user": {
      "username": "xxxxxx"
    },
    "phones": {
      "phone": [
        "5********",
        "05********"
      ]
    },
    "reason": "Customer opted back in by phone"
  }
}'
```

```js [JavaScript]
// 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({
    rmNumBL: {
      user: {
        username: 'xxxxxx',
      },
      phones: {
        phone: [
          '5********',
          '05********',
        ],
      },
      reason: 'Customer opted back in by phone',
    },
  }),
})

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 [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' => [
        'rmNumBL' => [
            'user' => [
                'username' => 'xxxxxx',
            ],
            'phones' => [
                'phone' => [
                    '5********',
                    '05********',
                ],
            ],
            'reason' => 'Customer opted back in by phone',
        ],
    ],
]);

$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 [Laravel]
<?php

use Illuminate\Support\Facades\Http;

$result = Http::withToken(config('services.textme.token'))
    ->acceptJson()
    ->post('https://my.textme.co.il/api', [
        'rmNumBL' => [
            'user' => [
                'username' => 'xxxxxx',
            ],
            'phones' => [
                'phone' => [
                    '5********',
                    '05********',
                ],
            ],
            'reason' => 'Customer opted back in by phone',
        ],
    ])
    ->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);
```

```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={
        "rmNumBL": {
            "user": {
                "username": "xxxxxx",
            },
            "phones": {
                "phone": [
                    "5********",
                    "05********",
                ],
            },
            "reason": "Customer opted back in by phone",
        },
    },
)
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)
```

```go [Go]
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"os"
)

func main() {
	payload, _ := json.Marshal(map[string]any{
		"rmNumBL": map[string]any{
			"user": map[string]any{
				"username": "xxxxxx",
			},
			"phones": map[string]any{
				"phone": []any{
					"5********",
					"05********",
				},
			},
			"reason": "Customer opted back in by phone",
		},
	})

	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 [Java]
// 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 TextMeBlacklistRemove {
    public static void main(String[] args) throws Exception {
        String body = """
            {
              "rmNumBL": {
                "user": {
                  "username": "xxxxxx"
                },
                "phones": {
                  "phone": [
                    "5********",
                    "05********"
                  ]
                },
                "reason": "Customer opted back in by phone"
              }
            }
            """;

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

```csharp [C#]
// .NET 8+ using System.Net.Http
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;

var payload = """
    {
      "rmNumBL": {
        "user": {
          "username": "xxxxxx"
        },
        "phones": {
          "phone": [
            "5********",
            "05********"
          ]
        },
        "reason": "Customer opted back in by phone"
      }
    }
    """;

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);
```

```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({
  "rmNumBL" => {
    "user" => {
      "username" => "xxxxxx",
    },
    "phones" => {
      "phone" => [
        "5********",
        "05********",
      ],
    },
    "reason" => "Customer opted back in by phone",
  },
})

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

```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!({
          "rmNumBL": {
            "user": {
              "username": "xxxxxx"
            },
            "phones": {
              "phone": [
                "5********",
                "05********"
              ]
            },
            "reason": "Customer opted back in by phone"
          }
        }))
        .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

::: code-group

```xml [XML]
<?xml version="1.0" encoding="UTF-8"?>
<rmNumBL>
    <status>944</status>
    <message>X phone numbers Successfully deleted , Y Phone numbers not exist in blacklist</message>
</rmNumBL>
```

```json [JSON]
{
  "status": 944,
  "message": "X phone numbers Successfully deleted , Y Phone numbers not exist in blacklist"
}
```

:::

| Status | Meaning |
|---|---|
| `0` | Every number given was removed. |
| `944` | **Partial success.** Some were removed; others were not on the list. The `message` gives the counts. |

### Errors

| Status | When |
|---|---|
| `933` | A phone number is invalid, or `reason` is missing. |
| `2` | `phones` is missing or empty. |
| `511` | The account is not entitled to this operation. |

## Field notes

### How numbers get blocked

Three routes, only one of which is you:

1. **A recipient opts out**: by following the removal link or replying, when the message carried [`add_unsubscribe`](./send.md#opt-out-footers). This is the common case, and it happens without your involvement.
2. **You block them** with `addNumBL`, because a customer asked by phone or email, or because your own suppression list says so.
3. **The carrier or regulator blocks them**, which shows up in delivery reports as [status `17` or `201`](../reference/dlr-statuses.md#blocked-or-refused).

### The blocklist is enforced for you

You do not have to filter destinations before sending. Blocked numbers are dropped at send time, and a send whose destinations are *all* blocked fails with status `8` rather than silently reporting success.

That failure is worth logging separately: `8` means the audience has evaporated, which is a data problem rather than a code one.

### Unblocking responsibly

Removing a number from the blocklist means messaging someone who previously asked you to stop. Do it only with a record of them asking to come back, which is precisely what `reason` is for. Israeli anti-spam law places the burden of proof on the sender.

See [Opt-out & compliance](../use-cases/opt-out-and-compliance.md) for the full round trip.
