Subscribers
Reseller operations. An account that resells TextMe capacity can create sub-accounts, fund their wallets from its own credit, and read their balances.
POST https://my.textme.co.il/apiIf your account does not resell, these return status 511 and you can skip the page.
Two usernames, two roles
Throughout this page, user.username is your account (the reseller doing the work) while userDetails.username is the sub-account being acted on. Getting them the wrong way round is the most common mistake here.
addSub Create a sub-account
Creates a sub-account with login credentials, a default sender and an opening credit balance drawn from yours.
Parameters
| Name | Type | Description | Required |
|---|---|---|---|
addSub | object | Contains all other elements. | ✔️ |
user | object | Contains the user element. | ✔️ |
user.username | string | Your username. The reseller account. | ✔️ |
userDetails | object | The details of the user you want to add. | ✔️ |
userDetails.name | string | Display name for the new user. | ✔️ |
userDetails.username | string | Login username for the new user. | ✔️ |
userDetails.password | string | Password for the new user. | ✔️ |
userDetails.source | string | Default sender for the new user. | ✔️ |
userDetails.amount | int | Credits to grant on creation, taken from your balance. | ✔️ |
userDetails.otpPhone | string | Phone number for OTP authentication. | ➖ |
Request example
<?xml version="1.0" encoding="UTF-8"?>
<addSub>
<user>
<username>xxxxxx</username>
</user>
<userDetails>
<name>israel israeli</name>
<username>israelisraeli</username>
<password>israelisraeli</password>
<source>055xxxxxxx</source>
<amount>70000</amount>
<otpPhone>5xxxxxxxx</otpPhone>
</userDetails>
</addSub>{
"addSub": {
"user": {
"username": "xxxxxx"
},
"userDetails": {
"name": "israel israeli",
"username": "israelisraeli",
"password": "israelisraeli",
"source": "055xxxxxxx",
"amount": "70000",
"otpPhone": "5xxxxxxxx"
}
}
}curl --location 'https://my.textme.co.il/api' \
--header "Authorization: Bearer $TEXTME_API_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
"addSub": {
"user": {
"username": "xxxxxx"
},
"userDetails": {
"name": "israel israeli",
"username": "israelisraeli",
"password": "israelisraeli",
"source": "055xxxxxxx",
"amount": "70000",
"otpPhone": "5xxxxxxxx"
}
}
}'// 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({
addSub: {
user: {
username: 'xxxxxx',
},
userDetails: {
name: 'israel israeli',
username: 'israelisraeli',
password: 'israelisraeli',
source: '055xxxxxxx',
amount: '70000',
otpPhone: '5xxxxxxxx',
},
},
}),
})
const result = await response.json()
// Errors arrive as HTTP 200 too, so the payload status is what counts
if (Number(result.status) !== 0) {
throw new Error(`TextMe ${result.status}: ${result.message}`)
}
console.log(result)<?php
// composer require guzzlehttp/guzzle
$client = new \GuzzleHttp\Client([
'headers' => [
'Authorization' => 'Bearer '.getenv('TEXTME_API_TOKEN'),
'Accept' => 'application/json',
],
]);
$response = $client->post('https://my.textme.co.il/api', [
'json' => [
'addSub' => [
'user' => [
'username' => 'xxxxxx',
],
'userDetails' => [
'name' => 'israel israeli',
'username' => 'israelisraeli',
'password' => 'israelisraeli',
'source' => '055xxxxxxx',
'amount' => '70000',
'otpPhone' => '5xxxxxxxx',
],
],
],
]);
$result = json_decode($response->getBody()->getContents(), true);
// Errors arrive as HTTP 200 too, so the payload status is what counts
if ((int) $result['status'] !== 0) {
throw new RuntimeException("TextMe {$result['status']}: {$result['message']}");
}
print_r($result);<?php
use Illuminate\Support\Facades\Http;
$result = Http::withToken(config('services.textme.token'))
->acceptJson()
->post('https://my.textme.co.il/api', [
'addSub' => [
'user' => [
'username' => 'xxxxxx',
],
'userDetails' => [
'name' => 'israel israeli',
'username' => 'israelisraeli',
'password' => 'israelisraeli',
'source' => '055xxxxxxx',
'amount' => '70000',
'otpPhone' => '5xxxxxxxx',
],
],
])
->throw()
->json();
// Errors arrive as HTTP 200 too, so the payload status is what counts
throw_if((int) $result['status'] !== 0, RuntimeException::class,
"TextMe {$result['status']}: {$result['message']}");
logger()->info('TextMe', $result);# pip install httpx
import os
import httpx
response = httpx.post(
"https://my.textme.co.il/api",
headers={"Authorization": f"Bearer {os.environ['TEXTME_API_TOKEN']}"},
json={
"addSub": {
"user": {
"username": "xxxxxx",
},
"userDetails": {
"name": "israel israeli",
"username": "israelisraeli",
"password": "israelisraeli",
"source": "055xxxxxxx",
"amount": "70000",
"otpPhone": "5xxxxxxxx",
},
},
},
)
response.raise_for_status()
result = response.json()
# Errors arrive as HTTP 200 too, so the payload status is what counts
if int(result["status"]) != 0:
raise RuntimeError(f"TextMe {result['status']}: {result['message']}")
print(result)package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
)
func main() {
payload, _ := json.Marshal(map[string]any{
"addSub": map[string]any{
"user": map[string]any{
"username": "xxxxxx",
},
"userDetails": map[string]any{
"name": "israel israeli",
"username": "israelisraeli",
"password": "israelisraeli",
"source": "055xxxxxxx",
"amount": "70000",
"otpPhone": "5xxxxxxxx",
},
},
})
req, _ := http.NewRequest("POST", "https://my.textme.co.il/api", bytes.NewReader(payload))
req.Header.Set("Authorization", "Bearer "+os.Getenv("TEXTME_API_TOKEN"))
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
var result struct {
Status json.Number `json:"status"`
Message string `json:"message"`
}
if err := json.NewDecoder(res.Body).Decode(&result); err != nil {
panic(err)
}
// Errors arrive as HTTP 200 too, so the payload status is what counts
if result.Status.String() != "0" {
panic(fmt.Sprintf("TextMe %s: %s", result.Status, result.Message))
}
fmt.Println(result.Message)
}// Java 17+ using java.net.http. No dependencies (parse with Jackson/Gson)
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class TextMeSubscriberAdd {
public static void main(String[] args) throws Exception {
String body = """
{
"addSub": {
"user": {
"username": "xxxxxx"
},
"userDetails": {
"name": "israel israeli",
"username": "israelisraeli",
"password": "israelisraeli",
"source": "055xxxxxxx",
"amount": "70000",
"otpPhone": "5xxxxxxxx"
}
}
}
""";
HttpRequest request = HttpRequest.newBuilder(URI.create("https://my.textme.co.il/api"))
.header("Authorization", "Bearer " + System.getenv("TEXTME_API_TOKEN"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
// Errors arrive as HTTP 200 too, so the payload status is what counts
System.out.println(response.body());
}
}// .NET 8+ using System.Net.Http
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
var payload = """
{
"addSub": {
"user": {
"username": "xxxxxx"
},
"userDetails": {
"name": "israel israeli",
"username": "israelisraeli",
"password": "israelisraeli",
"source": "055xxxxxxx",
"amount": "70000",
"otpPhone": "5xxxxxxxx"
}
}
}
""";
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
"Bearer", Environment.GetEnvironmentVariable("TEXTME_API_TOKEN"));
var response = await http.PostAsync("https://my.textme.co.il/api",
new StringContent(payload, Encoding.UTF8, "application/json"));
var result = JsonDocument.Parse(await response.Content.ReadAsStringAsync()).RootElement;
var status = result.GetProperty("status").ToString();
// Errors arrive as HTTP 200 too, so the payload status is what counts
if (status != "0")
{
var message = result.GetProperty("message").ToString();
throw new Exception($"TextMe {status}: {message}");
}
Console.WriteLine(result);require "net/http"
require "json"
uri = URI("https://my.textme.co.il/api")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer #{ENV.fetch('TEXTME_API_TOKEN')}"
request["Content-Type"] = "application/json"
request.body = JSON.dump({
"addSub" => {
"user" => {
"username" => "xxxxxx",
},
"userDetails" => {
"name" => "israel israeli",
"username" => "israelisraeli",
"password" => "israelisraeli",
"source" => "055xxxxxxx",
"amount" => "70000",
"otpPhone" => "5xxxxxxxx",
},
},
})
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
result = JSON.parse(response.body)
# Errors arrive as HTTP 200 too, so the payload status is what counts
raise "TextMe #{result['status']}: #{result['message']}" unless result["status"].to_i.zero?
pp result// [dependencies]
// reqwest = { version = "0.12", features = ["json"] }
// tokio = { version = "1", features = ["full"] }
// serde_json = "1"
use serde_json::{json, Value};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let result: Value = reqwest::Client::new()
.post("https://my.textme.co.il/api")
.bearer_auth(std::env::var("TEXTME_API_TOKEN")?)
.json(&json!({
"addSub": {
"user": {
"username": "xxxxxx"
},
"userDetails": {
"name": "israel israeli",
"username": "israelisraeli",
"password": "israelisraeli",
"source": "055xxxxxxx",
"amount": "70000",
"otpPhone": "5xxxxxxxx"
}
}
}))
.send()
.await?
.json()
.await?;
// Errors arrive as HTTP 200 too, so the payload status is what counts
if result["status"] != 0 {
return Err(format!("TextMe {}: {}", result["status"], result["message"]).into());
}
println!("{result}");
Ok(())
}Response
<?xml version="1.0" encoding="UTF-8"?>
<addSub>
<status>0</status>
<message>The user was created successfully</message>
</addSub>{
"status": 0,
"message": "The user was created successfully"
}Errors
| Status | When |
|---|---|
990 | amount exceeds the credit you hold. |
991 | amount contains something other than digits. |
992 | source is too long or too short. |
993 | password is too long or too short. |
994 | username already exists. |
995 | username is too long or too short. |
996 | name is too long or too short. |
511 | Your account is not entitled to create sub-accounts. |
updateAmountSub Top up a wallet
Moves credit into a sub-account's wallet.
Parameters
| Name | Type | Description | Required |
|---|---|---|---|
updateAmountSub | object | Contains all other elements. | ✔️ |
user | object | Contains the user element. | ✔️ |
user.username | string | Your username. The reseller account. | ✔️ |
userDetails | object | The details of the user you want to update. | ✔️ |
userDetails.username | string | The sub-account's internal username. | ✔️ |
userDetails.amount | int | Credits to grant. | ✔️ |
userDetails.amount_int | int | Use this instead of amount to update the sub-account's money balance rather than its message credits. Send it without an amount element. | ➖ |
amount and amount_int are alternatives
amount moves message credits; amount_int moves money. Send one or the other, never both. amount_int is documented as replacing amount, not supplementing it.
Request example
<?xml version="1.0" encoding="UTF-8"?>
<updateAmountSub>
<user>
<username>xxxxxx</username>
</user>
<userDetails>
<username>username1</username>
<amount>70000</amount>
</userDetails>
</updateAmountSub>{
"updateAmountSub": {
"user": {
"username": "xxxxxx"
},
"userDetails": {
"username": "username1",
"amount": "70000"
}
}
}curl --location 'https://my.textme.co.il/api' \
--header "Authorization: Bearer $TEXTME_API_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
"updateAmountSub": {
"user": {
"username": "xxxxxx"
},
"userDetails": {
"username": "username1",
"amount": "70000"
}
}
}'// 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({
updateAmountSub: {
user: {
username: 'xxxxxx',
},
userDetails: {
username: 'username1',
amount: '70000',
},
},
}),
})
const result = await response.json()
// Errors arrive as HTTP 200 too, so the payload status is what counts
if (Number(result.status) !== 0) {
throw new Error(`TextMe ${result.status}: ${result.message}`)
}
console.log(result)<?php
// composer require guzzlehttp/guzzle
$client = new \GuzzleHttp\Client([
'headers' => [
'Authorization' => 'Bearer '.getenv('TEXTME_API_TOKEN'),
'Accept' => 'application/json',
],
]);
$response = $client->post('https://my.textme.co.il/api', [
'json' => [
'updateAmountSub' => [
'user' => [
'username' => 'xxxxxx',
],
'userDetails' => [
'username' => 'username1',
'amount' => '70000',
],
],
],
]);
$result = json_decode($response->getBody()->getContents(), true);
// Errors arrive as HTTP 200 too, so the payload status is what counts
if ((int) $result['status'] !== 0) {
throw new RuntimeException("TextMe {$result['status']}: {$result['message']}");
}
print_r($result);<?php
use Illuminate\Support\Facades\Http;
$result = Http::withToken(config('services.textme.token'))
->acceptJson()
->post('https://my.textme.co.il/api', [
'updateAmountSub' => [
'user' => [
'username' => 'xxxxxx',
],
'userDetails' => [
'username' => 'username1',
'amount' => '70000',
],
],
])
->throw()
->json();
// Errors arrive as HTTP 200 too, so the payload status is what counts
throw_if((int) $result['status'] !== 0, RuntimeException::class,
"TextMe {$result['status']}: {$result['message']}");
logger()->info('TextMe', $result);# pip install httpx
import os
import httpx
response = httpx.post(
"https://my.textme.co.il/api",
headers={"Authorization": f"Bearer {os.environ['TEXTME_API_TOKEN']}"},
json={
"updateAmountSub": {
"user": {
"username": "xxxxxx",
},
"userDetails": {
"username": "username1",
"amount": "70000",
},
},
},
)
response.raise_for_status()
result = response.json()
# Errors arrive as HTTP 200 too, so the payload status is what counts
if int(result["status"]) != 0:
raise RuntimeError(f"TextMe {result['status']}: {result['message']}")
print(result)package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
)
func main() {
payload, _ := json.Marshal(map[string]any{
"updateAmountSub": map[string]any{
"user": map[string]any{
"username": "xxxxxx",
},
"userDetails": map[string]any{
"username": "username1",
"amount": "70000",
},
},
})
req, _ := http.NewRequest("POST", "https://my.textme.co.il/api", bytes.NewReader(payload))
req.Header.Set("Authorization", "Bearer "+os.Getenv("TEXTME_API_TOKEN"))
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
var result struct {
Status json.Number `json:"status"`
Message string `json:"message"`
}
if err := json.NewDecoder(res.Body).Decode(&result); err != nil {
panic(err)
}
// Errors arrive as HTTP 200 too, so the payload status is what counts
if result.Status.String() != "0" {
panic(fmt.Sprintf("TextMe %s: %s", result.Status, result.Message))
}
fmt.Println(result.Message)
}// Java 17+ using java.net.http. No dependencies (parse with Jackson/Gson)
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class TextMeSubscriberWallet {
public static void main(String[] args) throws Exception {
String body = """
{
"updateAmountSub": {
"user": {
"username": "xxxxxx"
},
"userDetails": {
"username": "username1",
"amount": "70000"
}
}
}
""";
HttpRequest request = HttpRequest.newBuilder(URI.create("https://my.textme.co.il/api"))
.header("Authorization", "Bearer " + System.getenv("TEXTME_API_TOKEN"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
// Errors arrive as HTTP 200 too, so the payload status is what counts
System.out.println(response.body());
}
}// .NET 8+ using System.Net.Http
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
var payload = """
{
"updateAmountSub": {
"user": {
"username": "xxxxxx"
},
"userDetails": {
"username": "username1",
"amount": "70000"
}
}
}
""";
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
"Bearer", Environment.GetEnvironmentVariable("TEXTME_API_TOKEN"));
var response = await http.PostAsync("https://my.textme.co.il/api",
new StringContent(payload, Encoding.UTF8, "application/json"));
var result = JsonDocument.Parse(await response.Content.ReadAsStringAsync()).RootElement;
var status = result.GetProperty("status").ToString();
// Errors arrive as HTTP 200 too, so the payload status is what counts
if (status != "0")
{
var message = result.GetProperty("message").ToString();
throw new Exception($"TextMe {status}: {message}");
}
Console.WriteLine(result);require "net/http"
require "json"
uri = URI("https://my.textme.co.il/api")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer #{ENV.fetch('TEXTME_API_TOKEN')}"
request["Content-Type"] = "application/json"
request.body = JSON.dump({
"updateAmountSub" => {
"user" => {
"username" => "xxxxxx",
},
"userDetails" => {
"username" => "username1",
"amount" => "70000",
},
},
})
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
result = JSON.parse(response.body)
# Errors arrive as HTTP 200 too, so the payload status is what counts
raise "TextMe #{result['status']}: #{result['message']}" unless result["status"].to_i.zero?
pp result// [dependencies]
// reqwest = { version = "0.12", features = ["json"] }
// tokio = { version = "1", features = ["full"] }
// serde_json = "1"
use serde_json::{json, Value};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let result: Value = reqwest::Client::new()
.post("https://my.textme.co.il/api")
.bearer_auth(std::env::var("TEXTME_API_TOKEN")?)
.json(&json!({
"updateAmountSub": {
"user": {
"username": "xxxxxx"
},
"userDetails": {
"username": "username1",
"amount": "70000"
}
}
}))
.send()
.await?
.json()
.await?;
// Errors arrive as HTTP 200 too, so the payload status is what counts
if result["status"] != 0 {
return Err(format!("TextMe {}: {}", result["status"], result["message"]).into());
}
println!("{result}");
Ok(())
}Response
<?xml version="1.0" encoding="UTF-8"?>
<updateAmountSub>
<status>0</status>
<message>Wallet successfully updated</message>
</updateAmountSub>{
"status": 0,
"message": "Wallet successfully updated"
}Errors
| Status | When |
|---|---|
990 | The amount exceeds the credit you hold. |
991 | The amount contains something other than digits. |
503 | userDetails.username is not one of your sub-accounts. |
511 | Your account is not entitled to this operation. |
Not idempotent
There is no request id and no deduplication. Calling this twice grants the credit twice. If a call times out, read the balance with getBlanceSubs before retrying, never blind-retry a top-up.
getBlanceSubs Read every balance
Returns the balance of every sub-account in one call.
The name is misspelled on the wire
The root element really is getBlanceSubs. "Blance", not "Balance". Spelling it correctly returns status 997, Not a valid command sent.
Parameters
| Name | Type | Description | Required |
|---|---|---|---|
getBlanceSubs | object | Contains all other elements. | ✔️ |
user | object | Contains the user element. | ✔️ |
username | string | Your username. The reseller account. | ✔️ |
Request example
<?xml version="1.0" encoding="UTF-8"?>
<getBlanceSubs>
<user>
<username>xxxxxx</username>
</user>
</getBlanceSubs>{
"getBlanceSubs": {
"user": {
"username": "xxxxxx"
}
}
}curl --location 'https://my.textme.co.il/api' \
--header "Authorization: Bearer $TEXTME_API_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
"getBlanceSubs": {
"user": {
"username": "xxxxxx"
}
}
}'// 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({
getBlanceSubs: {
user: {
username: 'xxxxxx',
},
},
}),
})
const result = await response.json()
// Errors arrive as HTTP 200 too, so the payload status is what counts
if (Number(result.status) !== 0) {
throw new Error(`TextMe ${result.status}: ${result.message}`)
}
console.log(result)<?php
// composer require guzzlehttp/guzzle
$client = new \GuzzleHttp\Client([
'headers' => [
'Authorization' => 'Bearer '.getenv('TEXTME_API_TOKEN'),
'Accept' => 'application/json',
],
]);
$response = $client->post('https://my.textme.co.il/api', [
'json' => [
'getBlanceSubs' => [
'user' => [
'username' => 'xxxxxx',
],
],
],
]);
$result = json_decode($response->getBody()->getContents(), true);
// Errors arrive as HTTP 200 too, so the payload status is what counts
if ((int) $result['status'] !== 0) {
throw new RuntimeException("TextMe {$result['status']}: {$result['message']}");
}
print_r($result);<?php
use Illuminate\Support\Facades\Http;
$result = Http::withToken(config('services.textme.token'))
->acceptJson()
->post('https://my.textme.co.il/api', [
'getBlanceSubs' => [
'user' => [
'username' => 'xxxxxx',
],
],
])
->throw()
->json();
// Errors arrive as HTTP 200 too, so the payload status is what counts
throw_if((int) $result['status'] !== 0, RuntimeException::class,
"TextMe {$result['status']}: {$result['message']}");
logger()->info('TextMe', $result);# pip install httpx
import os
import httpx
response = httpx.post(
"https://my.textme.co.il/api",
headers={"Authorization": f"Bearer {os.environ['TEXTME_API_TOKEN']}"},
json={
"getBlanceSubs": {
"user": {
"username": "xxxxxx",
},
},
},
)
response.raise_for_status()
result = response.json()
# Errors arrive as HTTP 200 too, so the payload status is what counts
if int(result["status"]) != 0:
raise RuntimeError(f"TextMe {result['status']}: {result['message']}")
print(result)package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
)
func main() {
payload, _ := json.Marshal(map[string]any{
"getBlanceSubs": map[string]any{
"user": map[string]any{
"username": "xxxxxx",
},
},
})
req, _ := http.NewRequest("POST", "https://my.textme.co.il/api", bytes.NewReader(payload))
req.Header.Set("Authorization", "Bearer "+os.Getenv("TEXTME_API_TOKEN"))
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
var result struct {
Status json.Number `json:"status"`
Message string `json:"message"`
}
if err := json.NewDecoder(res.Body).Decode(&result); err != nil {
panic(err)
}
// Errors arrive as HTTP 200 too, so the payload status is what counts
if result.Status.String() != "0" {
panic(fmt.Sprintf("TextMe %s: %s", result.Status, result.Message))
}
fmt.Println(result.Message)
}// Java 17+ using java.net.http. No dependencies (parse with Jackson/Gson)
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class TextMeSubscriberBalances {
public static void main(String[] args) throws Exception {
String body = """
{
"getBlanceSubs": {
"user": {
"username": "xxxxxx"
}
}
}
""";
HttpRequest request = HttpRequest.newBuilder(URI.create("https://my.textme.co.il/api"))
.header("Authorization", "Bearer " + System.getenv("TEXTME_API_TOKEN"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
// Errors arrive as HTTP 200 too, so the payload status is what counts
System.out.println(response.body());
}
}// .NET 8+ using System.Net.Http
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
var payload = """
{
"getBlanceSubs": {
"user": {
"username": "xxxxxx"
}
}
}
""";
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
"Bearer", Environment.GetEnvironmentVariable("TEXTME_API_TOKEN"));
var response = await http.PostAsync("https://my.textme.co.il/api",
new StringContent(payload, Encoding.UTF8, "application/json"));
var result = JsonDocument.Parse(await response.Content.ReadAsStringAsync()).RootElement;
var status = result.GetProperty("status").ToString();
// Errors arrive as HTTP 200 too, so the payload status is what counts
if (status != "0")
{
var message = result.GetProperty("message").ToString();
throw new Exception($"TextMe {status}: {message}");
}
Console.WriteLine(result);require "net/http"
require "json"
uri = URI("https://my.textme.co.il/api")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer #{ENV.fetch('TEXTME_API_TOKEN')}"
request["Content-Type"] = "application/json"
request.body = JSON.dump({
"getBlanceSubs" => {
"user" => {
"username" => "xxxxxx",
},
},
})
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
result = JSON.parse(response.body)
# Errors arrive as HTTP 200 too, so the payload status is what counts
raise "TextMe #{result['status']}: #{result['message']}" unless result["status"].to_i.zero?
pp result// [dependencies]
// reqwest = { version = "0.12", features = ["json"] }
// tokio = { version = "1", features = ["full"] }
// serde_json = "1"
use serde_json::{json, Value};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let result: Value = reqwest::Client::new()
.post("https://my.textme.co.il/api")
.bearer_auth(std::env::var("TEXTME_API_TOKEN")?)
.json(&json!({
"getBlanceSubs": {
"user": {
"username": "xxxxxx"
}
}
}))
.send()
.await?
.json()
.await?;
// Errors arrive as HTTP 200 too, so the payload status is what counts
if result["status"] != 0 {
return Err(format!("TextMe {}: {}", result["status"], result["message"]).into());
}
println!("{result}");
Ok(())
}Response
<?xml version="1.0" encoding="UTF-8"?>
<getBlanceSubs>
<status>0</status>
<message></message>
<balances>
<balance>
<amount>80</amount>
<sms_user_id>xxx</sms_user_id>
<name>name1</name>
</balance>
<balance>
<amount>50</amount>
<sms_user_id>xxx</sms_user_id>
<name>name2</name>
</balance>
</balances>
</getBlanceSubs>{
"status": 0,
"message": "",
"balances": [
{
"amount": "80",
"sms_user_id": "XXXX",
"name": "name1"
},
{
"amount": "50",
"sms_user_id": "XXXX",
"name": "name2"
}
]
}| Field | Type | Description |
|---|---|---|
balances | array | One entry per sub-account. |
balances[].amount | string | Credits remaining. |
balances[].sms_user_id | string | The sub-account's internal id. |
balances[].name | string | Its display name. |
The response has no username
Entries carry name and sms_user_id, but not the login username you used in addSub. If you need to map balances back to your own records, store sms_user_id, or keep the name unique and meaningful.
Errors
| Status | When |
|---|---|
3, 10, 11 | Token invalid, expired, or belonging to another username. |
511 | Your account is not entitled to this operation. |
Field notes
Credit flows one way through the API
addSub and updateAmountSub move credit from you to a sub-account. There is no operation that pulls it back. Grant in the amounts you actually intend to hand over, and top up more often rather than in large blocks.
Acting on a sub-account's behalf
Your token authenticates; user.username chooses who the operation is for. Set it to a sub-account's username and you can send, read reports and manage lists on their behalf. See Authentication. The account must be one of yours, or the call fails with 503.
Sub-accounts verify their own senders; getVerifiedPhones with is_subs: 1 gives you a view across all of them.
Passwords go over the wire
addSub carries a plaintext password. It is TLS-protected in transit, but it will end up in any request log you keep. Redact userDetails.password before logging, and generate the value rather than reusing one a human picked.

