Integrations (Beta)
Connect third-party newsletter and marketing tools so leads captured on your site sync automatically. Mailchimp available now; Constant Contact, Brevo, and Kit coming soon.
Integrations (Beta)
Connect the tools you already use for newsletters and marketing. When a visitor fills out a contact form on your site (or signs up via the footer), their email flows directly into your audience — tagged, attributed, and ready for your next campaign.
Beta: Integrations are in early access while we validate the experience with our first agents. Available on Professional and Prestige plans.
What's available
| Service | Status | Auth |
|---|---|---|
| Mailchimp | Available now | OAuth2 |
| Constant Contact | Coming soon | OAuth2 |
| Brevo (Sendinblue) | Coming soon | API key |
| Kit (ConvertKit) | Coming soon | OAuth2 |
Mailchimp
Connect your account
- From the dashboard, go to Integrations in the Settings group
- Click Connect on the Mailchimp card
- Mailchimp will ask you to log in and authorize "1 OAK MLS"
- After clicking Allow, you're redirected back to the configuration page
That's the entire OAuth half. We never see or store your Mailchimp password.
Pick an audience
After connecting, choose which Mailchimp audience your synced leads should land in:
- The Audience dropdown shows every list your Mailchimp account can write to
- Select one (e.g. "Newsletter subscribers" or "All Contacts")
- Click Save changes
You can change this later — past sync history isn't affected, but new leads will go to the newly-selected audience.
Default tags
Comma-separated tags applied to every contact synced from your site. Use these to segment by source in Mailchimp:
website-lead, miami, 2026Common patterns:
website-lead— distinguish synced leads from manual imports- City names — segment by service area
- Year tags — easy yearly campaign filtering
Sync settings
Two toggles control behavior:
- Double opt-in (default: on) — Mailchimp sends a confirmation email; only confirmed addresses become subscribers. Recommended for GDPR safety. Turn off only if you have explicit consent flows elsewhere.
- Sync new leads automatically (default: off until you're ready) — When on, leads with marketing consent flow to Mailchimp immediately. When off, leads are still saved to your contacts, just not pushed.
Testing the connection
The Send test contact button pushes a synthetic test address to your selected audience:
test+<timestamp>@1oak.testVerify it appears in your Mailchimp Audience → All contacts view. Status will be pending if double opt-in is enabled (the confirmation email bounces because 1oak.test isn't a real domain — Mailchimp will mark it cleaned in ~30 days, harmless).
For a more thorough test, send to a real email you control with a +test alias.
How it works
Consent gating
Every form on your site that captures email shows a marketing consent checkbox (default unchecked). Only contacts who explicitly opt in are synced to Mailchimp. Contacts without consent stay in your Clients list but are never pushed.
This is GDPR-safe and CAN-SPAM compliant by design:
- Explicit consent required (unchecked by default)
- Audit trail —
marketing_consent_sourceandmarketing_consent_atare recorded per contact - Bidirectional unsubscribe — when someone unsubscribes in Mailchimp, the unsubscribe flows back to your contacts; they will never be re-added even if they fill out another form
Sync sources
Four lead-capture surfaces auto-sync when consent is given:
- Contact form submissions — explicit consent checkbox on the form
- Landing page leads — explicit consent checkbox on the form
- Footer newsletter signup — implicit consent (the form's purpose IS the consent)
- Coming Soon waitlist — implicit consent (signup IS the consent)
Recent sync activity
The bottom of the Mailchimp page shows your last 15 sync events:
- outbound · upsert · sent — successful sync to Mailchimp
- outbound · upsert · skipped · no_consent — contact didn't tick the consent box
- outbound · upsert · skipped · unsubscribed — previously unsubscribed; we're respecting that
- inbound · unsubscribe — Mailchimp told us someone unsubscribed
- outbound · upsert · failed — Mailchimp API rejected the request (rare; details in the row)
Disconnecting
Click Disconnect on the connection card to:
- Clear the encrypted tokens from our database
- Stop pushing future leads to Mailchimp
- Keep your existing Mailchimp contacts untouched (we don't delete them)
- Preserve your sync log for audit purposes
You can reconnect at any time and pick a new audience.
Compliance notes
We take consent seriously because email regulations do.
- GDPR: We capture and store explicit consent before any sync. Audit trail is per-contact.
- CAN-SPAM: Mailchimp handles the unsubscribe link; we honor inbound unsubscribes within minutes.
- Double opt-in: Recommended (default on) — adds a confirmation step that proves intent.
- Privacy policy: Inform visitors that contacts may be synced to Mailchimp as a sub-processor. We provide standard language for this.
Troubleshooting
"OAuth state verification failed"
The OAuth handshake takes too long (>15 minutes between clicking Connect and clicking Allow), or your dev server restarted mid-flow. Just retry — start fresh from the Connect button.
Audience dropdown is empty
Either your Mailchimp account has no audiences (create one in Mailchimp first), or the access token can't list them. Click Disconnect and reconnect to refresh the token.
Test contact sent successfully but doesn't appear in Mailchimp
Check that you're viewing the right audience in Mailchimp's UI. The audience dropdown in the top of Mailchimp's "All contacts" page must match the one you selected here.
Real leads not syncing
Check the Recent sync activity section:
skipped · no_consent— the contact didn't tick the consent boxskipped · sync_disabled— the "Sync new leads automatically" toggle is offskipped · unsubscribed— the contact previously unsubscribed (we never re-add them)failed— Mailchimp API error; check the error column
For platform admins
Environment setup
Each environment (dev / prod) needs its own registered Mailchimp OAuth app at mailchimp.com/developer/marketing/guides/access-user-data-oauth-2/:
- Redirect URI must match exactly what's set in
MAILCHIMP_OAUTH_REDIRECT_URI. Mailchimp rejectslocalhost— use127.0.0.1for dev. - Production redirect URI must be
https://.
Required env vars:
MAILCHIMP_CLIENT_ID= # from your registered app
MAILCHIMP_CLIENT_SECRET= # from your registered app
MAILCHIMP_OAUTH_REDIRECT_URI= # exact match with the registered URI
MAILCHIMP_WEBHOOK_SECRET= # generate fresh: openssl rand -hex 32Webhook auto-registration
When an agent saves their audience selection, the platform automatically registers an inbound webhook on that Mailchimp audience using the URL pattern:
https://<your-domain>/api/webhooks/newsletter/mailchimp?secret=<MAILCHIMP_WEBHOOK_SECRET>The webhook subscribes to subscribe, unsubscribe, cleaned, profile, and upemail events from all sources (user, admin, API). The registration is idempotent — re-saving the same audience adopts the existing webhook rather than creating a duplicate.
When an agent disconnects, the webhook is automatically removed from their Mailchimp account.
If registration fails (e.g. token revoked, Mailchimp rate-limited), the agent sees a non-blocking warning with a retry button. The save itself succeeds either way — leads still sync outbound, only the inbound unsubscribe path is degraded.
Tier flag sync
Newsletter integrations are gated by features.newsletterIntegrations in workspace settings, synced from the subscription plan via the Stripe webhook and admin billing override. To backfill an existing workspace (e.g. a charter override granted before the flag existed):
pnpm exec tsx scripts/sync-workspace-features.ts <slug>
pnpm exec tsx scripts/sync-workspace-features.ts --all # all active workspaces
pnpm exec tsx scripts/sync-workspace-features.ts --all --prod # against productionArchitecture
- New package:
@1oak/integrationswithNewsletterProviderinterface so additional providers (Constant Contact, Brevo, Kit) drop in without changing shared code - Tokens: AES-256-GCM encrypted via
@1oak/syncwithTOKEN_ENCRYPTION_KEY - Tables:
newsletter_connections(per-workspace),newsletter_sync_log(audit + idempotency); both RLS service-role-only - Sync orchestrator:
syncContactToNewsletter()is fail-soft — never blocks lead capture on a sync error