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 nocustomer_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:00Zon first sync). SOQL pagination followsnextRecordsUrluntildone. - One
CaseHistoryquery is issued per modified case. - Watermark = maximum Case
LastModifiedDateseen (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,escalationorcsat_responsefacts. Case comments, email messages and owner changes are not read. Thesalesforce_agentforce_conversationvendor 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
Originis used as the channel verbatim (e.g.Web,Phone,Email); contract exclusions match case-insensitively. - Reopen detection only considers the literal
closedstatus; 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
reauthis reported, or use a long-lived integration user token.