External SMS Capture
When you use a BYO Twilio account, SMS may be sent through other platforms (Twilio Console, another CRM, custom scripts). Loquent captures these external messages automatically through the same event sink used for delivery status tracking.
How It Works
Section titled “How It Works”When a status event arrives for a message Loquent didn’t send, it creates the record instead of ignoring it:
Twilio status event (sent/delivered/failed) → Look up message by external_id → Not found? → This is an external message → Determine direction from phone ownership → Resolve or create contact → Create message record (source: "external", body: null) → Publish EVT_MESSAGE_NEW event → Background: fetch body from Twilio API → Background: update contact last_contacted_atPreviously, status events for unknown messages were silently dropped. Now they trigger message creation with the source field set to "external".
Message Source Field
Section titled “Message Source Field”The source field on message tracks where a message originated:
| Value | Meaning |
|---|---|
"loquent" | Sent through Loquent’s UI or text agent |
"external" | Sent via another platform (Twilio Console, third-party CRM) |
"import" | Imported during message history sync |
The field is nullable — messages created before this feature have source: null.
Body Enrichment
Section titled “Body Enrichment”External messages arrive without body text because Twilio status events don’t include it. Loquent fetches the body asynchronously:
- After creating the message, a background task calls the Twilio Messages API:
GET /Accounts/{account_sid}/Messages/{message_sid}.json
- Updates the message record with the body text
- Publishes an
EVT_MESSAGE_STATUSevent so the UI populates the message content
pub async fn fetch_twilio_message_body( account_sid: &str, auth_token: &str, message_sid: &str,) -> Result<Option<String>, AppError>Uses HTTP Basic Auth with the org’s Twilio credentials. Returns None if the message body is empty.
Race Condition Handling
Section titled “Race Condition Handling”Multiple Twilio events for the same message SID can arrive concurrently (e.g., sent and delivered in rapid succession). Loquent handles this with a unique constraint on (organization_id, external_id):
match create_message(input).await { Ok(msg) => msg, Err(AppError::Database(ref e)) if is_unique_violation(e) => { // Another event already created it — update status instead update_message_status(&message_sid, status).await? } Err(e) => return Err(e),}This ensures exactly one message record per Twilio SID, regardless of event ordering.
Real-Time Contact List Updates
Section titled “Real-Time Contact List Updates”The messaging sidebar subscribes to EVT_MESSAGE_NEW events and automatically re-fetches the contact list when external messages arrive. Contacts with new messages move to the top of the list.
let ctx = use_context::<RealtimeContext>();let mut refresh_key = use_signal(|| 0u32);
use_effect(move || { let events = (ctx.events)(); // Check for new message events since last seen if has_new_message_events { refresh_key += 1; // Triggers contact list re-fetch }});The contact list resource depends on refresh_key, so incrementing it triggers a fresh API call and re-sort by last_contacted_at.
SSE Events
Section titled “SSE Events”| Event | Constant | When Published |
|---|---|---|
messaging.message.new | EVT_MESSAGE_NEW | External message created (body may be null initially) |
messaging.message.status | EVT_MESSAGE_STATUS | Body enriched or status updated |
The conversation feed hook (use_realtime_messages) handles both: it appends new messages and updates existing ones as body text and status arrive.
End-to-End Flow
Section titled “End-to-End Flow”- User sends SMS via Twilio Console (not Loquent)
- Twilio fires a
message.sentevent to/twilio/events handle_status_update()finds no matching message in the database- Determines direction:
fromis an org number → outbound - Resolves or creates a contact for the
tonumber - Creates a message with
source: "external",body: null,status: sent - Publishes
EVT_MESSAGE_NEW— message appears in the feed (no body yet) - Background task fetches body from Twilio API, updates the record
- Publishes
EVT_MESSAGE_STATUS— body text appears in the UI - Contact list re-fetches, contact moves to the top
- Later
message.deliveredevent updates status via the normal path
Key Files
Section titled “Key Files”| File | Purpose |
|---|---|
src/mods/twilio/api/twilio_events_api.rs | Status event handling with create-on-miss logic |
src/mods/twilio/utils/fetch_twilio_message_util.rs | Twilio Messages API call for body enrichment |
src/mods/messaging/services/update_message_status_service.rs | Returns Option<Message> (None = not found) |
src/mods/messaging/components/messaging_contact_list_component.rs | SSE-driven contact list refresh |
migration/src/m20260310_000000_message_add_source.rs | Adds source column to message table |