Connecting Salesforce — read-only mirror into Turtini CRM

Turtini's Salesforce connector mirrors Account, Contact, and Opportunity records from your Salesforce org into Turtini's CRM, read-only in Phase 1. Mirrored records carry a `salesforceId` so you can always trace them back, and a `salesforceSyncedAt` timestamp so you know when the last pull happened. The point is to bring Salesforce data into the same surface as the rest of your operations — accounting, contracts, planning — without making your team double-enter anything.

Each org provides its OWN Salesforce Connected App. Turtini does not ship a shared Connected App — the credentials, scopes, branding, callback URL, and revoke controls all live in your own Salesforce instance, where they belong.

Step 1 — Create the Connected App in your Salesforce org:
1. In Salesforce: Setup → App Manager → New Connected App
2. Name it (e.g. "Turtini Sync") and fill in basic contact info
3. Enable OAuth Settings; set Callback URL to https://turtini.com/oauth-callback.html (or your custom-domain equivalent)
4. Selected OAuth Scopes: `api` and `refresh_token, offline_access`
5. (Recommended) Enable "Require Secret for Web Server Flow" and "Require Secret for Refresh Token Flow"
6. Save. Wait a couple of minutes for Salesforce to provision it.
7. Open the Connected App → Manage → Edit Policies → Permitted Users = "Admin approved users are pre-authorized" (or "All users may self-authorize" if you'd rather skip the profile step). Optionally restrict by profile.
8. From the Connected App's "View" page, copy the Consumer Key and Consumer Secret.

Step 2 — Paste those credentials into Turtini:
1. Settings → Integrations → Salesforce
2. Under "Your Salesforce Connected App," paste the Consumer Key + Consumer Secret
3. Save credentials. Turtini stores the Consumer Key on your org's integration metadata and the Consumer Secret in a per-org entry in Google Cloud Secret Manager (the same pattern used for JWT private keys).

Step 3 — Connect:
1. Click "Connect Salesforce"
2. A Salesforce login popup appears — sign in with the user account whose access the connector should use
3. Approve the requested permissions on the Salesforce consent screen
4. The popup closes and the connection activates. You'll see your Salesforce instance URL and the connected username.

JWT bearer (for legacy or headless instances):
Some Salesforce orgs — Government Cloud, sandboxes used by an integration service account, instances where the IT team prefers a non-interactive flow — can't run OAuth. For those:
1. In Salesforce, create a Connected App with "Use digital signatures" enabled and upload Turtini's public certificate (provided in Settings → Integrations → Salesforce → "Use JWT instead")
2. Pre-authorize the integration user against the Connected App
3. Paste the Consumer Key, the integration username, and your instance URL into Turtini
4. Click "Connect via JWT" — Turtini signs an assertion and exchanges it for an access token, no interactive sign-in required

What mirrors over:
• Accounts → `accounts` (matched and stamped with `salesforceId`)
• Contacts → `contacts` (linked to the parent account via `accountId`)
• Opportunities → `opportunities` (with stage and amount); `dealRegistrationSfid` also carries the Salesforce id for existing CRM tooling
• Standard fields map to Turtini's canonical fields; custom fields are not synced in Phase 1

Sync cadence:
• A daily scheduled sync (`syncSalesforceConnections`) pulls everything new or changed in the past 24 hours
• A "Sync now" button on the connection page runs the same sync on demand for the connected user
• Sync runs are idempotent — re-running won't create duplicates; existing records get updated in place by `salesforceId`

What this is NOT (Phase 1):
• Two-way sync. Turtini does not write back to Salesforce. Changes you make in Turtini stay in Turtini; Salesforce remains the source of truth for synced records.
• Custom-object sync. Only the three standard objects above.
• Real-time push. Updates land on the next scheduled sync or when you click "Sync now" — typically within a few minutes if you trigger it manually.

No platform-level Salesforce secrets:
There is no Turtini-wide Salesforce app. Every credential — Consumer Key, Consumer Secret, and (for JWT) the private signing key — belongs to your org and lives under your org's name in Google Cloud Secret Manager. Disconnecting disables the secret version (keeping the audit trail) so it can never be used again.

Status, troubleshooting, and disconnect: the same integrations page shows the connection's last sync time, last error (if any), and a "Disconnect" button. Disconnecting clears the stored token; mirrored records remain in Turtini with their `salesforceId` intact, so you can reconnect later without losing the link.