API tokens
Mint and read API tokens. Both actions need an existing valid token in the Authorization header. The very first token for an account has to be created in the web console, as described in Authentication.
POST https://my.textme.co.il/apigetApiToken Create a new token
Issues an additional token for username. The new token's name is generated from the creation date and time, and its expiry is fixed at issue.
Parameters
| Name | Type | Description | Required |
|---|---|---|---|
getApiToken | object | Root element. | ✔️ |
user | object | Contains the acting account. | ✔️ |
user.username | string | The account making the request, for a reseller, the administrator account. | ✔️ |
username | string | The account the token is being issued for. Same as user.username unless you are a reseller issuing for a sub-account. | ✔️ |
action | string | new to mint a token. | ✔️ |
Request example
<?xml version="1.0" encoding="UTF-8"?>
<getApiToken>
<user>
<username>admin_username</username>
</user>
<username>username_for_token</username>
<action>new</action>
</getApiToken>{
"getApiToken": {
"user": {
"username": "admin_username"
},
"username": "username_for_token",
"action": "new"
}
}curl --location 'https://my.textme.co.il/api' \
--header "Authorization: Bearer $TEXTME_API_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
"getApiToken": {
"user": {
"username": "admin_username"
},
"username": "username_for_token",
"action": "new"
}
}'// 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({
getApiToken: {
user: {
username: 'admin_username',
},
username: 'username_for_token',
action: 'new',
},
}),
})
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' => [
'getApiToken' => [
'user' => [
'username' => 'admin_username',
],
'username' => 'username_for_token',
'action' => 'new',
],
],
]);
$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', [
'getApiToken' => [
'user' => [
'username' => 'admin_username',
],
'username' => 'username_for_token',
'action' => 'new',
],
])
->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={
"getApiToken": {
"user": {
"username": "admin_username",
},
"username": "username_for_token",
"action": "new",
},
},
)
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{
"getApiToken": map[string]any{
"user": map[string]any{
"username": "admin_username",
},
"username": "username_for_token",
"action": "new",
},
})
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 TextMeTokenCreate {
public static void main(String[] args) throws Exception {
String body = """
{
"getApiToken": {
"user": {
"username": "admin_username"
},
"username": "username_for_token",
"action": "new"
}
}
""";
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 = """
{
"getApiToken": {
"user": {
"username": "admin_username"
},
"username": "username_for_token",
"action": "new"
}
}
""";
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({
"getApiToken" => {
"user" => {
"username" => "admin_username",
},
"username" => "username_for_token",
"action" => "new",
},
})
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!({
"getApiToken": {
"user": {
"username": "admin_username"
},
"username": "username_for_token",
"action": "new"
}
}))
.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"?>
<getApiToken>
<status>0</status>
<message>0zrjDjZ26Dll5dqdTBmuwwtQg8</message>
<expiration_date>07/06/2025 09:17:34</expiration_date>
</getApiToken>{
"status": 0,
"message": "0zrjDjZ26Dll5dqdTBmuwwtQg8",
"expiration_date": "07/06/2025 09:17:34"
}| Field | Type | Description |
|---|---|---|
status | int | 0 on success. |
message | string | The token itself. Not a human-readable note, as on other operations. |
expiration_date | string | When this token stops working, dd/mm/yyyy hh:mm:ss. |
The token arrives in message
This operation reuses the message field to carry the secret. Store it on receipt. There is no way to read an older token back, only the most recent one.
Errors
| Status | When |
|---|---|
3 | The token in the Authorization header is not recognised. |
10 | That token has expired. |
11 | That token does not belong to the user.username given. |
502 | action was something other than new or current. |
503 | username is not an account you may act for. |
511 | The account is not entitled to manage tokens. |
getApiToken Read the current token
Returns the most recent token for username instead of creating one. Nothing is issued and nothing is invalidated, which makes it a safe health check for a stored credential.
Parameters
Identical to the above, with action set to current.
| Name | Type | Description | Required |
|---|---|---|---|
getApiToken | object | Root element. | ✔️ |
user.username | string | The account making the request. | ✔️ |
username | string | The account whose token you want. | ✔️ |
action | string | current. | ✔️ |
Request example
<?xml version="1.0" encoding="UTF-8"?>
<getApiToken>
<user>
<username>admin_username</username>
</user>
<username>username_for_token</username>
<action>current</action>
</getApiToken>{
"getApiToken": {
"user": {
"username": "admin_username"
},
"username": "username_for_token",
"action": "current"
}
}curl --location 'https://my.textme.co.il/api' \
--header "Authorization: Bearer $TEXTME_API_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
"getApiToken": {
"user": {
"username": "admin_username"
},
"username": "username_for_token",
"action": "current"
}
}'// 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({
getApiToken: {
user: {
username: 'admin_username',
},
username: 'username_for_token',
action: 'current',
},
}),
})
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' => [
'getApiToken' => [
'user' => [
'username' => 'admin_username',
],
'username' => 'username_for_token',
'action' => 'current',
],
],
]);
$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', [
'getApiToken' => [
'user' => [
'username' => 'admin_username',
],
'username' => 'username_for_token',
'action' => 'current',
],
])
->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={
"getApiToken": {
"user": {
"username": "admin_username",
},
"username": "username_for_token",
"action": "current",
},
},
)
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{
"getApiToken": map[string]any{
"user": map[string]any{
"username": "admin_username",
},
"username": "username_for_token",
"action": "current",
},
})
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 TextMeTokenCurrent {
public static void main(String[] args) throws Exception {
String body = """
{
"getApiToken": {
"user": {
"username": "admin_username"
},
"username": "username_for_token",
"action": "current"
}
}
""";
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 = """
{
"getApiToken": {
"user": {
"username": "admin_username"
},
"username": "username_for_token",
"action": "current"
}
}
""";
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({
"getApiToken" => {
"user" => {
"username" => "admin_username",
},
"username" => "username_for_token",
"action" => "current",
},
})
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!({
"getApiToken": {
"user": {
"username": "admin_username"
},
"username": "username_for_token",
"action": "current"
}
}))
.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"?>
<getApiToken>
<status>0</status>
<message>0zrjDjZ26Dll5dqdTBmuwwtQg8</message>
<expiration_date>07/06/2025 09:17:34</expiration_date>
</getApiToken>{
"status": 0,
"message": "0zrjDjZ26Dll5dqdTBmuwwtQg8",
"expiration_date": "07/06/2025 09:17:34"
}Errors
| Status | When |
|---|---|
504 | The account has never had a token. Create the first one in the console. |
3, 10, 11, 502, 503, 511 | As above. |
Rotating a token
Because up to five tokens are valid simultaneously, rotation is a plain three-step sequence with no window where calls fail:
- Call
getApiTokenwithaction: newusing the token you currently hold. - Deploy the returned token to your application.
- Delete the old token in the console.
Expiry does not roll forward
Minting a new token leaves every existing token's expiration_date exactly where it was. If you rotate on a schedule, drive it from the expiration_date of the token in production, not from the date of the last rotation.
A sixth token cannot be created while five are active, delete one first.

