Skip to content

SMS Enrichment

SMS enrichment analyzes a contact’s message history to extract profile data — name, email, company, sentiment, and more. It fills empty contact fields without overwriting user-entered data, then creates an AI Enrichment note and updates the contact memory.

Trigger enrichment (individual or bulk)
→ Load all SMS messages for the contact
→ Format as chronological transcript (INBOUND/OUTBOUND labels)
→ Send to Gemini 3.1 Flash Lite via OpenRouter
→ Parse structured response (15 extracted fields)
→ Fill empty contact fields (never overwrite existing)
→ Create AiEnrichment note with extracted insights
→ Update contact memory with conversation analysis

The AI extracts a ConversationContactInfo struct with these fields:

FieldTypeFill Rule
first_nameStringOnly if contact’s first_name is empty
last_nameStringOnly if contact’s last_name is empty
emailOption<String>Only if contact has no existing emails
companyOption<String>Only if company is None
job_titleOption<String>Only if job_title is None
preferred_languageOption<String>Only if preferred_language is None
timezoneOption<String>Only if timezone is None
genderStringOnly if gender is None and confidence is "high"
pipeline_stageStringOnly if pipeline_stage is None; validated against: lead, prospect, qualified, customer, churned
ai_summaryOption<String>Always overwritten (derived AI field)
interestsVec<String>Note only (not stored on contact)
next_stepsVec<String>Note only
key_datesVec<String>Note only
pain_pointsVec<String>Note only
sentimentStringNote only ("positive", "neutral", "negative")

Fields like interests, next_steps, key_dates, pain_points, and sentiment are written to the AiEnrichment note and contact memory but not stored as contact fields.

POST /api/contacts/{contact_id}/enrich-from-messages

Enriches a single contact. Any authenticated member can call this. Returns 200 OK on success. Fails with 400 if the contact has no messages with text content.

Service: enrich_contact_from_messages(contact_id, organization_id) in src/mods/contact/services/ai/enrich_contact_from_messages_service.rs

POST /api/contacts/bulk-enrich-from-messages

Enriches multiple contacts in parallel. Owner or super admin only.

Request:

pub struct BulkEnrichRequest {
pub contact_ids: Vec<Uuid>, // max 50
pub batch_id: Uuid, // client-generated, used to track progress
}

Response:

pub struct BulkEnrichResult {
pub batch_id: Uuid,
pub total: usize,
pub succeeded: usize,
pub failed: usize,
pub failed_ids: Vec<Uuid>,
}

Validation: All contact IDs must belong to the caller’s organization. Maximum 50 contacts per request.

Concurrency: Processes up to 10 contacts simultaneously using futures_util::stream::buffer_unordered. Individual failures are caught and counted — they don’t abort the batch.

During bulk enrichment, the server publishes a progress event after each contact completes:

Event type: contact.enrich.progress

pub struct BulkEnrichProgress {
pub batch_id: Uuid,
pub contact_id: Uuid,
pub succeeded: bool,
pub completed: usize,
pub total: usize,
}

Events are published via event_hub().publish_org(org_id, ...) and delivered to all connected clients in the organization through the existing WebSocket infrastructure.

An “Enrich from SMS” button in the contact detail sidebar. Disabled when the contact has no messages.

#[component]
pub fn ContactEnrichSection(
contact_id: Uuid,
has_messages: bool,
on_enriched: EventHandler,
) -> Element

Located in src/mods/contact/components/ai/contact_enrich_section_component.rs.

A floating bottom bar that appears when contacts are selected in the assignments view. Shows selection count → progress bar → completion summary.

#[component]
pub fn BulkEnrichBar(
selected_ids: Signal<Vec<Uuid>>,
on_enriched: EventHandler,
) -> Element

The bar has three states:

  1. Idle — shows count and “Enrich from SMS” button
  2. Processing — shows a ProgressBar driven by WebSocket events
  3. Done — shows succeeded/failed counts with a dismiss button

Only renders for owners and super admins (matches the API restriction).

Located in src/mods/contact/components/ai/bulk_enrich_bar_component.rs.

A reusable horizontal progress bar component:

#[component]
pub fn ProgressBar(
value: f64, // 0.0 to 1.0
#[props(default)] class: String,
#[props(default)] label: Option<String>,
) -> Element

Located in src/ui/progress_bar_ui.rs.

SettingValue
AI modelgoogle/gemini-3.1-flash-lite-preview via OpenRouter
API keyopenrouter_api_key in core_conf table
Bulk concurrency10 (hardcoded as BULK_ENRICH_CONCURRENCY)
Max batch size50 contacts
Message truncation500 chars per message body

Each enrichment creates two artifacts:

  1. AiEnrichment note — a user-visible note with origin: Workflow, area: AiEnrichment, containing updated fields, summary, sentiment, interests, next steps, key dates, and pain points
  2. Contact memory update — appends the full extraction to the contact’s system memory note via update_contact_memory_after_call