Skip to content
GitHub
Get started →

Conversations

Every voice call from a customer install becomes a conversation row, kept 365 days unless deleted earlier. Use these endpoints to pull transcripts and recordings into your tools.

Auth: API Key. Scoped to the key’s site.

List conversations

GET /v1/public/conversations?limit=25&since=2026-05-01T00:00:00Z&has_recording=1
Authorization: Bearer sk_live_...

Query parameters

ParamTypeDefaultNotes
limitint251–100
cursorstringopaque from a previous next_cursor
sinceISO 8601started_at >= since
has_recordingboolonly conversations with an audio recording

Response

{
"data": [
{
"id": "conv_abc",
"site_id": "0aa06d96-...",
"started_at": "2026-05-07T11:00:00.000Z",
"ended_at": "2026-05-07T11:03:42.000Z",
"duration_seconds": 222,
"turn_count": 12,
"page_url": "https://example.com/contact",
"first_user_text": "Hi, I'd like to book a consultation",
"transcript": [
{ "role": "assistant", "text": "Hi! How can I help?", "ts_ms": 0 },
{ "role": "user", "text": "I'd like to book a consultation", "ts_ms": 1800 }
],
"tool_calls": [
{ "name": "submit_lead", "args": { "data": { "name": "Sarah", "email": "..." } } }
],
"recording_path": "0aa06d.../conv_abc.webm",
"recording_mime": "audio/webm",
"recording_bytes": 184320,
"created_at": "2026-05-07T11:03:42.000Z"
}
],
"next_cursor": null,
"has_more": false
}

Fetch one conversation

GET /v1/public/conversations/{id}
Authorization: Bearer sk_live_...

Same shape as a list item, but always includes the full transcript and tool_calls. Returns 404 if the conversation does not exist or is on a different site than the API key.

Get a recording URL

Recordings live in private Storage. To play one back, mint a signed URL:

GET /v1/public/conversations/{id}/recording
Authorization: Bearer sk_live_...
{
"data": {
"url": "https://...supabase.co/storage/v1/object/sign/conversation-recordings/...",
"expires_at": "2026-05-07T11:08:42.000Z"
}
}

The signed URL is valid for 5 minutes. Fetch it just before you need it — do not cache it.

Terminal window
# 1. Get the signed URL
SIGNED=$(curl -s -H "Authorization: Bearer $KEY" \
https://api.spelo.ai/v1/public/conversations/$ID/recording | jq -r '.data.url')
# 2. Download
curl -o conversation.webm "$SIGNED"

Recording availability

A recording exists when recording_path is non-null on the conversation row. This depends on:

  • site_configs.recording_enabled = true (Settings → Lead Capture)
  • the call lasted long enough for upload (calls under ~3s often skip recording)
  • upload succeeded (rare failures are logged but not retried)

The conversation.recording_ready webhook fires the moment a recording becomes available — subscribe to it instead of polling.

Retention

Conversations expire 365 days after started_at. The expires_at column on each row tells you when. A nightly cleanup job deletes expired rows AND the underlying Storage object — there is no recovery after expiry.

See also

  • Leads endpoints — leads captured during a conversation
  • Webhooksconversation.ended and conversation.recording_ready