Skip to content
Browse the documentation
Connectors

Salesforce

What the Salesforce connector reads, with which permissions, what it never touches, how it syncs, and the facts it produces.

Last changed Markdown versionllms.txt

Source module: app/connectors/salesforce.py. Kind: facts only. Display name: "Salesforce".

Purpose

Reads Case status history and Opportunity stage history through the REST/SOQL API so that resolution-type claims (from Agentforce exports or any vendor) and lead/opportunity outcomes can be checked against the CRM.

Auth and scopes

Item Value
Method Connected app access token, sent as Authorization: Bearer, against the org's instance_url; API version v60.0
Credentials stored (encrypted) instance_url, access_token
Least-privilege scopes OAuth scope api (read). Assign a profile or permission set with read on Case, CaseHistory, Opportunity, OpportunityHistory, OpportunityContactRole, and field-level read on Contact.Email. No create/edit/delete permissions.
Connection test GET /services/data/v60.0/limits (remaining daily API requests recorded in health)
Non-secret config none

Fields pulled and why

SOQL Fields Used for
Case WHERE LastModifiedDate > :since Id, Status, CreatedDate, ClosedDate, Origin, Contact.Email, LastModifiedDate Subject id, channel (Origin), hashed contact email, watermark
CaseHistory WHERE CaseId = :id AND Field = 'Status' OldValue, NewValue, CreatedDate Status-change and reopen facts
Opportunity WHERE LastModifiedDate > :since with subqueries OpportunityHistories and OpportunityContactRoles WHERE IsPrimary = true LIMIT 1 Id, StageName, CloseDate, LastModifiedDate; history StageName, CreatedDate; the primary contact role's Contact.Email Opportunity-stage facts, hashed contact email. Opportunity itself has no Contact lookup; the primary contact role is the customer.

What is hashed / never stored

  • Contact.Email (the Case contact; the Opportunity's primary contact role) → customer_hid (HMAC-SHA256, per-tenant key); the email is discarded. An opportunity with no primary contact role carries no customer_hid.
  • Case subject, description, comments, feed items, contact names/phones, and any other field are neither queried nor stored.
  • The access token is stored only as ciphertext.

Incremental sync and watermark

  • Both queries filter on LastModifiedDate > <watermark> (ISO-8601, 1970-01-01T00:00:00Z on first sync). SOQL pagination follows nextRecordsUrl until done.
  • One CaseHistory query is issued per modified case.
  • Watermark = maximum Case LastModifiedDate seen (opportunity timestamps do not advance it).
  • Upserts are idempotent; re-syncing a record refreshes its facts.

Rate-limit handling

Shared HttpClient (Retry-After, exponential backoff on 429/5xx, 6 attempts, immediate reauth failure on 401/403). Salesforce enforces a daily API request allocation; test_connection reports the remaining count so an admin can size backfills.

Fact types produced

fact_type Subject / kind When Attributes raw_ref
status_change sf_case_{Id} / ticket Each CaseHistory row for Status from, to (lower-cased), actor: "agent", customer_hid, channel: <Origin> salesforce:casehistory:{Id}:{i}
reopen sf_case_{Id} / ticket A status history row moving from closed to any other status customer_hid, channel salesforce:casehistory:{Id}:{i}:reopen
opportunity_stage sf_opp_{Id} / opportunity Each OpportunityHistory row stage: <StageName>, customer_hid salesforce:opphistory:{Id}:{i}

Claims produced

None. Salesforce Agentforce usage/claims are not exported by this connector; supply them via CSV upload mapped to salesforce_agentforce_conversation (or another contract), using subject ids that match the fact subject ids above or that resolve through identity links.

Subject id format

sf_case_{CaseId} and sf_opp_{OpportunityId} (15/18-character Salesforce ids as returned by the API).

Known limitations

  • No customer_message, human_reply, escalation or csat_response facts. Case comments, email messages and owner changes are not read. The salesforce_agentforce_conversation vendor lens (customer_message_within) therefore cannot be satisfied from this connector alone; conversation facts must come from a help-desk connector or a CSV upload linked by hashed email.
  • Case Origin is used as the channel verbatim (e.g. Web, Phone, Email); contract exclusions match case-insensitively.
  • Reopen detection only considers the literal closed status; orgs with custom closed statuses need a CSV mapping or a contract tweak.
  • One history query per case (N+1); large backfills consume API allocation.
  • Access tokens expire; the connector does not refresh them. Re-authenticate with a new token when reauth is reported, or use a long-lived integration user token.