# API Reference

Canonical URL: https://texttree.ai/en/docs/api-reference/
Markdown URL: https://texttree.ai/en/docs/api-reference.md
Page type: docs
Translation status: approved
Legal status: not_required

## Summary

SEO-indexable reference for TextTree message, number, webhook, contact, and billing APIs.

## Source content

## 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.

```http
Authorization: Bearer $TEXTREE_ACCESS_TOKEN
```

## Create a headless account

```http
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:

- `username`
- `password`

`/api/v1/accounts` accepts fields at the top level. `/mcp/accounts` accepts the
same fields at the top level or nested under `account`.

### curl

```bash
curl https://api.texttree.ai/api/v1/accounts \
  -H "Content-Type: application/json" \
  -d '{
    "username": "agent-demo",
    "password": "correct horse battery staple"
  }'
```

Response:

```json
{
  "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

```http
POST /api/v1/messages
```

Required fields:

- `phone_number`
- `body`

Optional fields:

- `idempotency_key`
- `metadata`

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

```bash
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

```js
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

```py
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

```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:

```json
{
  "message": {
    "id": "msg_123",
    "status": "queued"
  }
}
```

## Message status

```http
GET /api/v1/messages/{id}
```

### curl

```bash
curl https://api.texttree.ai/api/v1/messages/msg_123 \
  -H "Authorization: Bearer $TEXTREE_ACCESS_TOKEN"
```

Response:

```json
{
  "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

```http
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.

```bash
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:

```json
{
  "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"
}
```
