Campaigns
Every send creates a campaign. A scheduled one can be called off before it goes out, either by id or by name. Birthday campaigns — recurring sends triggered by a contact's date of birth — are listed and edited here too.
POST https://my.textme.co.il/apicancel Cancel a campaign by id
Cancels one scheduled campaign.
Parameters
| Name | Type | Description | Required |
|---|---|---|---|
cancel | 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. | ✔️ |
campaign_id | int | The number of your campaign. | ✔️ |
Request example
<?xml version="1.0" encoding="UTF-8"?>
<cancel>
<user>
<username>Leeroy</username>
</user>
<campaign_id>1234</campaign_id>
</cancel>{
"cancel": {
"user": {
"username": "Leeroy"
},
"campaign_id": "1234"
}
}curl --location 'https://my.textme.co.il/api' \
--header "Authorization: Bearer $TEXTME_API_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
"cancel": {
"user": {
"username": "Leeroy"
},
"campaign_id": "1234"
}
}'// 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({
cancel: {
user: {
username: 'Leeroy',
},
campaign_id: '1234',
},
}),
})
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
// 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' => [
'cancel' => [
'user' => [
'username' => 'Leeroy',
],
'campaign_id' => '1234',
],
],
]);
$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
use Illuminate\Support\Facades\Http;
$result = Http::withToken(config('services.textme.token'))
->acceptJson()
->post('https://my.textme.co.il/api', [
'cancel' => [
'user' => [
'username' => 'Leeroy',
],
'campaign_id' => '1234',
],
])
->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);# 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={
"cancel": {
"user": {
"username": "Leeroy",
},
"campaign_id": "1234",
},
},
)
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)package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
)
func main() {
payload, _ := json.Marshal(map[string]any{
"cancel": map[string]any{
"user": map[string]any{
"username": "Leeroy",
},
"campaign_id": "1234",
},
})
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 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 TextMeCampaignCancelById {
public static void main(String[] args) throws Exception {
String body = """
{
"cancel": {
"user": {
"username": "Leeroy"
},
"campaign_id": "1234"
}
}
""";
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());
}
}// .NET 8+ — System.Net.Http
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
var payload = """
{
"cancel": {
"user": {
"username": "Leeroy"
},
"campaign_id": "1234"
}
}
""";
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);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({
"cancel" => {
"user" => {
"username" => "Leeroy",
},
"campaign_id" => "1234",
},
})
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// [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!({
"cancel": {
"user": {
"username": "Leeroy"
},
"campaign_id": "1234"
}
}))
.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 version="1.0" encoding="UTF-8"?>
<cancel>
<status>0</status>
<message>Campaign successfuly cancel</message>
</cancel>{
"status": 0,
"message": "Campaign successfuly cancel"
}Errors
| Status | When |
|---|---|
955 | Campaign already cancel — already cancelled. Effectively a no-op; usually safe to ignore. |
966 | Campaign already sent — too late. |
970 | Campaigns was not cancelled. Contact support. |
977 | Campaign does not belong to customer or Not exist. |
cancel Cancel campaigns by name
Cancels every scheduled campaign whose campaign_name matches. The response reports how many were affected.
Parameters
| Name | Type | Description | Required |
|---|---|---|---|
cancel | 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. | ✔️ |
campaign_name | string | The name of your campaign. | ✔️ |
Request example
<?xml version="1.0" encoding="UTF-8"?>
<cancel>
<user>
<username>Leeroy</username>
</user>
<campaign_name>My Campaign</campaign_name>
</cancel>{
"cancel": {
"user": {
"username": "Leeroy"
},
"campaign_name": "My Campaign"
}
}curl --location 'https://my.textme.co.il/api' \
--header "Authorization: Bearer $TEXTME_API_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
"cancel": {
"user": {
"username": "Leeroy"
},
"campaign_name": "My Campaign"
}
}'// 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({
cancel: {
user: {
username: 'Leeroy',
},
campaign_name: 'My Campaign',
},
}),
})
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
// 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' => [
'cancel' => [
'user' => [
'username' => 'Leeroy',
],
'campaign_name' => 'My Campaign',
],
],
]);
$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
use Illuminate\Support\Facades\Http;
$result = Http::withToken(config('services.textme.token'))
->acceptJson()
->post('https://my.textme.co.il/api', [
'cancel' => [
'user' => [
'username' => 'Leeroy',
],
'campaign_name' => 'My Campaign',
],
])
->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);# 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={
"cancel": {
"user": {
"username": "Leeroy",
},
"campaign_name": "My Campaign",
},
},
)
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)package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
)
func main() {
payload, _ := json.Marshal(map[string]any{
"cancel": map[string]any{
"user": map[string]any{
"username": "Leeroy",
},
"campaign_name": "My Campaign",
},
})
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 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 TextMeCampaignCancelByName {
public static void main(String[] args) throws Exception {
String body = """
{
"cancel": {
"user": {
"username": "Leeroy"
},
"campaign_name": "My Campaign"
}
}
""";
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());
}
}// .NET 8+ — System.Net.Http
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
var payload = """
{
"cancel": {
"user": {
"username": "Leeroy"
},
"campaign_name": "My Campaign"
}
}
""";
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);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({
"cancel" => {
"user" => {
"username" => "Leeroy",
},
"campaign_name" => "My Campaign",
},
})
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// [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!({
"cancel": {
"user": {
"username": "Leeroy"
},
"campaign_name": "My Campaign"
}
}))
.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 version="1.0" encoding="UTF-8"?>
<cancel>
<status>0</status>
<message>Campaigns successfully cancel</message>
<count>1</count>
</cancel>{
"status": 0,
"message": "Campaigns successfully cancel",
"count": 1
}| Field | Type | Description |
|---|---|---|
status | int | 0 on success. |
message | string | Campaigns successfully cancel. |
count | int | How many campaigns were cancelled. |
Name matching is the whole selector
There is no confirmation step and no dry run. Every pending campaign carrying that name is cancelled — which is exactly why splitting a large run across several bulk calls with a shared campaign_name is a good idea: one request stops all of it.
The corollary is that a generic name such as newsletter will one day cancel more than you meant. Make names unique per run — newsletter-2026-08-17 rather than newsletter.
Errors
Same as cancelling by id, with count 0 and status 0 when nothing matched.
get_birthday_campaigns List birthday campaigns
Parameters
| Name | Type | Description | Required |
|---|---|---|---|
get_birthday_campaigns | 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. | ✔️ |
Request example
<?xml version="1.0" encoding="UTF-8"?>
<get_birthday_campaigns>
<user>
<username>XXXXXX</username>
</user>
</get_birthday_campaigns>{
"get_birthday_campaigns": {
"user": {
"username": "XXXXXX"
}
}
}curl --location 'https://my.textme.co.il/api' \
--header "Authorization: Bearer $TEXTME_API_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
"get_birthday_campaigns": {
"user": {
"username": "XXXXXX"
}
}
}'// 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({
get_birthday_campaigns: {
user: {
username: 'XXXXXX',
},
},
}),
})
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
// 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' => [
'get_birthday_campaigns' => [
'user' => [
'username' => 'XXXXXX',
],
],
],
]);
$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
use Illuminate\Support\Facades\Http;
$result = Http::withToken(config('services.textme.token'))
->acceptJson()
->post('https://my.textme.co.il/api', [
'get_birthday_campaigns' => [
'user' => [
'username' => 'XXXXXX',
],
],
])
->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);# 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={
"get_birthday_campaigns": {
"user": {
"username": "XXXXXX",
},
},
},
)
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)package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
)
func main() {
payload, _ := json.Marshal(map[string]any{
"get_birthday_campaigns": 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 — 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+ — 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 TextMeBirthdayList {
public static void main(String[] args) throws Exception {
String body = """
{
"get_birthday_campaigns": {
"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 — the payload status is what counts
System.out.println(response.body());
}
}// .NET 8+ — System.Net.Http
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
var payload = """
{
"get_birthday_campaigns": {
"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 — 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({
"get_birthday_campaigns" => {
"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 — 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!({
"get_birthday_campaigns": {
"user": {
"username": "XXXXXX"
}
}
}))
.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 version="1.0" encoding="utf-8"?>
<sms>
<status>0</status>
<message></message>
<birthday_campaigns>
<birthday_campaign>
<CAMPAIGN_ID>XXXX</CAMPAIGN_ID>
<CAMPAIGN_NAME>XXXX</CAMPAIGN_NAME>
<CREATED_ON>18/10/23 14:40:39</CREATED_ON>
<ACTIVE_DESTINATIONS>X</ACTIVE_DESTINATIONS>
</birthday_campaign>
</birthday_campaigns>
</sms>{
"status": 0,
"message": "",
"birthday_campaigns": {
"birthday_campaign": [
{
"CAMPAIGN_ID": "xxxxx",
"CAMPAIGN_NAME": "xxxxx",
"CREATED_ON": "18/10/23 14:40:39",
"ACTIVE_DESTINATIONS": ""
}
]
}
}| Field | Type | Description |
|---|---|---|
birthday_campaigns.birthday_campaign | array | One entry per campaign. |
CAMPAIGN_ID | string | The campaign's id — what edit_birthday_campaign needs. |
CAMPAIGN_NAME | string | Its name. |
CREATED_ON | string | When it was created, dd/mm/yy hh:mm:ss. |
ACTIVE_DESTINATIONS | string | How many contacts it currently targets. |
Field names are upper case here
Unlike the rest of the API, birthday campaign fields come back as CAMPAIGN_ID, CAMPAIGN_NAME and so on. Case-sensitive parsers need to expect it.
Errors
| Status | When |
|---|---|
3, 10, 11 | Token invalid, expired, or belonging to another username. |
511 | The account is not entitled to this operation. |
edit_birthday_campaign Change the message
Replaces the message body of an existing birthday campaign. The campaign's schedule, audience and name are untouched.
Parameters
| Name | Type | Description | Required |
|---|---|---|---|
edit_birthday_campaign | 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. | ✔️ |
campaign_id | object | The campaign_id you want to edit. | ✔️ |
message | object | The content of the new message. | ✔️ |
Request example
<?xml version="1.0" encoding="UTF-8"?>
<edit_birthday_campaign>
<user>
<username>XXXX</username>
</user>
<campaign_id>XXXX</campaign_id>
<message>XXXX</message>
</edit_birthday_campaign>{
"edit_birthday_campaign": {
"user": {
"username": "XXXX"
},
"campaign_id": "XXXX",
"message": "XXXX"
}
}curl --location 'https://my.textme.co.il/api' \
--header "Authorization: Bearer $TEXTME_API_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
"edit_birthday_campaign": {
"user": {
"username": "XXXX"
},
"campaign_id": "XXXX",
"message": "XXXX"
}
}'// 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({
edit_birthday_campaign: {
user: {
username: 'XXXX',
},
campaign_id: 'XXXX',
message: 'XXXX',
},
}),
})
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
// 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' => [
'edit_birthday_campaign' => [
'user' => [
'username' => 'XXXX',
],
'campaign_id' => 'XXXX',
'message' => 'XXXX',
],
],
]);
$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
use Illuminate\Support\Facades\Http;
$result = Http::withToken(config('services.textme.token'))
->acceptJson()
->post('https://my.textme.co.il/api', [
'edit_birthday_campaign' => [
'user' => [
'username' => 'XXXX',
],
'campaign_id' => 'XXXX',
'message' => 'XXXX',
],
])
->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);# 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={
"edit_birthday_campaign": {
"user": {
"username": "XXXX",
},
"campaign_id": "XXXX",
"message": "XXXX",
},
},
)
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)package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
)
func main() {
payload, _ := json.Marshal(map[string]any{
"edit_birthday_campaign": map[string]any{
"user": map[string]any{
"username": "XXXX",
},
"campaign_id": "XXXX",
"message": "XXXX",
},
})
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 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 TextMeBirthdayEdit {
public static void main(String[] args) throws Exception {
String body = """
{
"edit_birthday_campaign": {
"user": {
"username": "XXXX"
},
"campaign_id": "XXXX",
"message": "XXXX"
}
}
""";
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());
}
}// .NET 8+ — System.Net.Http
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
var payload = """
{
"edit_birthday_campaign": {
"user": {
"username": "XXXX"
},
"campaign_id": "XXXX",
"message": "XXXX"
}
}
""";
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);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({
"edit_birthday_campaign" => {
"user" => {
"username" => "XXXX",
},
"campaign_id" => "XXXX",
"message" => "XXXX",
},
})
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// [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!({
"edit_birthday_campaign": {
"user": {
"username": "XXXX"
},
"campaign_id": "XXXX",
"message": "XXXX"
}
}))
.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 version="1.0" encoding="utf-8"?>
<sms>
<status>0</status>
<message>birthday campaign successfully updated</message>
</sms>{
"status": 0,
"message": "birthday campaign successfully updated"
}Errors
| Status | When |
|---|---|
977 | The campaign does not exist, or belongs to another account. |
989 | The new message is empty or over 1005 characters. |
511 | The account is not entitled to this operation. |
Field notes
What can and cannot be cancelled
Cancellation only reaches campaigns that have not gone out. A send with no timing is dispatched immediately and is past cancelling by the time you could call — 966. Scheduling with timing is what buys you the window.
Which handle should you keep?
A send returns shipment_id; cancellation takes campaign_id. In practice they refer to the same campaign, and the more robust habit is to set a unique campaign_name on every send and cancel by name — it works across a run split into several calls, and it does not depend on having stored an id.
Birthday campaigns are created in the console
The API lists them and rewrites their message. Creating one, setting its schedule, or choosing its audience is a console operation.

