Skip to main content
Component map, call graph, and synchronous-versus-asynchronous behavior.

System Landscape

System landscape A Salesforce Org issues outbound HTTPS to the Chert messaging service through the Chert_Endpoint Named Credential. Chert dispatches sends to its delivery infrastructure and posts replies back into the Org via the ChertInboundRest REST resource. There are no hardcoded endpoints in Apex. Every outbound HTTP call flows through Chert_Endpoint.

Components

Apex

ClassResponsibilitySharing
ChertSendIMessageInvocable action. Signs the request, calls Chert, returns messageId and duplicate to the Flow.with sharing
ChertBridge@AuraEnabled data layer for the Lightning Web Components. Resolves Contact / Lead / Account context, exposes campaign status, drives bulk enrichment.with sharing
ChertEnrichServiceBackground phone-number enrichment. Signs and POSTs to Chert; persists results when the callback returns.with sharing
ChertEnrichSchedulerSchedulable + Batchable. Nightly run that enqueues missing-mobile Contacts for enrichment.with sharing
ChertEnrichmentRunNowRestREST resource for on-demand enrichment triggered from the LWC.with sharing
ChertEnrichResultRestREST resource for asynchronous enrichment callbacks from Chert. Bearer-authenticated.with sharing
ChertInboundRestREST resource for reply ingestion. Bearer-authenticated. Resolves the parent record by (salesforce_record_id, phone) tie-break.with sharing
ChertContactsRestSite-exposed REST resource for the operator dashboard’s live-Contacts feed. Bearer-authenticated. Read-only plus a single mutable field.without sharing
ChertTenantConfigStatic accessor for Chert_Tenant.Default Custom Metadata.with sharing

Lightning Web Components

ComponentSurfacePurpose
chertConversationLead, Contact, Account record pagesThree-tab pane: Conversation (full iMessage thread + inline send box, auto-refreshing), Status (CRM status, phone line in use, next scheduled send, sequence enrollment), Campaign (today’s send count, send window, recent activity). When the record has no phone number, the component shows a Find phone number action instead of the conversation view.
chertSequenceLauncherContact Quick Action and List View actionEnrolls one or more Contacts into a Chert outbound sequence.
chertBulkEnrichContact List View actionBulk-enriches missing mobile numbers. Concurrency-bounded.

Custom Objects and Metadata

ObjectTypePurpose
Chert_Message__cCustom objectAppend-only conversation log. One record per message, in or out.
Chert_Tenant__mdtCustom Metadata TypePer-Org credentials: tenant slug, signing secret, ingest token, base URL override.

Platform Wiring

ArtifactPurpose
Chert_Endpoint Named CredentialSingle outbound endpoint registration.
Chert_Messaging_User Permission SetEnd-user access grants.
Lead_Record_Page_Chert, Contact_Record_Page_Chert, Account_Record_Page_Chert FlexipagesLightning Record Pages with chertConversation placed.
Start_Chert_Sequence, Enrich_Phone_Numbers Quick ActionsLWC entry points on Contact.

Outbound Send Sequence

Send sequence
  1. A Flow, trigger, button, or LWC invokes ChertSendIMessage.
  2. The action loads Chert_Tenant.Default through ChertTenantConfig.
  3. The action serializes the JSON body, computes HMAC-SHA256(<ts>.<body>, secret), and attaches the x-chert-signature header.
  4. The action issues a POST through Chert_Endpoint to /send.
  5. Chert verifies signature and timestamp, resolves or creates the lead, and dispatches the send through its delivery infrastructure.
  6. Chert returns { ok, lead_id, message_id, duplicate }.
  7. The action inserts a Chert_Message__c row with direction = 'outbound' and the returned message_id.
  8. The action returns { messageId, duplicate } to the Flow.
End-to-end latency in the Apex transaction is dominated by the HTTPS round trip; typical observed latency is 400–900 ms.

Inbound Reply Sequence

Reply sequence
  1. A recipient replies. Chert receives the inbound message from its delivery infrastructure.
  2. Chert POSTs to <your-org>.my.salesforce.com/services/apexrest/chert/v1/inbound with the bearer ingest token in the Authorization header.
  3. ChertInboundRest validates the bearer against Chert_Tenant__mdt.Ingest_Token__c.
  4. The resource resolves the parent record by the salesforce_record_id from the original send, falling back to a phone-number match if the original record is unavailable.
  5. The resource inserts a Chert_Message__c with direction = 'inbound' and inserts a standard Task against the same parent record so the reply surfaces on the Salesforce Activity Timeline alongside emails and calls.
  6. Open chertConversation components subscribed to the Contact’s record updates pick up the new row on the next refresh.
Every outbound send writes the same pair: a Chert_Message__c row with direction = 'outbound' and a corresponding Task. Standard Salesforce reports built on Tasks pick up iMessage activity without custom report types.

Phone Enrichment

Enrichment fills Contact.MobilePhone (and the Chert-prefixed metadata fields) for records that arrive without a number. There are three trigger points and one resolver pipeline.

Trigger points

The Chert Messaging widget showing a Find phone number action and the Conversation, Status, and Campaign tabs
TriggerSurfacePath
Single contact, in SalesforceThe chertConversation component’s Find phone number action when no phone is on fileChertBridge.enrichContactChertEnrichService
Bulk, list viewThe Enrich Phone Numbers Quick Action on Contact list views (chertBulkEnrich)ChertBridge.bulkEnrichChertEnrichmentRunNowRest
Bulk, consoleThe Chert console’s enrichment dashboardCalls ChertEnrichmentRunNowRest directly
The Chert console enrichment dashboard with a list of contacts ready for bulk phone-number enrichment

Resolver waterfall

The Chert side runs a three-step waterfall:
1

Cache check

Recent enrichment results for the same identity (LinkedIn URL, email, or (name, company) tuple) are returned from cache without re-querying providers.
2

Multi-source resolution

Multiple phone-data providers are queried in parallel. Results are crosschecked against each other; a number that appears across two or more sources gets a high confidence score, a single-source result gets a lower one.
3

Write back to Salesforce

Chert POSTs the result to ChertEnrichResultRest (bearer-authenticated). The resource updates Contact.MobilePhone and stamps Chert_Phone_Source__c, Chert_Phone_Confidence__c, and Chert_Last_Enriched_At__c. If no provider returns a match, the row is stamped with Chert_Enrichment_Status__c = 'no_match' so the dashboard knows not to retry.
The first trigger (single contact) is synchronous from the operator’s perspective — they click and wait. Bulk paths kick off Queueable jobs on the Chert side and return immediately; the chertBulkEnrich component polls for completion via ChertEnrichResultRest callbacks.

Stamped metadata

FieldTypeMeaning
Chert_Phone_Source__cPicklistWhich provider returned the number, or manual if hand-entered
Chert_Phone_Confidence__cNumber0–100 score, higher when multiple sources agree
Chert_Last_Enriched_At__cDateTimeWhen enrichment last ran for this Contact
Chert_Enrichment_Status__cPicklistpending, enriched, no_match, or failed
The Permission Set grants Read on all four fields and Edit on MobilePhone so the bulk-enrich component can write back through standard DML.

Sequencing

The chertSequenceLauncher Quick Action enrolls Contacts into a Chert sequence. Sequences are configured in the Chert console; the package exposes only the enrollment surface. A sequence contains:
  • An ordered set of steps (initial message, follow-ups)
  • A delay between each step
  • Message copy per step, with template variables for {name}, {company}, {calendar}, and any {custom_field} you bind from a CSV column or Salesforce field
  • A send window and timezone
  • A per-line spacing rule between consecutive sends
  • A phone-line pool that scopes which lines this sequence may use
When a lead replies at any step, the sequence stops automatically — no further automated messages go out for that lead. The reply is logged to Chert_Message__c and the Activity Timeline; from there a rep handles the conversation manually.

Entity Relationship

ERD Chert_Message__c carries a Lookup to Contact, a Lookup to Lead, and a Chert_Message_Id__c external ID for upserts during ingestion. Both Contact and Lead lookups are nullable; exactly one is populated per record. Chert_Tenant__mdt is a single-record metadata type. The Default record is the only supported instance.

Synchronous versus Asynchronous Boundaries

ActionWhere it runs
ChertSendIMessage.sendSynchronous. Returns to the caller within one Apex transaction.
ChertBridge.getContext, getCampaignStatus, getAccountContactsSynchronous read paths invoked from the LWC.
ChertBridge.startSequenceForRecordsSynchronous orchestration; one Chert callout per record. Bounded to 80 records per call by the LWC; larger sets chunk client-side.
ChertBridge.enrichContactSynchronous trigger of an asynchronous Chert-side enrichment.
ChertEnrichScheduler.executeAsynchronous. Scheduled Apex daily; enqueues a batch.
Chert’s enrichment callback to ChertEnrichResultRestAsynchronous. Out-of-band write into the Org.
Chert’s reply callback to ChertInboundRestAsynchronous. Out-of-band write into the Org.

Idempotency and Dedup

Outbound sends carry a deterministic idempotency_key. Within Chert’s 6-hour rolling window, a duplicate key returns { duplicate: true, ok: true } without re-delivering. The Flow should treat duplicate: true as success. Inbound replies are deduped on Chert’s message_id, persisted as the Chert_Message_Id__c external ID on Chert_Message__c. Re-delivery of the same reply upserts in place.

Error Handling

SurfaceOn failure
ChertSendIMessageReturns { ok: false, errorCode, errorMessage } to the Flow. The Flow author chooses the retry strategy.
ChertBridge @AuraEnabled methodsThrow AuraHandledException with a sanitized message. The LWC surfaces the error through lightning/platformShowToastEvent.
ChertInboundRest, ChertEnrichResultRestReturn HTTP 4xx for malformed input or auth failure, 5xx for unexpected server errors. Chert retries 5xx with exponential backoff up to three attempts.

Chert-side API responses

The /api/salesforce/* endpoints on Chert’s side (which the Apex classes above call) keep the { ok: false, ... } shape — the Apex client branches on ok — but every error path now also carries the canonical numeric code, a generic human message, a retryable boolean, and a trace_id (also in the X-Trace-ID header). The numeric code follows the shared Messaging API error scheme; provider names, env var names, and raw database errors are no longer leaked in message — the full detail is logged server-side under trace_id. Auth and signature failures return distinct codes (2012 missing credentials, 2004 invalid signature/token, 2013 timestamp skew) rather than one opaque “unauthorized”. The widget context endpoint (/api/salesforce/v1/widget/context) was made consistent: a record with no lead yet always returns 200 { ok: true, lead: null } — it no longer sometimes returns 404.

Extension Points

HookUse
ChertSendIMessage invocableCompose into any Flow, Process Builder, or trigger.
ChertBridge @AuraEnabled methodsBuild additional LWC surfaces against the same data layer.
Chert_Message__c triggersAdd Org-side automation on inbound reply ingestion (assign owner, create Task, raise Platform Event).

See Also