Bojan Josifoski < founder />

How I Built a B2B SaaS on WordPress (Architecture, Stack, and Lessons)

February 21, 2026 • Bojan

I Built a Full B2B SaaS on WordPress. Here’s the Entire Stack.

WordPress powers 43% of the web. Most people think that means blogs and marketing sites. It doesn’t have to.

I built SampleHQ on WordPress because after 16+ years of working with it, I know exactly what it can do when you push it. It’s a B2B SaaS platform for manufacturers who ship product samples. Packaging companies, label printers, flooring distributors. It handles sample catalogs, order management, fulfillment tracking, CRM integrations with HubSpot and Salesforce, revenue attribution, billing with Paddle, and per-tenant plan enforcement. Each customer gets their own workspace with custom branding, custom roles, and data isolation.

It runs on WordPress Multisite. Nobody who uses it knows that. Not because we’re hiding anything, but because the experience is that seamless.

I wrote about the general idea back in October 2025, when the platform was still early. This is the updated version. The one with real architecture, real tradeoffs, and real numbers from a system that’s actually running in production.

Why WordPress

Because it’s the most capable application framework nobody gives credit to.

WordPress already solves the hardest problems in building a SaaS. When you know the platform deeply, you don’t fight it. You leverage it. Here’s what it gave us out of the box that would take months to build from scratch.

Authentication and user management. WordPress handles password hashing, session management, cookie scoping, role assignment, and capability checks. I didn’t write any of that. I wrote four custom business roles on top of it and moved on.

Database abstraction. $wpdb isn’t glamorous, but it works. Prepared statements, table prefixing for multisite isolation, and it’s already connected. I added 8 custom tables for CRM data, attribution snapshots, and audit logs. The rest (users, posts that became “samples”, options, transients) WordPress handles.

Multisite as multi-tenancy. This is the big one. WordPress Multisite gives every customer a completely isolated subsite with its own database tables, its own uploads directory, its own options, its own users. When I call switch_to_blog($site_id), the entire context shifts. One codebase, hundreds of isolated tenants. That’s infrastructure battle-tested across millions of sites for 20 years.

Cron, transients, options API, REST API, hooks system. All the boring plumbing that every SaaS needs. WordPress ships with all of it. Imperfect, sometimes frustrating, but functional from day one.

Building equivalent infrastructure from scratch (auth, multisite isolation, session management, a hook/event system, database migrations, cron scheduling, a REST framework) would take 4-6 months before writing a single line of actual product code. WordPress ships with all of it. That’s not a shortcut. That’s a 20-year head start.

The Architecture Nobody Expects

Here’s what the stack actually looks like.

Two custom plugins running as network-active (loaded on every subsite):

samplehq-platform handles everything tenant-level. OAuth login (Google, Salesforce, HubSpot), workspace provisioning, Paddle billing, subscription management, plan enforcement, cross-domain SSO, email verification, and downgrade grace periods. 42 PSR-4 classes with declare(strict_types=1) and constructor injection. No procedural WordPress spaghetti.

samplehq-core handles everything product-level. CRM clients for HubSpot and Salesforce, webhook ingestion, the attribution computation engine with 5 different rules, deal linking, network admin dashboards, REST API endpoints, and audit logging.

A custom theme that is the entire frontend. 31 virtual pages (dashboards, order forms, sample editors, reports, settings) and none of them exist in the database. A VirtualPagesLoader class scans a directory, registers each PHP file as a URL route, and intercepts requests through WordPress’s template_include filter. I wrote about this pattern in detail here. Drop a file in the folder, it becomes a page. No database row, no revision history, no post ID.

5 mu-plugins for things that must always be active regardless of plugin state: SSO domain fixes, session security, plan switching, default theme enforcement, and SMTP configuration in dev. I covered why mu-plugins are essential for this kind of lockdown in The Theme & Plugin Seatbelt.

Multi-Tenancy Without a Single Framework

In a typical SaaS, you’d use something like Apartment for Rails or a tenant_id column in every database table. WordPress Multisite does something more radical. It creates entirely separate table sets per tenant. Site 1 gets wp_1_posts, wp_1_options, wp_1_users. Site 47 gets wp_47_posts, wp_47_options, wp_47_users.

Data isolation isn’t something I enforce. It’s structural. There’s no WHERE tenant_id = ? to forget. A query on one subsite physically cannot see another subsite’s data. That’s not a feature I built. That’s WordPress core.

When a new customer signs up, the provisioning flow looks like this:

  1. OAuth callback validates the identity (Google, HubSpot, or Salesforce)
  2. Email verification sends a one-time token (SHA-256 hashed, 1-hour TTL, rate-limited to 3 per hour)
  3. Cloudflare Turnstile blocks bots
  4. The Provisioner class calls wpmu_create_blog() and WordPress creates all the subsite tables
  5. Schema migrations run on the new subsite (custom tables for CRM data)
  6. Default settings, roles, and capabilities are configured
  7. Trial subscription is created via Paddle
  8. Cross-domain SSO token is issued so the user lands on their new workspace already logged in

That whole flow, from “Sign up with Google” to “Welcome to your workspace”, takes about 2 seconds. On WordPress. I went deep on the auth architecture in this article about passwordless and SSO.

Billing That Actually Works

Paddle handles payment processing, tax compliance, and subscription management. I don’t touch credit cards. What I do handle is the business logic around it.

When a subscription changes, Paddle sends a webhook. The webhook hits a custom endpoint, gets signature-verified, queued to the database, and processed asynchronously. The processor updates the site’s plan option, adjusts feature flags, and if it’s a downgrade, triggers the grace period system. I covered the full payment-to-site-creation pipeline in this deep dive.

The grace period system is one of the more nuanced pieces. When someone downgrades from Pro (10 users, 250 samples) to Starter (2 users, 50 samples), you can’t just lock them out instantly. They get 14 days. During that window, everything stays editable. After day 14, enforcement kicks in. The system sorts their samples by last modified date and their users by last login date, keeps the most recent ones editable up to the new plan limit, and makes the rest read-only. Owners are always exempt.

Three emails go out automatically. One on downgrade day, one at 3 days remaining, one when enforcement activates. Each email is role-aware. The owner sees “Upgrade your plan.” The manager sees “Contact your account owner.”

All of this runs on WordPress cron with a daily batch processor. No external queue, no Redis, no Lambda functions. Just wp_schedule_event() and a well-structured PHP class.

CRM Integration at the Infrastructure Level

SampleHQ connects to HubSpot and Salesforce. Not as a superficial data sync, but as a deep bidirectional integration. Deals flow in via webhooks and nightly API backfills. Sample orders get linked to deals through a manual interface or auto-suggestion engine. And then the attribution system computes how much revenue each sample influenced.

Five attribution rules: equal share, recency-weighted, first touch, last touch, and per-SKU weighting. Each is a class implementing a shared interface. Each produces immutable attribution snapshots stored in denormalized tables indexed for instant aggregation. I wrote the full technical breakdown of the attribution engine here.

The result is 7 report pages that show exactly which products, which reps, and which customers are generating revenue through samples. “Our walnut flooring samples convert at 14:1 ROI” is the kind of sentence this system makes possible. No spreadsheet, no guessing.

The CRM clients use wp_remote_post() for API calls and WordPress transients for token caching. For the Salesforce integration specifically, I had to build programmatic metadata creation using the Metadata API, which I covered in this technical deep dive. Not elegant by Silicon Valley standards. Very functional by shipping-product standards.

Plan Enforcement Without the User Noticing

Three plan tiers. Starter gets 2 users, 50 samples, 500 MB storage, read-only CRM. Pro gets 10 users, 250 samples, 5 GB storage, one full CRM connection. Enterprise gets unlimited everything plus custom branding and API access.

Every limit is enforced in real time. Try to create a 3rd user on Starter and the form tells you why you can’t. Try to import 300 samples on Pro and the import caps at 250, telling you how many were skipped. Try to upload a file that would push you past your storage limit and the upload fails with a clear message.

The enforcement isn’t scattered across random files. A LimitChecker class handles users, samples, and storage. A FeatureGate class handles feature flags. Both read from a central PlanConfig that defines every limit and every feature for every tier. Change a number in one place, it changes everywhere. I broke down the full enforcement architecture in this article.

Storage is particularly interesting. Instead of maintaining a database counter (which drifts), the system scans the tenant’s upload directory in real time using PHP’s RecursiveIteratorIterator. If a user deletes a file, the quota reflects it immediately. Every uploaded image gets auto-optimized, resized and compressed, typically reducing a 5 MB photo to 150 KB. Users never notice. Their storage just lasts longer.

The Part Where It Doesn’t Look Like WordPress

When a customer logs in, they see a custom login page with OAuth buttons and magic link login. They land on a Tailwind-styled dashboard specific to their role. They navigate 31 pages without ever touching wp-admin. They manage users, create orders, configure CRM connections, generate PDFs with QR codes, toggle dark mode, and export reports.

Every WordPress fingerprint has been removed. No toolbar. No admin panel access (non-admins get redirected). No wp-login.php (it returns a 403). No RSS feeds. No XML-RPC. No REST API discovery links. No generator meta tag. No block editor styles. A file called remove-wp.php strips all of it. I wrote about the full approach including custom error pages here.

View the page source and there’s nothing that says WordPress. Check the response headers, nothing. Run a CMS detector and most of them guess wrong.

The four custom roles (Owner, Manager, Sales, Fulfillment) each see a different dashboard, different navigation, different data scope. A sales rep sees only their own orders and their own attributed revenue. A fulfillment user sees only orders ready to ship. The API enforces this at the query level, not the template level. The wrong data never leaves the server.

Custom error pages, custom password reset flow, custom email templates with branded headers and badge labels, custom PDF generation with TCPDF. Everything the user touches has been rebuilt.

What I Actually Ship On

The production stack:

No Docker. No Kubernetes. No microservices. No message queues. No separate frontend deployment. One codebase, one server, one database.

The Numbers

Since people always ask:

What You Need to Know If You Go This Route

This approach works, but it rewards depth. You need to actually know WordPress, not just use it.

Multisite requires expertise. Cookie scoping across subdomains, switch_to_blog() state management, network-active plugin behavior. These are solvable problems, but they’re not documented well. You have to know where to look. We’ve built solutions for all of them, and they’ve been solid in production.

Security headers need attention. Getting CSP, HSTS, and proper CORS working across a multisite with cross-domain SSO takes careful mu-plugin configuration. WordPress gives you the hooks. You just need to use them deliberately.

Version discipline matters. We pin plugin versions, test before every core update, and keep third-party dependencies minimal. WordPress’s ecosystem is massive, which is both its strength and something you manage actively.

Scaling is a known path. WordPress Multisite creates separate database tables per tenant. At large scale, that means database infrastructure needs to grow accordingly. Managed hosting providers like Kinsta handle this well, and there are established patterns for horizontal scaling when the time comes.

The team needs WordPress depth. This kind of architecture requires developers who understand WordPress internals, modern PHP patterns, and API integrations. That’s a specialized skill set, but it’s also a competitive advantage. The people who can do this build things others think aren’t possible.

Why This Works

Most SaaS stacks optimize for developer experience at scale. Dozens of engineers, CI/CD pipelines, microservice boundaries, deployment orchestration. That makes sense for large teams.

WordPress optimizes for something different: speed to value. How fast can we go from “customers need this” to “it’s in production”?

We don’t debug authentication libraries. We don’t configure database connection pools. We don’t write migration frameworks. We write the product (the order form, the attribution engine, the CRM sync, the billing logic) and WordPress handles the infrastructure. Every month we spend building features instead of plumbing is a month our customers benefit from.

WordPress has been doing this for 20 years. The authentication works. The database abstraction works. The multisite isolation works. The REST API works. The hooks system works. It’s not trendy, but it’s proven. And proven is what customers care about.

WordPress Can Do More Than You Think

SampleHQ has, real CRM integrations, real attribution data, and real plan enforcement. Custom roles, custom dashboards, custom billing logic, custom everything.

It’s built entirely on WordPress. And it works beautifully.

If you’re building a B2B product and you know WordPress deeply, don’t let anyone tell you to pick a different stack. The platform is more capable than most developers realize. The question isn’t whether WordPress can handle it. The question is whether you know how to use it.

About the Author

About the Author

I’m Bojan Josifoski - Co-Founder and the creator of SampleHQ, a multi-tenant SaaS platform for packaging and label manufacturers.

← Back to Blog