Skip to content

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

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

NameTypeDescriptionRequired
blacklistobjectContains all other elements.✔️
userobjectContains the user element.✔️
usernamestringThe username of the account by which you are recognized in the system.✔️
fromstringStart of the range, formatted dd/mm/yy hh:mm.✔️
tostringEnd of the range, formatted dd/mm/yy hh:mm.✔️

Request example

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
{
  "blacklist": {
    "user": {
      "username": "XXXXXX"
    },
    "from": "02/06/22 00:00",
    "to": "15/12/22 23:59"
  }
}
bash
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
// Node.js 18+ / browsers — 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 — the payload status is what counts
if (Number(result.status) !== 0) {
  throw new Error(`TextMe ${result.status}: ${result.message}`)
}

console.log(result)
php
<?php
// composer require guzzlehttp/guzzle

$client = new \GuzzleHttp\Client([
    'headers' => [
        'Authorization' => 'Bearer '.getenv('TEXTME_API_TOKEN'),
        'Accept' => 'application/json',
    ],
]);

$response = $client->post('https://my.textme.co.il/api', [
    'json' => [
        '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 — the payload status is what counts
if ((int) $result['status'] !== 0) {
    throw new RuntimeException("TextMe {$result['status']}: {$result['message']}");
}

print_r($result);
php
<?php

use Illuminate\Support\Facades\Http;

$result = Http::withToken(config('services.textme.token'))
    ->acceptJson()
    ->post('https://my.textme.co.il/api', [
        'blacklist' => [
            'user' => [
                'username' => 'XXXXXX',
            ],
            'from' => '02/06/22 00:00',
            'to' => '15/12/22 23:59',
        ],
    ])
    ->throw()
    ->json();

// Errors arrive as HTTP 200 too — the payload status is what counts
throw_if((int) $result['status'] !== 0, RuntimeException::class,
    "TextMe {$result['status']}: {$result['message']}");

logger()->info('TextMe', $result);
python
# pip install httpx
import os

import httpx

response = httpx.post(
    "https://my.textme.co.il/api",
    headers={"Authorization": f"Bearer {os.environ['TEXTME_API_TOKEN']}"},
    json={
        "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 — the payload status is what counts
if int(result["status"]) != 0:
    raise RuntimeError(f"TextMe {result['status']}: {result['message']}")

print(result)
go
package main

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

func main() {
	payload, _ := json.Marshal(map[string]any{
		"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 — 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 17+ — 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 — the payload status is what counts
        System.out.println(response.body());
    }
}
csharp
// .NET 8+ — 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 — 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
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 — the payload status is what counts
raise "TextMe #{result['status']}: #{result['message']}" unless result["status"].to_i.zero?

pp result
rust
// [dependencies]
// reqwest = { version = "0.12", features = ["json"] }
// tokio = { version = "1", features = ["full"] }
// serde_json = "1"
use serde_json::{json, Value};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let result: Value = reqwest::Client::new()
        .post("https://my.textme.co.il/api")
        .bearer_auth(std::env::var("TEXTME_API_TOKEN")?)
        .json(&json!({
          "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 — the payload status is what counts
    if result["status"] != 0 {
        return Err(format!("TextMe {}: {}", result["status"], result["message"]).into());
    }

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

Response

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
{
  "status": 0,
  "message": "",
  "transactions": [
    {
      "phone": "5********",
      "date": "22/08/22 11:00"
    },
    {
      "phone": "5********",
      "date": "23/11/22 12:37"
    }
  ]
}
FieldTypeDescription
statusint0 on success.
transactionsarrayOne entry per blocked number.
transactions[].phonestringThe blocked number, partly masked.
transactions[].datestringWhen it was blocked, dd/mm/yy hh:mm.

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

StatusWhen
2from or to missing.
3, 10, 11Token invalid, expired, or belonging to another username.
511The 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

NameTypeDescriptionRequired
addNumBLobjectContains all other elements.✔️
userobjectContains the user element.✔️
usernamestringThe username of the account by which you are recognized in the system.✔️
phonesobjectContains all phone elements.✔️
phonestringA number to block. Repeatable.✔️

Request example

xml
<?xml version="1.0" encoding="UTF-8"?>
<addNumBL>
    <user>
        <username>xxxxxx</username>
    </user>
    <phones>
        <phone>5********</phone>
        <phone>05********</phone>
    </phones>
</addNumBL>
json
{
  "addNumBL": {
    "user": {
      "username": "xxxxxx"
    },
    "phones": {
      "phone": [
        "5********",
        "05********"
      ]
    }
  }
}
bash
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
// Node.js 18+ / browsers — 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 — the payload status is what counts
if (Number(result.status) !== 0) {
  throw new Error(`TextMe ${result.status}: ${result.message}`)
}

console.log(result)
php
<?php
// composer require guzzlehttp/guzzle

$client = new \GuzzleHttp\Client([
    'headers' => [
        'Authorization' => 'Bearer '.getenv('TEXTME_API_TOKEN'),
        'Accept' => 'application/json',
    ],
]);

$response = $client->post('https://my.textme.co.il/api', [
    'json' => [
        'addNumBL' => [
            'user' => [
                'username' => 'xxxxxx',
            ],
            'phones' => [
                'phone' => [
                    '5********',
                    '05********',
                ],
            ],
        ],
    ],
]);

$result = json_decode($response->getBody()->getContents(), true);

// Errors arrive as HTTP 200 too — the payload status is what counts
if ((int) $result['status'] !== 0) {
    throw new RuntimeException("TextMe {$result['status']}: {$result['message']}");
}

print_r($result);
php
<?php

use Illuminate\Support\Facades\Http;

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

// Errors arrive as HTTP 200 too — the payload status is what counts
throw_if((int) $result['status'] !== 0, RuntimeException::class,
    "TextMe {$result['status']}: {$result['message']}");

logger()->info('TextMe', $result);
python
# pip install httpx
import os

import httpx

response = httpx.post(
    "https://my.textme.co.il/api",
    headers={"Authorization": f"Bearer {os.environ['TEXTME_API_TOKEN']}"},
    json={
        "addNumBL": {
            "user": {
                "username": "xxxxxx",
            },
            "phones": {
                "phone": [
                    "5********",
                    "05********",
                ],
            },
        },
    },
)
response.raise_for_status()
result = response.json()

# Errors arrive as HTTP 200 too — the payload status is what counts
if int(result["status"]) != 0:
    raise RuntimeError(f"TextMe {result['status']}: {result['message']}")

print(result)
go
package main

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

func main() {
	payload, _ := json.Marshal(map[string]any{
		"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 — 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 17+ — 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 — the payload status is what counts
        System.out.println(response.body());
    }
}
csharp
// .NET 8+ — 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 — 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
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 — the payload status is what counts
raise "TextMe #{result['status']}: #{result['message']}" unless result["status"].to_i.zero?

pp result
rust
// [dependencies]
// reqwest = { version = "0.12", features = ["json"] }
// tokio = { version = "1", features = ["full"] }
// serde_json = "1"
use serde_json::{json, Value};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let result: Value = reqwest::Client::new()
        .post("https://my.textme.co.il/api")
        .bearer_auth(std::env::var("TEXTME_API_TOKEN")?)
        .json(&json!({
          "addNumBL": {
            "user": {
              "username": "xxxxxx"
            },
            "phones": {
              "phone": [
                "5********",
                "05********"
              ]
            }
          }
        }))
        .send()
        .await?
        .json()
        .await?;

    // Errors arrive as HTTP 200 too — the payload status is what counts
    if result["status"] != 0 {
        return Err(format!("TextMe {}: {}", result["status"], result["message"]).into());
    }

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

Response

xml
<?xml version="1.0" encoding="UTF-8"?>
<addNumBL>
    <status>946</status>
    <message>number has successfully added to blacklist</message>
</addNumBL>
json
{
  "status": 946,
  "message": "number has successfully added to blacklist"
}
StatusMeaning
946Success. The numbers were added.

Errors

StatusWhen
2phones is missing or empty.
9A number is too short or too long.
511The 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

NameTypeDescriptionRequired
rmNumBLobjectContains all other elements.✔️
userobjectContains the user element.✔️
usernamestringThe username of the account by which you are recognized in the system.✔️
phonesobjectContains all phone elements.✔️
phonestringA number to unblock. Repeatable.✔️
reasonstringWhy the number is being unblocked.✔️

Request example

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
{
  "rmNumBL": {
    "user": {
      "username": "xxxxxx"
    },
    "phones": {
      "phone": [
        "5********",
        "05********"
      ]
    },
    "reason": "Customer opted back in by phone"
  }
}
bash
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
// Node.js 18+ / browsers — 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 — the payload status is what counts
if (Number(result.status) !== 0) {
  throw new Error(`TextMe ${result.status}: ${result.message}`)
}

console.log(result)
php
<?php
// composer require guzzlehttp/guzzle

$client = new \GuzzleHttp\Client([
    'headers' => [
        'Authorization' => 'Bearer '.getenv('TEXTME_API_TOKEN'),
        'Accept' => 'application/json',
    ],
]);

$response = $client->post('https://my.textme.co.il/api', [
    'json' => [
        '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 — the payload status is what counts
if ((int) $result['status'] !== 0) {
    throw new RuntimeException("TextMe {$result['status']}: {$result['message']}");
}

print_r($result);
php
<?php

use Illuminate\Support\Facades\Http;

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

// Errors arrive as HTTP 200 too — the payload status is what counts
throw_if((int) $result['status'] !== 0, RuntimeException::class,
    "TextMe {$result['status']}: {$result['message']}");

logger()->info('TextMe', $result);
python
# pip install httpx
import os

import httpx

response = httpx.post(
    "https://my.textme.co.il/api",
    headers={"Authorization": f"Bearer {os.environ['TEXTME_API_TOKEN']}"},
    json={
        "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 — the payload status is what counts
if int(result["status"]) != 0:
    raise RuntimeError(f"TextMe {result['status']}: {result['message']}")

print(result)
go
package main

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

func main() {
	payload, _ := json.Marshal(map[string]any{
		"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 — 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 17+ — 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 — the payload status is what counts
        System.out.println(response.body());
    }
}
csharp
// .NET 8+ — 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 — 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
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 — the payload status is what counts
raise "TextMe #{result['status']}: #{result['message']}" unless result["status"].to_i.zero?

pp result
rust
// [dependencies]
// reqwest = { version = "0.12", features = ["json"] }
// tokio = { version = "1", features = ["full"] }
// serde_json = "1"
use serde_json::{json, Value};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let result: Value = reqwest::Client::new()
        .post("https://my.textme.co.il/api")
        .bearer_auth(std::env::var("TEXTME_API_TOKEN")?)
        .json(&json!({
          "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 — the payload status is what counts
    if result["status"] != 0 {
        return Err(format!("TextMe {}: {}", result["status"], result["message"]).into());
    }

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

Response

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
{
  "status": 944,
  "message": "X phone numbers Successfully deleted , Y Phone numbers not exist in blacklist"
}
StatusMeaning
0Every number given was removed.
944Partial success. Some were removed; others were not on the list. The message gives the counts.

Errors

StatusWhen
933A phone number is invalid, or reason is missing.
2phones is missing or empty.
511The 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. 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.

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 for the full round trip.