Start a textdemo conversation
Starts a textdemo conversation: records implied-by-submit
consent for the platform-owned demo organization and fires the
agent's opening SMS to contact_number, both inside a single
transaction so the consent audit row and the queued greeting
commit together or not at all.
This endpoint is called server-to-server by the AgentMessage
marketing-site proxy, never by a browser directly. The proxy
fills the evidence_* and page_url fields from the inbound
browser request; the demo organization and demo sending number
are server-side configuration and are never accepted from the
request body.
contact_number is normalized to E.164 before validation, so a
national-format US number submitted by the form still resolves.
The agreement_text blob is the exact opt-in language the user
agreed to; its SHA-256 is stored on the consent-history row as
tamper-evident evidence (the raw text is not persisted).
On a brand-new contact the conversation is opened, the canned
greeting is enqueued, and the call returns 202 Accepted with
conversation_started: true and the queued greeting's
outbound_message_id. On a contact that already has demo history
the call is an idempotent no-op (no second greeting) and returns
200 OK with conversation_started: false.
Requires the demo:write scope. The route is served only when
the textdemo live-SMS path is enabled
(OPENAI_API_KEY + AM_TEXTDEMO_DEMO_ORG_ID +
AM_TEXTDEMO_DEMO_NUMBER all set); otherwise it is unmounted.
Full flow: docs/textdemo.md.
Authenticate by sending your API key as a bearer token:
Authorization: Bearer am_live_.... Every request is automatically
scoped to the organization that owns the key and to the scopes granted
to that key.
In: header
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
application/json
application/json
application/json
curl -X POST "https://example.com/v1/demo/start" \ -H "Content-Type: application/json" \ -d '{ "contact_number": "+15554443333", "name": "Jamie Rivera", "agreement_text": "I agree to receive a text-message demo from AgentMessage. Msg & data rates may apply. Reply STOP to opt out.", "captured_at": "2026-06-03T15:23:01Z", "evidence_ip": "203.0.113.42", "evidence_user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)", "page_url": "https://agentmessage.io/demo" }'{
"success": true,
"data": {
"consent_id": "0190a1b2-c3d4-e5f6-a7b8-c9d0e1f2a3b4",
"conversation_started": false,
"message": "conversation already active"
}
}{
"success": true,
"data": {
"consent_id": "0190a1b2-c3d4-e5f6-a7b8-c9d0e1f2a3b4",
"conversation_started": true,
"message": "greeting enqueued",
"outbound_message_id": "8f3a2b1c-1c2d-4e5f-9a8b-0c1d2e3f4a5b"
}
}{
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "authentication failed",
"request_id": "01JTBQH2FZ8K1RXC0WJ4Z9P3VM"
}
}{
"success": false,
"error": {
"code": "FORBIDDEN",
"message": "missing required scope",
"request_id": "01JTBQH2FZ8K1RXC0WJ4Z9P3VM"
}
}{
"success": false,
"error": {
"code": "VALIDATION_FAILED",
"message": "validation failed",
"request_id": "01JTBQH2FZ8K1RXC0WJ4Z9P3VM",
"details": {
"to": "must be E.164",
"body": "must be 1..1600 chars"
}
}
}