Documentation

Developer API

Message send and status contracts for the current Textree alpha API.

Copy-paste API examples
curl JavaScript Python Elixir
curl
curl https://api.texttree.ai/api/v1/messages \
  -H "Authorization: Bearer $TEXTREE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"phone_number":"+15551234567","body":"Hello"}'

Developer API

The current JSON API is intentionally narrow. It is focused on one production-critical path: queueing outbound SMS and checking its status.

Base URL

Local Phoenix development runs at http://localhost:4001.

All documented developer endpoints live under /api/v1.

Authentication

Authenticated API calls require a Textree-issued bearer access token. The same local identity model is used for the browser app, developer API, and MCP routes.

Authorization: Bearer <textree_access_token>
Accept: application/json

POST /api/v1/messages requires messages:write. Number inventory endpoints require numbers:read or numbers:write. Email, Google, wallet, and agent identities are normalized into the same local Textree identity before authorization.

Health

GET /api/v1/health

This route is public and is useful for deployment checks and local smoke tests.

Queue an outbound message

POST /api/v1/messages

Request body

{
  "phone_number": "+15551234567",
  "body": "Your verification code is 482019",
  "estimated_cost_cents": 2,
  "idempotency_key": "msg_2026_04_26_0001",
  "metadata": {
    "campaign": "alpha-invite",
    "source": "api"
  }
}

Fields

  • phone_number: required E.164 recipient phone number
  • body: required string, 1 to 1600 chars
  • estimated_cost_cents: optional positive integer, defaults to the configured SMS cost
  • idempotency_key: optional string, 8 to 128 chars, scoped per user
  • metadata: optional JSON object

phone_number is the V1 recipient field. Do not send to unless a future API version explicitly documents it.

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 verification code is 482019",
    "idempotency_key": "msg_2026_04_26_0001"
  }'

Success responses

Newly queued messages return 202 Accepted:

{
  "message": {
    "id": "8f3d0f4f-5ab3-4db9-bf6a-92c72bc1f2bb",
    "status": "queued",
    "phone_number": "+15551234567",
    "body": "Your verification code is 482019",
    "provider": "messaging_provider",
    "consent_status": "missing",
    "external_id": null,
    "estimated_cost_cents": 2,
    "idempotency_key": "msg_2026_04_26_0001",
    "metadata": {
      "campaign": "alpha-invite",
      "source": "api"
    },
    "inserted_at": "2026-04-26T20:44:12Z",
    "updated_at": "2026-04-26T20:44:12Z",
    "links": {
      "self": "/api/v1/messages/8f3d0f4f-5ab3-4db9-bf6a-92c72bc1f2bb"
    }
  },
  "replayed": false
}

If the same user replays the same idempotency_key with the same message payload, Textree returns 200 OK with the existing message and "replayed": true.

Error responses

  • 422 with {"error":"validation_failed","details":...} when the body is malformed
  • 403 with {"error":"workspace_suppressed"} when the recipient is blocked by a workspace suppression
  • 402 with {"error":"spend_limit_exceeded"} when the current spend cap would be exceeded
  • 409 with {"error":"idempotency_conflict"} when an idempotency key is reused with a different recipient, body, or cost

Fetch message status

GET /api/v1/messages/:id

This returns the same message envelope used in create responses.

The current message status lifecycle is:

  • queued
  • dispatching
  • sent
  • delivered
  • failed
  • blocked

external_id is populated once provider-facing execution has a stable provider identifier.

Status example

curl https://api.texttree.ai/api/v1/messages/8f3d0f4f-5ab3-4db9-bf6a-92c72bc1f2bb \
  -H "Authorization: Bearer $TEXTREE_ACCESS_TOKEN"
{
  "message": {
    "id": "8f3d0f4f-5ab3-4db9-bf6a-92c72bc1f2bb",
    "status": "delivered",
    "phone_number": "+15551234567",
    "estimated_cost_cents": 2,
    "links": {
      "self": "/api/v1/messages/8f3d0f4f-5ab3-4db9-bf6a-92c72bc1f2bb"
    }
  }
}

Before you send

The message API is not self-bootstrapping yet. Before this endpoint can succeed for a new workspace, an operator must:

  • authenticate through Textree and include a token with messages:write
  • confirm the recipient is not under an active workspace suppression
  • set an active spend limit in /app
  • configure a test sender or live sender number

Numbers API

GET /api/v1/numbers requires numbers:read.

POST /api/v1/numbers requires numbers:write and buys a number through the Textree messaging provider boundary. Live buys remain blocked unless the deployment explicitly enables the 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"
  }'

Successful buys return 201 Created:

{
  "number": {
    "id": "b86fbe4d-2b92-4664-8e91-d2ed1d37f827",
    "number": "+14155550123",
    "friendly_name": "Support line",
    "capabilities": ["sms"],
    "status": "connected",
    "inbound_webhook_url": "https://app.texttree.ai/webhooks/provider/incoming",
    "compliance_status": "pending"
  },
  "webhook_configured": true
}

Current alpha boundaries

The developer API does not currently expose:

  • legacy consent/contact metadata CRUD endpoints
  • workspace-suppression CRUD endpoints
  • spend-limit CRUD endpoints
  • public funding-session creation