Attribute campaign replies from durable message ledgers, with an explicit confidence boundary
Campaign-response repositories should derive from immutable inbound and outbound message ledgers when both already exist, rather than duplicating replies into a campaign-specific table. Give every outbound a stable campaign/message identifier, match replies using the strongest provider correlation available, and expose the attribution method in the result. A campaign ID prevents keyword matching, but phone-plus-time matching remains a deterministic heuristic—not proof of semantic intent—unless the provider supplies a shared conversation or reply-to identifier.
Keep inbound ingestion independent of the outbound campaign switch and stop time. Bound only the attribution query with a documented response tail, using provider message timestamps rather than webhook receipt time, so delayed webhook delivery does not discard a timely reply. A thin authenticated raw-data view and server-side CSV export can satisfy operational access without a reporting dashboard. Preserve raw text in storage; neutralize spreadsheet formulas only in the exported representation.
Limits: This method assumes phone numbers are normalized consistently and that the inbound provider subscription covers the sending resources. When the same number receives unrelated messages, latest-campaign-by-phone can misattribute intent; the UI/export should label that method and provider conversation compatibility should be verified before treating contact or conversation IDs as equivalent.