Salesforce CRM (REST API)#
Purpose: Contacts and Leads lookup; status updates; email/title updates where business rules allow.
Code: src/auto_responder/services/databases/salesforce.py
Configuration#
| Setting | Notes |
|---|---|
SALESFORCE_DOMAIN | Login, test, custom domain, or full instance URL. |
SALESFORCE_ACCESS_TOKEN | Bearer token auth when set. |
SALESFORCE_CLIENT_ID | OAuth client id (simple-salesforce flows). |
SALESFORCE_USERNAME, SALESFORCE_PASSWORD, SALESFORCE_SECURITY_TOKEN | Username/password flow when not using access token. |
See config.py for the full set and validation. Initialization failures fall back to MockSalesforceDatabase in main.py (live mode still attempts the real connector first).
Contact lookup#
| Call | Purpose |
|---|---|
lookup_by_email(email) | Returns list of ContactRecord (Contacts and/or Leads) |
Action engine touches#
Salesforce writes follow the same active-only CUPOLA gate as Hodor. Email / title updates run only when _handle_title_update confirms an active CUPOLA row; otherwise the row routes to Human Review. CUPOLA-side reactivation is disabled; Salesforce Lead / non-Multipub Contact reactivation on ACTIVE still runs when mirroring applies. OUT_OF_OFFICE does not touch Salesforce.
INACTIVE (_handle_inactive)#
For each Salesforce record on the contact:
| Rule | Action |
|---|---|
| Multipub subscriber exists and record is a Contact | Skip status update ("Multipub-related") |
| Otherwise (Lead or non–Multipub-tied Contact) | update_lead_status(id, False) or update_contact_status(id, False) |
Reads:
| Call | Purpose |
|---|---|
check_active_subscription(contact_id) | For Contacts only — subscription warning list |
ACTIVE (_handle_active)#
Same Multipub-related Contact skip rule. For eligible records with is_active=False:
| Call | Purpose |
|---|---|
update_lead_status(id, True) | Reactivate Lead |
update_contact_status(id, True) | Reactivate Contact |
If already active, logged as "already active". Note that this path does not imply a CUPOLA reactivation — CUPOLA stays inactive and a reactivation candidate is routed to IP4 Human Review.
OUT_OF_OFFICE#
Handled by _handle_out_of_office. The entire Salesforce loop is skipped; a Human Review row is emitted (HUMAN_REVIEW_REASON_OUT_OF_OFFICE).
Changed email / updated title#
Runs only when the CUPOLA active-only gate in _handle_title_update passes. After the gate, loops contact.records:
- Skips multipub system rows entirely.
- Skips Salesforce Contact rows when
multipub_recordexists (same "Multipub-related" rule). - For remaining records,
update_contact_email/update_contact_titleon the connector.
If the CUPOLA gate fails (no row, or inactive row), Salesforce is not updated — the row goes to Human Review with HUMAN_REVIEW_REASON_UPDATE_ON_INACTIVE or HUMAN_REVIEW_REASON_ACTIVE_NEW_CONTACT.
Note: Replacement flows add the new person in Cupola; action_engine.py does not call Salesforce add_contact for replacements.
Read-only / dry-run#
- Read-only: Real connector wrapped →
update_contact_status,update_lead_status,update_contact_email,update_contact_title,add_contactare intercepted; SOQL lookups still live (unless Salesforce failed init and mock is used). - Dry-run: Mock — no API calls.
Output artifacts#
Salesforce IDs captured on inactive-person records in OutputDocumentCollector.
Related#
- multipub.html — drives which Salesforce Contacts are skipped
- sfmc.html — separate Marketing Cloud product; not the same API