Skip to content

Architecture

The agent has two halves that talk over an authenticated webhook:

  • Salesforce — the Lightning app, custom objects, Apex services and the LWC UI. It decides when to enrich and owns the write-back rules.
  • n8n — the workflow that does the external research and writes results back into the enrichment objects.
flowchart TB
  subgraph SF[Salesforce]
    APP[Data Enrichment app\nDashboard · Config · Help]
    REC[Lead / Contact / Account]
    ENR[LeadEnrichment__c\nContactEnrichment__c\nAccountEnrichment__c]
    RUN[EnrichmentRun__c\nrun log]
    APEX[Apex services\ncallout + write-back]
  end

  subgraph N8N[n8n workflow]
    WH[Webhook\n/enrichment-agent]
    RES[Research nodes\nApify · SerpAPI · Gemini]
    NORM[Normalize + map]
  end

  REC -->|start run| APEX
  APP --> APEX
  APEX -->|POST data.entity/id\nX-Enrichment-Token| WH
  WH --> RES --> NORM
  NORM -->|write-back| ENR
  ENR -->|accepted values| REC
  APEX --> RUN

The request/response contract

Salesforce sends a deliberately small payload — just the entity type and record Id. n8n loads the current record itself before enriching, so the request never carries stale field data.

{
  "data": {
    "entity": "lead",
    "id": "00Q000000000000AAA"
  }
}

n8n responds fast (HTTP 202/200) and enriches asynchronously, then writes the result back. The full contract, including recommended source fields per object, is in Payload contract.

Write-back objects

Source object Enrichment object
Lead LeadEnrichment__c
Contact ContactEnrichment__c
Account AccountEnrichment__c

The enrichment object holds everything the agent found. Salesforce trigger logic then syncs accepted values to the source record and updates the related EnrichmentRun__c. See the write-back model.

Security

  • The webhook is protected with a shared-secret header (X-Enrichment-Token) so only Salesforce can trigger runs. An open webhook would let anyone burn Apify/SerpAPI/Gemini credits. See Webhook authentication.
  • Access is gated by three permission sets.
  • Runtime configuration lives in OrgSettings__c, so it survives package upgrades and never ships secrets.

Configuration storage

Layer Stored in Editable by
Deploy-time defaults Custom metadata Developers (deployment)
Runtime behaviour (pause, branding, webhook token) OrgSettings__c custom setting Admins (no deployment)
Value translations DataEnrichmentValueMapping__c records Admins (list view)