Skip to Content

Salesforce Integration

The shared server-side plumbing behind Contact, Subscribe, and Unsubscribe: one integration, three forms.

Approach

Published as @brown-forman/p1-salesforce-forms: the Salesforce client, CSRF handling, and Turnstile verification live in the package; brand sites supply credentials and drop in the Puck components.

Server side: one Next.js API route per form type:

app/api/csrf-token/route.ts : issues a CSRF token for the session app/api/submit-contact-us/route.ts : validates + proxies to Salesforce app/api/subscribe/route.ts : validates + proxies to Salesforce app/api/unsubscribe/route.ts : validates + proxies to Salesforce

Each route: verifies the CSRF token, verifies the Turnstile response server-side (never trust a client-reported pass/fail), validates the payload, then calls Salesforce with org credentials that only ever live in server env vars.

A Salesforce Web-to-Lead POST is fire-and-forget: if it fails, there is no record anywhere that the visitor ever submitted the form. See Form Builder → Submission Storage for the plan to persist every submission before Salesforce (or notification email) is even attempted.

Config shape

interface SalesforceFormsConfig { salesforce: { orgId: string; // brand-specific, confirmed, not a shared constant apiCredentials: { clientId: string; clientSecret: string }; // server-only }; brandId: string; // e.g. "45" for Herradura, carried on every submission segmentId?: string; sourceCode?: string; turnstileSiteKey: string; // public, client-side turnstileSecretKey: string; // server-only contactCategories?: string[]; // powers the Contact form's Category picklist }

Resolved

  • Confirm the DOB/country-on-every-form requirement: Confirmed real, but Herradura’s own implementation satisfies it via the site-wide gate, not per-form fields. Our build follows that pattern.
  • Is there one shared Salesforce org across all Brown-Forman brands, or one per brand?: Confirmed: segmented per brand. orgId is brand-specific config; see the brand_id/segment_id evidence on Subscribe.
Last updated on