Engineering5 Min Read
Three Thousand WhatsApp Messages a Day

Three Thousand WhatsApp Messages a Day

Aadesh Ingle
Try it yourselfMessage replay and lineage

Our logistics customer receives around three thousand WhatsApp messages and another five hundred emails each day. They contain freight demand in free text: a truck type, route, tonnage, and date expressed through abbreviations and multiple languages. The ops team was reading the messages and typing load requests into the CRM by hand.

At that volume, manual capture was slow and messages were missed. An uncaptured request could not be priced, assigned, or followed up, so the data loss had a direct commercial consequence.

An LLM extracts the fields from each message, but most of the engineering work went into moving those fields safely into the customer's system.

The pipeline we built

What we built is a queue-based ingestion pipeline. Messages come in from WhatsApp and Gmail, get queued, an LLM extracts the structured fields, the extracted entities get mapped against the customer's actual world (vehicle types, locations), and a CRM entry comes out the other end. No human in the typing loop.

Each stage earns its place:

  • The queue is there because traffic is bursty and providers hiccup. Messages can't be dropped just because a downstream service had a bad minute. Ingestion and processing had to fail independently and retry independently.
  • Extraction handles the messy part: free text in multiple languages, "32ft sxl needed nagpur to blr tmrw" and a hundred variations of it. This is where LLMs genuinely changed what's feasible. The old regex-and-rules approach dies a slow death against input like this.
  • Mapping is the stage people underestimate. The model can tell you the message mentions a truck type and two cities. It cannot tell you which canonical vehicle code and which location records those map to in this customer's system. Extraction without resolution is trivia. Resolution is what makes a row a CRM can act on.

The prompt stabilized early. We spent more time on retries, deduplication, ordering, and deciding what to do when the same load request arrived through two channels.

The replay story matters more than the prompt story. If the model or mapper improves next month, you should be able to replay yesterday's raw messages through the new pipeline and see exactly which rows would change. Without that, every improvement is also a migration you cannot measure.

Demand lineage

One message earns its way into the CRM

Trace the article’s representative freight message through extraction, resolution and duplicate handling.

Scenario

Representative article message with invented IDs and source time. Toggle delivery and location conditions to change the route to the CRM.

Sequence

  1. Keep the sourceCurrent
  2. Queue deliveryUpcoming
  3. Extract candidate fieldsUpcoming
  4. Resolve canonical recordsUpcoming
  5. Check demand identityUpcoming
  6. Commit with lineageUpcoming
Freight message field lineage and conditional CRM route
WhatsApp source16 Jan · 16:00 IST · msg-01
Queued eventRaw text + source metadata
Candidate fields32ft sxl → vehiclenagpur → originblr → destination18t → 18 tonnestmrw → 17 Jan 2026
Canonical dictionaryblr → loc-bengaluru
Identity checkNo existing association
New CRM demanddemand-01 · 1 new row
Source pointer survivesDate anchors to source timeIllustrative IDs and outcomes; no per-field accuracy claim.

Keep the source

“32ft sxl needed nagpur to blr tmrw 18t ready by evening call back urgent”. The illustrative source arrived 16 January 2026 at 16:00 IST. Keep its raw text and provider ID.

Read the full explanation

New delivery

Representative article message with invented IDs and source time. Toggle delivery and location conditions to change the route to the CRM.

  1. Keep the source. “32ft sxl needed nagpur to blr tmrw 18t ready by evening call back urgent”. The illustrative source arrived 16 January 2026 at 16:00 IST. Keep its raw text and provider ID.
  2. Queue delivery. A durable queued event separates intake from extraction, so downstream processing can retry.
  3. Extract candidate fields. 32ft sxl → vehicle; nagpur → origin; blr → destination; 18t → tonnage; tmrw → 17 January using source time in IST. Replaying later keeps that date.
  4. Resolve canonical records. In this illustrative dictionary, 32ft sxl maps to vehicle-32-sxl and blr maps to loc-bengaluru. These are example IDs, not production records.
  5. Check demand identity. No existing provider-ID association is present in this example. Check demand identity before creating a row; similar text alone is not proof of a duplicate.
  6. Commit with lineage. Create one CRM row with canonical IDs, source pointer and derivation versions. The original message remains available for replay.

Duplicate delivery

Representative article message with invented IDs and source time. Toggle delivery and location conditions to change the route to the CRM.

  1. Keep the source. “32ft sxl needed nagpur to blr tmrw 18t ready by evening call back urgent”. The illustrative source arrived 16 January 2026 at 16:00 IST. Keep its raw text and provider ID.
  2. Queue delivery. A redelivery carries the same source ID. Retrying delivery is not evidence of a second load request.
  3. Extract candidate fields. 32ft sxl → vehicle; nagpur → origin; blr → destination; 18t → tonnage; tmrw → 17 January using source time in IST. Replaying later keeps that date.
  4. Resolve canonical records. In this illustrative dictionary, 32ft sxl maps to vehicle-32-sxl and blr maps to loc-bengaluru. These are example IDs, not production records.
  5. Check demand identity. The provider ID already maps to example-demand-01. Preserve that association rather than creating another demand. If a new derivation is ambiguous, hold that proposed change without erasing the known link. A new message from another channel still needs a separate demand-identity check.
  6. Use the existing row. The duplicate creates zero new rows and retains its link to the same CRM demand. Retry-safe delivery does not imply automatic cross-channel deduplication.

Ambiguous location

Representative article message with invented IDs and source time. Toggle delivery and location conditions to change the route to the CRM.

  1. Keep the source. “32ft sxl needed nagpur to blr tmrw 18t ready by evening call back urgent”. The illustrative source arrived 16 January 2026 at 16:00 IST. Keep its raw text and provider ID.
  2. Queue delivery. A durable queued event separates intake from extraction, so downstream processing can retry.
  3. Extract candidate fields. 32ft sxl → vehicle; nagpur → origin; blr → destination; 18t → tonnage; tmrw → 17 January using source time in IST. Replaying later keeps that date.
  4. Resolve canonical records. In this illustrative customer dictionary, blr could mean the city or a depot. Keep both candidates and leave destination ID unset.
  5. Check demand identity. No existing provider-ID association is present in this example. Check demand identity before creating a row; similar text alone is not proof of a duplicate.
  6. Hold for review. This new derivation stops before an automatic CRM write. A reviewer chooses the destination using the raw message and retained candidates. Duplicate delivery does not remove the ambiguity; any known CRM association survives, and its existing row is not overwritten.

Duplicate + ambiguous

Illustrative reprocessing of an already-linked source under an ambiguous dictionary. Preserve the existing CRM association while reviewing the new derivation.

  1. Keep the source. “32ft sxl needed nagpur to blr tmrw 18t ready by evening call back urgent”. The illustrative source arrived 16 January 2026 at 16:00 IST. Keep its raw text and provider ID.
  2. Queue delivery. A redelivery carries the same source ID. Retrying delivery is not evidence of a second load request.
  3. Extract candidate fields. 32ft sxl → vehicle; nagpur → origin; blr → destination; 18t → tonnage; tmrw → 17 January using source time in IST. Replaying later keeps that date.
  4. Resolve canonical records. In this illustrative customer dictionary, blr could mean the city or a depot. Keep both candidates and leave destination ID unset.
  5. Check demand identity. The provider ID already maps to example-demand-01. Preserve that association rather than creating another demand. If a new derivation is ambiguous, hold that proposed change without erasing the known link. A new message from another channel still needs a separate demand-identity check.
  6. Hold for review. This new derivation stops before an automatic CRM write. A reviewer chooses the destination using the raw message and retained candidates. Duplicate delivery does not remove the ambiguity; any known CRM association survives, and its existing row is not overwritten.

New delivery

Representative article message with invented IDs and source time. Toggle delivery and location conditions to change the route to the CRM.

  1. Keep the source. “32ft sxl needed nagpur to blr tmrw 18t ready by evening call back urgent”. The illustrative source arrived 16 January 2026 at 16:00 IST. Keep its raw text and provider ID.
  2. Queue delivery. A durable queued event separates intake from extraction, so downstream processing can retry.
  3. Extract candidate fields. 32ft sxl → vehicle; nagpur → origin; blr → destination; 18t → tonnage; tmrw → 17 January using source time in IST. Replaying later keeps that date.
  4. Resolve canonical records. In this illustrative dictionary, 32ft sxl maps to vehicle-32-sxl and blr maps to loc-bengaluru. These are example IDs, not production records.
  5. Check demand identity. No existing provider-ID association is present in this example. Check demand identity before creating a row; similar text alone is not proof of a duplicate.
  6. Commit with lineage. Create one CRM row with canonical IDs, source pointer and derivation versions. The original message remains available for replay.

Duplicate delivery

Representative article message with invented IDs and source time. Toggle delivery and location conditions to change the route to the CRM.

  1. Keep the source. “32ft sxl needed nagpur to blr tmrw 18t ready by evening call back urgent”. The illustrative source arrived 16 January 2026 at 16:00 IST. Keep its raw text and provider ID.
  2. Queue delivery. A redelivery carries the same source ID. Retrying delivery is not evidence of a second load request.
  3. Extract candidate fields. 32ft sxl → vehicle; nagpur → origin; blr → destination; 18t → tonnage; tmrw → 17 January using source time in IST. Replaying later keeps that date.
  4. Resolve canonical records. In this illustrative dictionary, 32ft sxl maps to vehicle-32-sxl and blr maps to loc-bengaluru. These are example IDs, not production records.
  5. Check demand identity. The provider ID already maps to example-demand-01. Preserve that association rather than creating another demand. If a new derivation is ambiguous, hold that proposed change without erasing the known link. A new message from another channel still needs a separate demand-identity check.
  6. Use the existing row. The duplicate creates zero new rows and retains its link to the same CRM demand. Retry-safe delivery does not imply automatic cross-channel deduplication.

Ambiguous location

Representative article message with invented IDs and source time. Toggle delivery and location conditions to change the route to the CRM.

  1. Keep the source. “32ft sxl needed nagpur to blr tmrw 18t ready by evening call back urgent”. The illustrative source arrived 16 January 2026 at 16:00 IST. Keep its raw text and provider ID.
  2. Queue delivery. A durable queued event separates intake from extraction, so downstream processing can retry.
  3. Extract candidate fields. 32ft sxl → vehicle; nagpur → origin; blr → destination; 18t → tonnage; tmrw → 17 January using source time in IST. Replaying later keeps that date.
  4. Resolve canonical records. In this illustrative customer dictionary, blr could mean the city or a depot. Keep both candidates and leave destination ID unset.
  5. Check demand identity. No existing provider-ID association is present in this example. Check demand identity before creating a row; similar text alone is not proof of a duplicate.
  6. Hold for review. This new derivation stops before an automatic CRM write. A reviewer chooses the destination using the raw message and retained candidates. Duplicate delivery does not remove the ambiguity; any known CRM association survives, and its existing row is not overwritten.

Duplicate + ambiguous

Illustrative reprocessing of an already-linked source under an ambiguous dictionary. Preserve the existing CRM association while reviewing the new derivation.

  1. Keep the source. “32ft sxl needed nagpur to blr tmrw 18t ready by evening call back urgent”. The illustrative source arrived 16 January 2026 at 16:00 IST. Keep its raw text and provider ID.
  2. Queue delivery. A redelivery carries the same source ID. Retrying delivery is not evidence of a second load request.
  3. Extract candidate fields. 32ft sxl → vehicle; nagpur → origin; blr → destination; 18t → tonnage; tmrw → 17 January using source time in IST. Replaying later keeps that date.
  4. Resolve canonical records. In this illustrative customer dictionary, blr could mean the city or a depot. Keep both candidates and leave destination ID unset.
  5. Check demand identity. The provider ID already maps to example-demand-01. Preserve that association rather than creating another demand. If a new derivation is ambiguous, hold that proposed change without erasing the known link. A new message from another channel still needs a separate demand-identity check.
  6. Hold for review. This new derivation stops before an automatic CRM write. A reviewer chooses the destination using the raw message and retained candidates. Duplicate delivery does not remove the ambiguity; any known CRM association survives, and its existing row is not overwritten.

1 / 6 · Keep the source

Speed
TakeawayThe reported flow handles about 3,000 WhatsApp messages and 500 emails daily at about 97% average accuracy across the flow. That figure does not guarantee accuracy for each field.
Try it yourself

Replay a burst without multiplying demand

Change delivery and recovery conditions. Follow six invented source events through every processing attempt and CRM outcome.

2
1
2
7 days
Provider-ID idempotency
Destination dictionary for message 01
01
02
03
04
05
06

Columns = six source IDs. Each mark = a delivered copy. Select a mark to inspect its path. “Same row” is an idempotent no-op; “Queued” means the durable source is retained for recovery.

Sourcemsg-01Copy 1
Attempts2Worker recovered
CRM outcomeNew rowSource pointer retained
CRM rows created6Six unique demands in this toy batch
Duplicate writes avoided612 delivered copies
Held deliveries00 queued · 0 need resolution
Inspect message 01: source time survives replay
“32ft sxl needed nagpur to blr tmrw 18t”
Source arrival
16 Jan 2026, 16:00 IST
Replay day
2026-01-23
Derived pickup
2026-01-17. Always source + 1 day.
Destination ID
loc-bengaluru
Provenance
msg-01 → extractor-v1 → resolver-v1 → CRM outcome

Illustrative deterministic batch: six different demands, copied provider IDs, identical transient failures before any CRM write, durable intake, and one potentially ambiguous message. Exhausted copies remain queued; repeated reviews are delivery counts, not unique review tasks. Lost acknowledgements after a write need outcome reconciliation; this batch does not simulate them. Cross-channel business deduplication needs separate evidence. The article’s 97% is an observed flow average, not this simulation’s accuracy.

Extraction contractThe model is only one signer of the contract
CaseSourceValidatorFailure caught
Vehicle typeLLM candidate from message textCanonical vehicle dictionary + aliasesModel says "truck" when CRM needs a specific code.
LocationLLM candidate + abbreviation expansionCustomer location table and lane historyAmbiguous city, misspelling, duplicate depot name.
DateRelative phrase from messageMessage timestamp and business calendar"Tomorrow" drifting when replayed later.
Demand identitySource channel, sender, route, time window, extracted fieldsIdempotency key + duplicate searchSame demand creates two CRM rows.
Exception laneWhat happens when confidence is not enough

Trigger: missing vehicle type, ambiguous city, duplicate candidate, low extraction confidence, or a date that depends on replay time.

System action: keep the raw source message, write a pending record with provenance, and route only the uncertain fields for human correction.

Reviewer action: choose the canonical vehicle/location, mark duplicate-or-new, and leave the corrected field next to the original candidate.

Feedback: promote repeated corrections into alias dictionaries, eval cases, or validation rules. Do not let them disappear into the CRM.

This is the difference between automation and silent data corruption.
Replay recordThe raw message is the permanent fact

Raw source: channel, sender, provider message ID, timestamp, original text, attachments, and ingestion version.

Derived row: extraction model, prompt version, resolver version, canonical IDs, confidence, and idempotency key.

Replay mode: dry-run a new extractor or resolver against old messages, diff the CRM payload, and sample the changed rows before writing.

Rollback: keep enough provenance to explain why a CRM row exists and which source event produced it.

Reprocessing is how a data pipeline learns without losing the original evidence.

What it does now

The flow runs live for the business at about 97% average accuracy on the demand it processes. That's an average across the flow, not a guarantee on every field. At roughly 3,000 WhatsApp messages and 500 emails a day, the math is simple: structured demand capture happens at a scale and speed no ops team could match by hand, and the misses are the exception to manage rather than the default state of the system.

Evidence ledgerThe numbers should keep their metadata
Around 3,000 WhatsApp messages and 500 emails per daymeasured

Operational volume observed in the live ingestion channels.

About 97% average accuracymeasured

Average across the demand-processing flow; not a guarantee on every extracted field.

Dropped messages mean lost revenuebelieved

Operational judgment from the business workflow: uncaptured demand cannot be priced, assigned, or followed up.

Source trailWhat this post is in conversation with

At the current volume, the model call is only one possible failure point. A better extractor cannot recover a dropped webhook, distinguish duplicate messages, or map an unknown city to the customer's canonical records. Those are ordinary data-pipeline problems, and we monitor them as such.

The LLM made the message text tractable. The queue, resolver, idempotency key, and review lane made its output safe enough to enter the CRM. That division of responsibility is the part of this system I would reuse.

End of entry

Keep track of what you have read.

Discussion