--- title: "Thread Events" description: "Get the finished SMS and WhatsApp conversation, including transcript, outcome, evaluations, and follow-up history." publishedAt: "2026-08-22" modifiedAt: "2026-08-22" category: "Reference" tags: "Voicetta, thread.completed, SMS, WhatsApp, evaluations, close_reason, webhook" navOrder: "34" canonical: "https://voicetta.com/developers/thread-events" --- # Thread Events A `thread.completed` event represents a finished SMS or WhatsApp conversation. Messages tell you what happened during the conversation. The thread tells you how the conversation ended. It can contain: * the transcript * summary * intent * outcome * sentiment * evaluation results * follow-ups * cost information ## When it fires A thread is not completed when the last message arrives. Voicetta waits for the conversation to become inactive before grading it. The default inactivity period is 30 minutes. If a recovery follow-up is needed, the conversation can stay open longer. Expect `thread.completed` roughly 30 minutes after the conversation goes quiet, unless a follow-up extends it. If you need the live conversation, use message events. ## Payload ```json { "event": "thread.completed", "event_id": "c82fa013-5b7d-4029-8e4f-8d3c6b5f9a42", "version": 1, "occurred_at": "2026-08-20T14:45:00+00:00", "thread": { "thread_id": "c82fa013-5b7d-4029-8e4f-8d3c6b5f9a42", "workspace_id": "2a1f8e7d-4c3b-4a29-9f18-6d5e4c3b2a19", "assistant_id": "7c6b5a49-3e2d-4c1b-8a97-5f4e3d2c1b09", "assistant_name": "Front Desk", "channel": "whatsapp", "close_reason": "graded", "message_count": 6, "started_at": "2026-08-20T14:12:00+00:00", "last_message_at": "2026-08-20T14:19:00+00:00", "completed_at": "2026-08-20T14:45:00+00:00", "anchor_message_id": "b71e9d02-4a6c-4f18-9d3e-7c2b5a4e8f31", "customer": { "customer_id": "5d4c3b2a-1f09-4e8d-7c6b-5a493e2d1c0b", "name": "Jane Texter", "phone": "+15551234567" }, "transcript": [ { "role": "user", "content": "Do you have a room free on Friday?" }, { "role": "assistant", "content": "Yes, we have a double available." } ], "analysis": { "summary": "Customer asked about Friday availability and held a double room.", "intent": "booking_enquiry", "outcome": "booked", "sentiment": "positive" }, "evaluations": [ { "evaluation_id": "3e2d1c0b-5a49-4f8e-9d7c-6b5a493e2d1c", "title": "Offered a booking reference", "result": "pass", "initial_result": "fail", "rationale": "The reference was given after the follow-up.", "recovered": true } ], "costs": { "currency": "USD", "total": "0.0300", "charged_message_count": 3 }, "follow_ups": [ { "status": "sent", "channel": "whatsapp", "sent_at": "2026-08-20T14:40:00+00:00" } ] } } ``` ## Reading the fields * `thread_id` is the conversation id. * `thread_id` is also present on message events. * `anchor_message_id` identifies the message associated with the grading. * `completed_at` is when grading finished. * `last_message_at` is when the conversation went quiet. * `message_count` is the number of messages in the transcript. * `evaluations[].recovered` tells you whether a failed criterion was fixed by a follow-up. ## Costs `costs.total` is an indicative value based on message usage. It is not the amount charged on your invoice. Use your invoice for billing. ## `close_reason` Always read `close_reason`. Not every completed thread has evaluation results. | Reason | Meaning | Graded? | |---|---|---| | `graded` | Normal completed conversation | Yes | | `no_reply_after_follow_up` | Recovery follow-up was not answered | Yes | | `customer_opted_out` | Customer opted out | Partly | | `no_enabled_evaluations` | No evaluations were enabled | No | | `empty_transcript` | Nothing could be graded | No | | `grading_failed` | Grading failed | No | | `follow_up_failed` | Recovery follow-up failed | No | | `follow_up_daily_cap` | Daily follow-up limit was reached | No | | `follow_up_no_criteria` | Nothing needed recovery | Partly | | `follow_up_no_customer_phone` | No phone number was available | No | | `follow_up_missing_openai_key` | Follow-up generation was not configured | No | | `follow_up_send_ambiguous` | Follow-up delivery could not be confirmed | No | | `follow_up_skipped:` | Follow-up was skipped | No | Do not treat every thread as graded. For quality reporting, use `graded` and `no_reply_after_follow_up` as your graded population. Treat the other close reasons as not graded. ## Why ungraded threads are still delivered A conversation happened even if grading failed. We still send it. That gives your product a complete picture of communication instead of silently hiding conversations because evaluation did not run. ## What threads do not contain Threads do not contain: * recordings * voice latency * voice configuration * `is_test` Those fields only apply to voice calls. ## Reading threads through the API ```text GET /v1/threads ``` You can filter by date, assistant, channel, and `close_reason`. Use: ```text close_reason=graded ``` when you only want conversations that produced evaluation results. ## Related pages * [Message Events](/developers/message-events) * [Webhook Reference](/developers/webhooks) * [Read API Reference](/developers/api-reference) * [Overview](/developers/overview)