Switchyard CRM connects your WordPress site to your CRM. When someone registers, buys something or fills in a form, their record lands in the CRM with the fields you mapped. When you tag them in the CRM, that tag decides what they can see on your site. It works with ten CRMs, and tag-based access control runs the same on all of them. I built it for site owners who want one plugin between WordPress and their CRM instead of a different integration for each tool.
Download Switchyard CRM free on WordPress.org or get Switchyard CRM Pro free on GitHub.
Supported CRMs
The plugin connects to one CRM at a time. Pick it under Switchyard CRM > Connection:
- ActiveCampaign
- HubSpot
- Mailchimp
- Klaviyo
- Kit (formerly ConvertKit)
- Brevo
- FluentCRM, which runs inside your own WordPress site, so nothing leaves it
- MailerLite
- Zoho CRM
- Salesforce
CRMs that have tags use tags. Klaviyo, Brevo and MailerLite organize people into lists or groups instead, so Switchyard uses those and the settings screens use the CRM’s own words. Salesforce has no tags, so Switchyard stores them in a multi-select picklist. Tag-based access control works the same way on all ten.
What the Free Plugin Does
- Sends new registrations and profile changes to the CRM with the fields you map
- Syncs WooCommerce orders, including guest checkout, and Easy Digital Downloads purchases and refunds, with tags per product and per order status
- Syncs submissions from Gravity Forms, WPForms, Fluent Forms and Contact Form 7, with per-form field mapping
- Restricts any post, page or custom post type by CRM tag, and keeps restricted content out of archives, search, feeds and the REST API
- Adds shortcodes that show or hide content inside a post by tag, login state or user field
- Takes tag changes back from ActiveCampaign and Kit through a webhook, and pulls tags and fields from any CRM on demand or on a schedule
- Retries failed requests with backoff and pauses calls to a CRM that stops responding, so a CRM outage doesn’t break checkout or registration
- Logs every sync with credentials redacted
- Includes a staging mode that blocks all writes on a cloned site, WP-CLI commands and a developer API with filters on every payload
What Pro Adds
Switchyard CRM Pro is for sites that sell courses, memberships or products and want the CRM to react to what each customer does. It adds:
- Integrations for LearnDash, LifterLMS, MemberPress and Paid Memberships Pro that tag people on enrollment, completion and membership changes
- Abandoned cart capture, taken when the email address is typed at WooCommerce or Easy Digital Downloads checkout
- Event tracking onto the CRM’s activity timeline for orders, form submissions and course completions, on ActiveCampaign and Klaviyo
- Video engagement tags that apply at 25, 50, 75 and 100 percent watched
- Logins: tag accounts that haven’t logged in for a set number of days, and redirect people after login by tag
- One CRM contact per person across a WordPress multisite network
Get Switchyard CRM Pro from GitHub. It’s free while it’s in early access. It needs the free plugin active, and updates show up on the WordPress Updates screen.
Installation
Install the free plugin
- In WordPress, go to Plugins > Add New Plugin and search for Switchyard CRM Sync and Access. You can also download it from WordPress.org.
- Install and activate it.
- Go to Switchyard CRM > Connection and pick your CRM.
It needs WordPress 6.8 or later and PHP 7.4 or later.
Install Switchyard CRM Pro
- Download
switchyard-crm-pro.zipfrom the latest release. - Go to Plugins > Add New Plugin > Upload Plugin and upload the zip.
- Activate it. Keep the free plugin active.
Pro needs PHP 8.1 or later.
Documentation
Connect a CRM
Switchyard CRM adds one menu item to the WordPress admin with six tabs: Connection, Sync, Access, Integrations, Logs and Tools.
On Connection, choose your CRM from the CRM dropdown and click Save and connect. The credential fields for that CRM appear after the first save. A green Connected label means the credentials worked, a red Not connected label means they didn’t. Saved API keys are never shown again. Leave a password field blank to keep the stored value.
Each CRM asks for what it needs:
- ActiveCampaign: API URL and API Key. Tracking Account ID and Event Tracking Key are optional.
- HubSpot: Private App Token, with CRM contacts and lists scopes. Portal ID is optional and is used for site tracking.
- Mailchimp: API Key and Audience ID. Require double opt-in is a checkbox.
- Klaviyo: Private API Key with profile and list scopes. Public API Key (Site ID) is optional and is used for site tracking.
- Kit: API Key (v4).
- Brevo: API Key (v3).
- FluentCRM: nothing.
- MailerLite: API Token.
- Zoho CRM: Client ID, Client Secret, Refresh Token and Data Centre.
- Salesforce: Consumer Key, Consumer Secret, Refresh Token and Instance URL. Sandbox org and Tag Field API Name are optional.
For ActiveCampaign and Kit, the two CRMs that can send webhooks, a Webhook box under the form shows your site’s webhook endpoint and its secret. See the webhook section below.
Sync users and map fields
The Sync tab holds the sync switches and the field map.
- Sync on registration: send new users to the CRM when they register.
- Sync on profile update: send changes when a user edits their profile.
- Create users from webhooks (ActiveCampaign and Kit only): create a WordPress user when the CRM reports a contact that has no account.
- Sync in the background: queue the work with Action Scheduler instead of making the visitor wait. Switch it off to sync during the request.
- Enable site tracking: load the CRM’s own tracking script for visitors. It’s off by default and available for ActiveCampaign, HubSpot and Klaviyo.
- Staging mode (block all writes): stops every outbound write and logs it instead. Turn it on after cloning a production site so the copy can’t change real CRM records.
Below the switches, the Field mapping table lists every WordPress field on the left: the user’s email, first and last name, display name, username, website, biography, registered date, role and user ID, plus fields that active integrations add, such as WooCommerce billing and shipping details. For each one, pick the CRM field it should go to and tick Sync. Values are converted on the way out: dates become ISO 8601, checkboxes become true or false, and multi-select values stay as arrays where the CRM supports them.
Restrict a post or page by tag
- Open the post, page or custom post type in the editor. The Switchyard access box sits in the sidebar, below the other post settings.
- Tick Restrict access.
- Select one or more tags from your CRM. Hold Ctrl or Cmd to select several.
- Set Requires to any of these, or all of these.
- Save the post.
If you restrict a post and select no tags, any logged-in user can read it. Anyone who can edit the post can always read it, so a CRM outage never locks an author out of their own content.
Visitors who don’t qualify see a message or get redirected, depending on the Access tab. Restricted posts also disappear from archives, search, feeds and REST API responses, where content and excerpt come back empty.
Set the restricted message and redirect
The Access tab has three settings that apply site-wide:
- Restricted message: the text shown in place of a restricted post’s content. If you leave it empty, visitors see “You do not have access to this content.”
- Redirect to: send visitors who can’t read a post to a page of your choice, such as a login or sales page. The default is Show the message instead.
- Hide restricted content from listings, search and feeds: on by default. Switch it off only if you filter listings yourself.
Show or hide content inside a post
Four shortcodes control parts of a post.
[switchyard_if] shows its content to logged-in users who have a tag. It takes three attributes: tag is one tag or a comma-separated list, match is any (the default) or all, and not set to true flips the test.
[switchyard_if tag="VIP"]Members only.[/switchyard_if]
[switchyard_if tag="Trial" not="true"]Hidden from trial users.[/switchyard_if]
[switchyard_if tag="VIP,Founder" match="all"]Both tags required.[/switchyard_if]
Logged-out visitors never see the content of [switchyard_if], even with not="true". Use the two login shortcodes for them:
[switchyard_logged_in]Welcome back.[/switchyard_logged_in]
[switchyard_logged_out]Log in to read more.[/switchyard_logged_out]
[switchyard_field] prints a field of the current user, with a fallback for logged-out visitors or empty values. key can be user_email, display_name, user_login or any user meta key such as first_name.
Hi [switchyard_field key="first_name" default="there"]
Uploaded files and tag caching
Uploaded files aren’t gated. Restricting a post doesn’t restrict a PDF, video or image attached to it, because the web server serves those straight from disk without asking WordPress. Anyone with the direct URL can download them. If you sell downloads, route them through PHP or store them outside the web root.
Access decisions read a tag set cached on the user, so no page waits on the CRM. The cache refreshes on login, when a webhook arrives, when this site changes a tag and on a schedule. A tag removed directly in the CRM takes effect at the user’s next login unless your CRM sends a webhook.
Rules for categories and menu items
You set access rules for categories, other taxonomy terms and menu items in code.
A tag rule on a category or other term applies to every post in it, unless the post has its own rule. Store the tag IDs from your CRM as term meta:
update_term_meta( $term_id, 'switchyard_tags', array( '12' ) );
update_term_meta( $term_id, 'switchyard_match', 'any' );
A menu item can require tags, or be limited to logged-in or logged-out visitors. A menu item that points to a restricted post is hidden from people who can’t read the post, and a hidden parent takes its submenu with it:
update_post_meta( $menu_item_id, '_switchyard_menu_tags', array( '12' ) );
update_post_meta( $menu_item_id, '_switchyard_menu_match', 'all' );
update_post_meta( $menu_item_id, '_switchyard_menu_state', 'logged_in' );
Send tag changes from the CRM to WordPress
On ActiveCampaign and Kit, a CRM automation can grant or revoke access by calling your site. The Webhook box on the Connection tab shows the endpoint, which has the form https://example.com/wp-json/switchyard/v1/webhook/activecampaign, and the shared secret.
- Create an automation or webhook in your CRM that fires when a tag changes.
- Point it at the endpoint and send the secret in an
X-Switchyard-Secretheader. - If your CRM can’t send a custom header, use the second URL in the box instead. It carries the secret in the query string, which web servers write to their access logs, so use it only when you have to.
Switchyard doesn’t trust the tags in the request. It reads them again from the CRM’s API, so a forged or replayed request can’t grant access. Click Rotate secret if the secret leaks, then update it in your CRM.
The other CRMs get tag changes through the scheduled refresh and the Tools tab.
Connect forms
Form integrations load by themselves when their plugin is active: Gravity Forms, WPForms, Fluent Forms and Contact Form 7. Each submission creates or updates a contact in the CRM, whether or not the person has a WordPress account. Switchyard finds the email address in the submission, using the field you name if there is one and the first email-shaped value otherwise. A submission with no email address is skipped and logged.
Per-form settings live in one option per plugin, named switchyard_integration_ followed by the integration’s slug: gravity-forms, wpforms, fluent-forms or contact-form-7. Each form is keyed by its ID and takes four settings: enabled, map from form field names to CRM fields, tags to apply, and email_field.
update_option( 'switchyard_integration_contact-form-7', array(
'forms' => array(
'123' => array(
'enabled' => true,
'email_field' => 'your-email',
'map' => array( 'your-name' => 'first_name' ),
'tags' => array( 'Newsletter' ),
),
),
) );
Connect WooCommerce and Easy Digital Downloads
Orders sync when someone checks out, including guest checkout, using the billing details on the order. WooCommerce and Easy Digital Downloads add their own fields to the Field mapping table: billing and shipping details, order count, total spent and last order date for WooCommerce, and purchase count, lifetime value and customer address for Easy Digital Downloads. Refunds are synced too.
Tags on purchases come from the Switchyard purchase tags box on each product or download. The tags you pick there are applied to the buyer when they order. They’re separate from the Switchyard access box, which only controls who can see the product page.
Site-wide purchase tags, tags for specific WooCommerce order statuses and tags for refunds are stored in the switchyard_integration_woocommerce and switchyard_integration_edd options:
update_option( 'switchyard_integration_woocommerce', array(
'purchase_tags' => array( 'Customer' ),
'status_tags' => array( 'completed' => array( 'Paid customer' ) ),
'refund_tags' => array( 'Refunded' ),
'remove_tags_on_refund' => true,
) );
The switchyard_order_synced action fires for every synced order with a normalized order object, so you can send orders anywhere else.
Check which integrations are active
The Integrations tab lists every integration and its status: Active, Available, not loaded, or Plugin not active. With Pro installed, the list includes LearnDash, LifterLMS, MemberPress and Paid Memberships Pro.
Read the logs and troubleshoot
The Logs tab shows the last 200 entries with time, level, source and message, and a Context line you can expand. Credentials are redacted before anything is written. Set Log level to Error, Warning, Notice, Info or Debug and click Save. The default is Error, so raise it to Info or Debug while you test and set it back afterward. Entries are deleted after 30 days, and Clear logs empties the table now.
When something isn’t syncing, check these in order:
- The Connection tab shows Connected. If it shows Not connected, re-enter the credentials.
- Staging mode on the Sync tab is off.
- The field you expect is ticked in Field mapping.
- The Logs tab, set to Info, shows an entry for the event. A form submission with no email address, for example, is logged and skipped.
- Sync in the background is on, so the work waits in Action Scheduler. Open Tools > Scheduled Actions to see whether jobs are pending or failed.
If a CRM stops responding, Switchyard retries with backoff and then stops calling it for five minutes instead of slowing every page. Access decisions use the cached tags in the meantime.
Run batch jobs
The Tools tab runs three jobs with the Run button:
- Refresh tags and fields from the CRM: clears the cached tag and field lists.
- Resync all users to the CRM: queues one job per user.
- Pull tags for all users from the CRM: refreshes every user’s cached tags.
The jobs run in the background, so watch the Logs tab for progress. On a large site, use WP-CLI instead.
Use WP-CLI
wp switchyard status
wp switchyard sync 42
wp switchyard sync
wp switchyard tags 42 --refresh
wp switchyard import --tag=12 --role=subscriber
status shows the connection. sync pushes one user, or everyone, to the CRM. tags shows a user’s tags, and --refresh pulls them from the CRM first. import creates WordPress users from CRM contacts, optionally only those with one tag, and sets their role.
Use the developer API
switchyard_apply_tags( array( 'Customer' ) );
switchyard_remove_tags( array( 'Trial' ), $user_id );
if ( switchyard_has_tag( 'Customer' ) ) {
// ...
}
switchyard_sync_user( $user_id );
switchyard_sync_contact( '[email protected]', $fields, array( 'Lead' ) );
switchyard_can_access( $post_id, $user_id );
Tags can be passed as IDs or as labels. Labels are looked up in the CRM and created if they don’t exist. Filters cover the sync payload (switchyard_sync_fields), the access decision (switchyard_can_access), form fields and tags, HTTP arguments and more. The developer guide lists them all, and has instructions for adding a CRM of your own.
Turn Pro modules on and off (Pro)
Every Pro module switches on when you activate the plugin, and each one stays idle until its settings exist. To turn a module off, untick it in the Modules table on the Integrations tab. Integrations, including the LMS and membership ones, have their own Enabled checkbox in the list above it. If a developer has forced one on or off with the switchyard_pro_enable_feature or switchyard_enable_integration filter, its row is locked and says “Set by a filter on this site.”
LearnDash, LifterLMS, MemberPress and Paid Memberships Pro (Pro)
These integrations load when their plugin is active and appear on the Integrations tab. They add mappable fields to the Field mapping table: courses enrolled, courses completed and last course activity for LearnDash, and membership level, status and expiry date for MemberPress. Paid Memberships Pro adds membership level and status.
Tags are stored per course, lesson, quiz or membership product as post meta named _switchyard_tags_ followed by the event:
- LearnDash:
enrolled,completed,passedandfailed. Removing a student from a course removes the enrolled tags. - LifterLMS:
enrolledandcompleted, on courses and lessons. - MemberPress:
activeandlapsed, on the membership product. - Paid Memberships Pro:
activeandcancelled, stored per level in theswitchyard_integration_pmprooption underlevels.
update_post_meta( $course_id, '_switchyard_tags_completed', array( 'Course graduate' ) );
Tag abandoned carts (Pro)
On the WooCommerce or Easy Digital Downloads checkout page, Pro captures the email address as soon as the visitor types it, as long as their cart isn’t empty. It waits 15 minutes. If the order doesn’t complete in that time, the contact gets your abandoned tags. If it completes, the pending capture is cancelled, and logged-in customers get the recovered tags and lose the abandoned tags. Settings live in the switchyard_integration_abandoned_cart option:
update_option( 'switchyard_integration_abandoned_cart', array(
'delay' => 900,
'capture_tags' => array( 'Checkout started' ),
'abandoned_tags' => array( 'Abandoned cart' ),
'recovered_tags' => array( 'Recovered' ),
) );
delay is in seconds. The capture endpoint is rate limited to ten requests an hour per visitor.
Send events to the CRM timeline (Pro)
When the connected CRM has an activity timeline, Pro sends three events to it: Order Placed, Form Submitted and Course Completed. It works with ActiveCampaign and Klaviyo. The course event fires from the LearnDash and LifterLMS integrations.
Tag video viewers (Pro)
Pro can tag logged-in people when they watch part of a video on a post. Add a data-switchyard-media attribute to a video element, then declare the video and its tags in the switchyard_integration_media option. Progress reports at 25, 50, 75 and 100 percent. Only thresholds you’ve configured can apply tags, and a visitor who can’t read the post can’t earn them.
<video src="intro.mp4" data-switchyard-media="intro" controls></video>
update_option( 'switchyard_integration_media', array(
'posts' => array( '123' => array( 'intro' ) ),
'media' => array(
'intro' => array(
array( 'percent' => 50, 'tags' => array( 'Watched half' ) ),
array( 'percent' => 100, 'tags' => array( 'Watched intro' ) ),
),
),
) );
Tag inactive users and redirect after login (Pro)
Pro records each user’s last login. A daily job applies your inactive tags to accounts that haven’t logged in for the number of days you set, 90 by default, and removes those tags the next time the person logs in. It also redirects people after login by tag, using the first rule that matches. Settings live in the switchyard_integration_logins option:
update_option( 'switchyard_integration_logins', array(
'inactive_tags' => array( 'Inactive' ),
'inactive_days' => 90,
'redirects' => array(
array( 'tag' => 'Course Student', 'url' => 'https://example.com/my-courses/' ),
),
) );
Keep one contact across a multisite network (Pro)
On a multisite network, Pro stores a contact’s CRM ID on the user once and reuses it on every site the user belongs to, so one person stays one contact in the CRM. It needs no settings and only runs on multisite.
FAQ
Does it work with a CRM that has no tags?
Yes. Klaviyo, Brevo and MailerLite use lists or groups, and Salesforce uses a multi-select picklist. Switchyard uses whichever the CRM provides, so tag-based access control works on all ten.
What happens if my CRM goes down?
Your site keeps working. Failed calls retry with backoff and then queue for later. After repeated failures, Switchyard stops calling that CRM for five minutes instead of adding a timeout to every page load. Access decisions use the cached tags until the CRM answers again.
Does it work with guest checkout?
Yes. A WooCommerce or Easy Digital Downloads order from someone without an account still creates or updates the contact, using the billing details on the order. Form submissions work the same way.
Can I move to a different CRM later?
Yes. Your field mapping and access rules live on your site. Connect the new CRM, map the fields to its field names, and create the same tags there.
What data leaves my site?
Switchyard only talks to the CRM you connect, and it contacts nothing until you enter the credentials. It sends the email address plus the profile, order and form fields you mapped. Site tracking is a separate switch, off by default, that loads the CRM’s own tracking script in your visitors’ browsers. FluentCRM runs on your own site, so selecting it sends nothing elsewhere.
What does Pro add over the free plugin?
The free plugin syncs users, forms and orders to all ten CRMs and handles access control. Pro is for sites that sell courses, memberships or products. It connects LearnDash, LifterLMS, MemberPress and Paid Memberships Pro, tags abandoned carts so your CRM can follow up, puts orders and course completions on the CRM timeline, tags video viewers and inactive members, and keeps one contact per person across a multisite network. It’s free on GitHub while it’s in early access.
Switchyard CRM is one of the WordPress plugins I build. For the companies and projects behind them, see my projects page.
