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

Lightning Web Components

Custom Objects and Metadata

Platform Wiring

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

The chertConversation component shows a Find phone number action when the record has no number on file.

The Chert console enrichment dashboard with a list of contacts ready for bulk phone-number enrichment

The Chert console's Enrichment dashboard. Operators select rows and run enrichment in bulk; results write back to Salesforce.

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

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

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

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

See Also