Skipped answer is null — read form_status, not form_data
Skipped answer is null — read form_status, not form_data
Both unanswered questions and questions that were never reached appear as
null in form_data. This makes it impossible to distinguish “caller skipped this question” from “call ended before we got here” by looking at form_data alone.The trap is testing truthiness: if (form_data.field_name) fails silently for three legitimate answer values:0— a valid numeric answer""— a valid (if unusual) string answerfalse— a valid boolean answer
form_status.field_name === "answered" before using a value from form_data. A status of "answered" guarantees the caller provided a valid response. A status of "skipped" means the question was reached but not answered. If the key is absent from form_status, the question was never asked.screened_out counts as completed in analytics
screened_out counts as completed in analytics
The Keep this in mind when reporting conversion rates or SLA metrics — the dashboard number will be higher than your “fully answered” count.
completed counter in agent analytics includes both complete and screened_out calls. The completion_rate metric is built from this combined count.This is intentional — a screened-out caller did complete the relevant portion of the form (they gave an answer that matched an end_call_values trigger). But if you want to count only calls where the caller answered all questions all the way through, you need to subtract screened_out from completed yourself.created_at is last-write time, not call start time
created_at is last-write time, not call start time
created_at on a conversation record is updated every time the record is written — including when someone manually edits an answer via the API. It is not a fixed timestamp set at the moment the call was connected.This has two consequences:-
Sorting by
created_atsorts by most recent activity, not by when calls happened. A call from last month that was edited today appears at the top of acreated_at DESCsort. -
A past time window is never final. If you poll for all calls between yesterday 09:00 and 10:00, you will get a set of results. If you poll the same window an hour later, a call from that slot that was edited in the meantime will reappear — and a call that slipped in at exactly 10:00:00 originally might now be gone if its
created_atmoved.
conversation_id as your stable identifier. When a call comes back in a poll response, update your local record rather than inserting a new one. For polling new activity, use a since value slightly before your last successful poll timestamp to catch records whose created_at was bumped into the gap.A multi-select answer is a string, not a list
A multi-select answer is a string, not a list
When a caller answers a Not:If you pass the raw value to code that expects a list — an array Be precise with the delimiter — splitting on
multi-select question, FormBharo stores the result as a single comma-and-space-joined string — not as a JSON array..map(), a Python for loop, a database array column — it will fail or behave unexpectedly.What to do instead: Split on ", " (comma followed by a space) to reconstruct the list before using the value."," alone (no space) will leave leading spaces on every item after the first.