Skip to main content

SMS and Conversations

Two ways to send, one inbox to read. Send SMS from a receptionist's number or from your tenant's dedicated outbound number, then work replies through the conversations inbox.

Direct send

From a receptionist

POST /api/v1/receptionists/:id/sms sends an SMS from the receptionist's phone number.

Request body
{
"to": "+12125551234",
"message": "Your appointment is confirmed!"
}

Both fields are required. to must be E.164; message is capped at 1600 characters.

Response
{
"id": "msg_abc123",
"status": "sent",
"to": "+12125551234",
"from": "+16145551234",
"message": "Your appointment is confirmed!",
"sent_at": "2026-03-11T15:00:00Z"
}

GET /api/v1/receptionists/:id/sms returns paginated SMS history for the receptionist, with limit (1-100, default 20), cursor, and phone (filter by contact phone number) query parameters.

From your dedicated outbound number

POST /api/v1/sms/send sends from your tenant's dedicated outbound number. Use this when you don't need a receptionist context, such as reminders, customer replies, or agent-driven texts. Scope: send_sms.

Request body
{
"to": "+12125551234",
"message": "Hi there! See you tomorrow.",
"lead_id": "uuid"
}

to and message (max 1600 characters) are required; lead_id optionally associates the message with a lead.

Response
{
"id": "msg_abc123",
"status": "sent",
"from": "+16145551234",
"to": "+12125551234",
"message": "Hi there! See you tomorrow.",
"provider": "telnyx",
"sent_at": "2026-04-20T15:00:00Z"
}

Opt-outs return 422

TCPA compliance

The API checks opt-out status before every send. If the recipient has texted STOP, the request returns 422 and you must skip that contact. Opt-outs cannot be overridden via the API; the customer must text START to re-subscribe.

Conversations (inbox)

Every conversation is tenant-scoped and keyed by the customer's E.164 phone number.

List conversations

GET /api/v1/conversations lists inbox conversations. Keyset-paginated, newest first.

ParameterDescription
limitInteger, 1-100, default 50
cursorFrom a previous response
statusactive, closed, archived, or opted_out
phoneExact E.164 match, e.g. +12125551234
Response
{
"data": [
{
"id": "uuid",
"customer_phone": "+12125551234",
"customer_name": "Jane Doe",
"status": "active",
"unread_count": 2,
"has_inbound": true,
"last_message_at": "2026-04-22T14:10:00Z",
"intent_score": "warm",
"intent_summary": "Wants a quote for water heater install",
"ai_chatbot_active": true,
"created_at": "2026-04-20T10:00:00Z",
"updated_at": "2026-04-22T14:10:00Z"
}
],
"has_more": false
}

Get a conversation

GET /api/v1/conversations/:id returns the conversation with its 20 most recent messages inline (oldest first within the page).

Response (excerpt)
{
"id": "uuid",
"customer_phone": "+12125551234",
"customer_name": "Jane Doe",
"status": "active",
"unread_count": 0,
"messages": [
{
"id": "uuid",
"direction": "inbound",
"from_phone": "+12125551234",
"to_phone": "+16145550000",
"body": "Hey, is this still available?",
"status": "received",
"segments": 1,
"created_at": "2026-04-22T14:09:40Z"
}
]
}

Message history for a thread

GET /api/v1/conversations/:id/messages returns paginated message history for one conversation. Keyset-paginated, newest first. Use limit and cursor.

Response
{
"data": [
{
"id": "uuid",
"conversation_id": "uuid",
"campaign_id": null,
"direction": "outbound",
"from_phone": "+16145550000",
"to_phone": "+12125551234",
"body": "Thanks, talk soon!",
"status": "delivered",
"segments": 1,
"cost_cents": 1,
"ai_handled": false,
"created_at": "2026-04-22T14:12:00Z"
}
],
"has_more": true,
"cursor": "eyJjcmVhdGVkX2F0IjoiMjAyNi0w..."
}

Reply into a conversation

POST /api/v1/conversations/:id/reply sends an SMS reply into an existing conversation. Scope: send_sms. The recipient is resolved from the conversation row, so no to field is required. The from-number is your tenant's dedicated SMS number. Respects opt-outs (422).

Request body
{
"message": "We have an 8am slot tomorrow if that works."
}
Response
{
"id": "provider-message-id",
"conversation_id": "uuid",
"status": "sent",
"from": "+16145550000",
"to": "+12125551234",
"message": "We have an 8am slot tomorrow if that works.",
"provider": "telnyx",
"sent_at": "2026-04-22T14:20:00Z"
}
Compliance footer

The compliance footer cannot be suppressed via the public API.

Update conversation status

PATCH /api/v1/conversations/:id updates a conversation's status. Scope: write_crm. Valid values: open, closed, archived. open maps to the internal active state for UI compatibility. opted_out is set by inbound STOP processing only and cannot be set here.

Request body
{ "status": "closed" }
Response
{
"id": "uuid",
"customer_phone": "+12125551234",
"status": "closed",
"updated_at": "2026-04-22T15:00:00Z"
}

Tenant-wide message feed

GET /api/v1/sms/messages returns a flat, tenant-wide message feed across every conversation. Useful for analytics and incremental sync. Keyset-paginated, newest first.

ParameterDescription
limitInteger, 1-100, default 50
cursorFrom a previous response
directioninbound or outbound
sinceISO 8601 timestamp, e.g. 2026-04-01T00:00:00Z
Example
curl -H "Authorization: Bearer $AUTOREV_API_KEY" \
"https://app.autorev.ai/api/v1/sms/messages?direction=inbound&since=2026-04-20T00:00:00Z"
Response
{
"data": [
{
"id": "uuid",
"conversation_id": "uuid",
"campaign_id": null,
"direction": "inbound",
"from_phone": "+12125551234",
"to_phone": "+16145550000",
"body": "Yes please",
"status": "received",
"segments": 1,
"created_at": "2026-04-22T09:05:00Z"
}
],
"has_more": false
}