Documentation
API Reference
SEO-indexable reference for TextTree message, number, webhook, contact, and billing APIs.
curl https://api.texttree.ai/api/v1/messages \
-H "Authorization: Bearer $TEXTREE_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"phone_number":"+15551234567","body":"Hello"}' Authentication
Use a TextTree-issued txt_... bearer access token. Tokens must carry the
route’s required scope, such as messages:write, numbers:read,
numbers:write, mcp:read, or mcp:execute. Legacy txk_... API keys are
not accepted by developer API or MCP routes.
Authorization: Bearer $TEXTREE_ACCESS_TOKEN
Create a headless account
POST /api/v1/accounts
POST /mcp/accounts
These public JSON endpoints create a username/password agent account without
email, Google OAuth, or wallet auth. Both routes call the same bootstrap
workflow and return one-time backup codes plus a txt_... TextTree bearer
token.
Required fields:
usernamepassword
/api/v1/accounts accepts fields at the top level. /mcp/accounts accepts the
same fields at the top level or nested under account.
curl
curl https://api.texttree.ai/api/v1/accounts \
-H "Content-Type: application/json" \
-d '{
"username": "agent-demo",
"password": "correct horse battery staple"
}'
Response:
{
"account": {
"id": "9d7d9df7-58a0-4716-b82e-7ad5e73f7b36",
"username": "agent-demo"
},
"backup_codes": ["AAAA-BBBB-CCCC-DDDD"],
"token": {
"type": "Bearer",
"access_token": "txt_...",
"scopes": ["mcp:read", "mcp:execute", "messages:write"],
"expires_at": "2026-06-03T15:30:00Z"
}
}
Account bootstrap is limited to one successful account creation per IP address
every five minutes. Repeated successful creation attempts return
429 account_creation_rate_limited with retry_after_seconds.
Send SMS
POST /api/v1/messages
Required fields:
phone_numberbody
Optional fields:
idempotency_keymetadata
API sends use automatic sender selection. App-originated sends can select a connected branded number; TextTree treats the selected sender as part of the idempotent message request.
curl
curl https://api.texttree.ai/api/v1/messages \
-H "Authorization: Bearer $TEXTREE_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"phone_number": "+15551234567",
"body": "Your appointment is tomorrow at 9 AM.",
"idempotency_key": "appointment-123-reminder"
}'
JavaScript
const response = await fetch("https://api.texttree.ai/api/v1/messages", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TEXTREE_ACCESS_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
phone_number: "+15551234567",
body: "Your appointment is tomorrow at 9 AM.",
idempotency_key: "appointment-123-reminder",
}),
});
const { message } = await response.json();
Python
import os
import requests
response = requests.post(
"https://api.texttree.ai/api/v1/messages",
headers={"Authorization": f"Bearer {os.environ['TEXTREE_ACCESS_TOKEN']}"},
json={
"phone_number": "+15551234567",
"body": "Your appointment is tomorrow at 9 AM.",
"idempotency_key": "appointment-123-reminder",
},
timeout=10,
)
message = response.json()["message"]
Elixir
Mix.install([:req])
%{body: %{"message" => message}} =
Req.post!(
"https://api.texttree.ai/api/v1/messages",
auth: {:bearer, System.fetch_env!("TEXTREE_ACCESS_TOKEN")},
json: %{
phone_number: "+15551234567",
body: "Your appointment is tomorrow at 9 AM.",
idempotency_key: "appointment-123-reminder"
}
)
Response:
{
"message": {
"id": "msg_123",
"status": "queued"
}
}
Message status
GET /api/v1/messages/{id}
curl
curl https://api.texttree.ai/api/v1/messages/msg_123 \
-H "Authorization: Bearer $TEXTREE_ACCESS_TOKEN"
Response:
{
"message": {
"id": "msg_123",
"status": "delivered",
"phone_number": "+15551234567",
"segments": 1,
"cost_cents": 1,
"created_at": "2026-04-28T14:00:00Z"
}
}
Use the Messages page for timeline, webhook, and failure debugging.
Reusing an idempotency_key with a different recipient, body, cost, or selected
sender returns an idempotency conflict.
Phone numbers
GET /api/v1/numbers
POST /api/v1/numbers
GET requires numbers:read. POST requires numbers:write and buys a
number through TextTree’s messaging provider boundary. area_code is supported
for local-number purchase intent. Live buys require the deployment’s explicit
live-number-buy guardrail.
curl https://api.texttree.ai/api/v1/numbers \
-H "Authorization: Bearer $TEXTREE_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"area_code": "415",
"friendly_name": "Support line"
}'
Response:
{
"number": {
"id": "num_123",
"number": "+14155550123",
"friendly_name": "Support line",
"status": "connected",
"compliance_status": "pending"
},
"webhook_configured": false,
"warning": "provider_inbound_webhook_configuration_api_not_documented"
}