Skip to main content

I Built an Admin-Only WordPress CRM With Cursor

I needed a lead engine for a chocolate brand’s hotel and corporate gifting outreach — not another SaaS trial and not a spreadsheet that dies after week two. So I shipped an admin-only WordPress CRM plugin with Cursor: import research CSVs, park draft emails, and only send after an administrator types a confirmation. Nothing auto-mails.

🎯 Not sure if this will run on your hardware?Use our free Local LLM Hardware Checker — pick your GPU and RAM, see which models will run with real tokens/sec estimates.
Check my hardware →

This is a build log, not a product pitch. The CRM lives on the brand’s WordPress site. The pattern is what I want on Builders: a real tool, pair-programmed with an AI coding agent, with the sharp edges left in.

What I actually shipped

  • Custom plugin, Administrators only (manage_options) — no public routes, no anonymous REST
  • Pipeline board + list across ten stages (Research → Ready to contact → Draft ready → Sent → … → Won / Lost / Nurture)
  • CSV import for research batches with header aliasing and SHA-256 dedupe on org + property + email/contact
  • Draft compose that parks Wave 1 hotel emails in Draft ready
  • Send path that requires typing SEND, then uses wp_mail / SMTP — import never sends
  • Activity log for draft and sent events

Offline dry-run on the research folder: hundreds of CSV rows collapsed to ~365 unique leads after dedupe. Wave 1 hotel drafts attach to matching leads when those rows exist.

Advertisement

How Cursor helped (and where I stayed in the loop)

Cursor was the pair-programmer for plugin scaffolding, admin UI, importer edge cases, and the “never send on import” guardrails. I kept the product rules:

  1. Admin-only. If you are not a WordPress administrator, you do not see the CRM.
  2. No invented contacts. Only CSV / draft markdown content is stored.
  3. Human confirmation before any outbound. Typing SEND is deliberate friction.
  4. SMTP identity is separate from “looks good in the editor.” From / Reply-To and the visible signature have to match the mailbox the brand actually owns.

That last one bit us. Mail hosting, MX/SPF, and “Send mail as” were not optional polish — without them the CRM is a fancy draft folder. We wired SMTP with an app password and kept testing until a From address the brand wants actually left the server.

What broke

  • Mailbox before CRM: Missing MX/SPF meant brand-domain mail never landed. The plugin was ready; the domain was not.
  • Auth vs visible From: SMTP authenticated as one mailbox while the visible From needed another. Getting that pair right took more time than the pipeline UI.
  • CSV messiness: Headers differed across research batches. The importer needed aliases or every import looked “empty.”
  • Temptation to auto-send: Easy to wire “import → send.” We refused. Wave 1 stays parked until an admin confirms.

What I left out on purpose

  • No public lead form tied into this plugin (the site form can still capture separately)
  • No automatic brochure PDF send
  • No “AI writes the cold email and blasts hundreds of hotels” loop
  • No Laya / local-agent runtime story in this post — this CRM is WordPress + Cursor, not a homelab agent

If you want the Cursor-on-mustafa.net side of how I ship interactive tools, I wrote up the free local LLM hardware checker separately. For local model hardware context, the Local LLMs hub is still the front door.

Ship checklist (if you copy the pattern)

  1. Lock capability checks and nonces on every admin AJAX action.
  2. Import and send are different code paths; import must not call wp_mail.
  3. Prove SMTP with a real From the customer will reply to before you call the CRM “live.”
  4. Keep research CSVs boring and deduped — the CRM is only as honest as the sheet.

FAQ

Is this a public mustafa.net app?

No. It is a WordPress plugin on the brand site. This post is the build story.

Does import send email?

No. Import only creates or updates leads and can park drafts. Send requires an administrator confirmation.

Why Cursor instead of a hosted CRM?

I needed admin-gated stages, research CSV import, and send friction on the same WordPress install the site already uses — without another monthly seat tax for a tiny outbound motion.

Written by MustafaEngineer running a 24/7 homelab since 2022. Every guide here is built and tested on my own hardware. No paid placements.

Keep reading