MSPercury
Founders · · 8 min read

The MSP partner network without a money flow: how MSPercury wires reciprocity into the platform

Every MSP turns down customers they can't serve. We built a partner network that lets them hand those leads to peers who can — with zero money flow between users, double-opt-in consent baked into the workflow, and a karma system that rewards reciprocity. Here's the architecture and why it stays cleanly outside ZAG, MiCA and GwG scope.

LF
Lucas Flores
IT Systeme Flores UG

Every MSP I know turns down customers regularly. Wrong region. No Linux skills in-house. Wants 24×7 but you don’t run a NOC. Wants a project rate, you only do retainers. The lead came in warm, they trust you, and you have to email them back with “sorry, that’s not us.”

In the best case you remember a peer who’d be a fit and you make a phone call. In the worst case the lead bounces away to whoever Googles best, the warm intro is wasted, and a relationship that started well ends without anyone winning.

This week we shipped the MSPercury Partner Network — four integrated features that turn that scenario into a workflow with all the data the platform already has. No marketplace fees, no commission structure, no token economy, no money flow between users at all. Reciprocity wired into the product so the math works for the long-haul. This post is the architecture, the regulatory framing, and the thinking behind it.

What the four features do, in order

1. Specialty profiles per workspace. Region, industries, technical skill stack, service model (on-site / remote / 24×7 / business hours), capacity status (open / selective / full), bio. Set once, used by everything else. Without this profile, your workspace can’t appear in the partner directory and can’t be picked as a forwarding target — the floor for participation is “we know roughly what you do.”

2. Partnerships between MSPs. LinkedIn-style mutual connections. One MSP requests, the other accepts in their inbox, both sides see each other’s full profile and can forward leads to each other. One-sided revoke tears the link down. Not transitive: a friend of your partner doesn’t get automatic visibility, just appears as “indirect” in the network view.

3. Lost-reason tracking with smart-match forwarding. When you mark a customer as lost, you pick from a structured catalog of reasons — too far, capacity full, Linux skills missing, wants 24×7, price too low, 22 codes total. Each reason carries a match-tag that the suggestion engine uses: LINUX_MISSING + a partner with linux in their skills → match score boost. The result is a ranked top-3 of partners actually positioned to take the lead, not a spammy broadcast.

4. GDPR double-opt-in lead forwarding. When you pick a partner, we email the lead a confirmation page — token-based, no login required. They confirm or deny in one click. Only after their explicit confirmation (timestamped, IP-logged, audit-trailed) does the receiving MSP see the contact details — and they can still decline in their inbox. The lead’s PII never leaves your tenant database until both gates have been passed.

The bit that mattered most: no money flow

Anyone who has even glanced at marketplace regulation in the EU knows there’s a cliff somewhere between “we connect people” and “we operate a payment system.” Once a platform routes money between users, it crosses into ZAG (Zahlungsdiensteaufsichtsgesetz — the German payment-services law transposing PSD2), potentially into MiCA territory if you start tokenising the credits, and you trip GwG (anti-money-laundering) obligations the moment you settle anything for users.

We picked the side of that cliff that doesn’t require a BaFin licence: pure data exchange. No commissions on forwarded leads. No “10 referral credits per accepted lead.” No escrow of subscription splits. Karma points are non-redeemable, non-transferable, scoped to one workspace, and exist purely as social signal: how active is this MSP in the network, do they accept what gets sent to them, do they convert it. They influence sort order in the directory and surface “active member” badges. They don’t pay for anything.

This was a deliberate choice with real trade-offs. Money flow would obviously be the most direct way to reward forwards, and there are platforms that do it well (Upwork, Fiverr, etc., all live on the regulated side of that cliff because their volume justifies it). MSPercury is a long-tail B2B SaaS with — in early access — a few dozen workspaces. Burning legal-engineering budget on payment licensing instead of building the actual product is the wrong order of operations. We can revisit when the network density makes it interesting.

The strategic bet is that reciprocity in a small dense network reproduces the right incentives without money — if you forward leads to peers and they forward back, both sides win on customers acquired without ad spend. The network gets denser, smart-match gets better, more leads route correctly. That’s the moat. The karma score is just a visible scoreboard on top of an underlying loop that’s already doing the work.

The state machine: why double-opt-in is in the schema

We initially mocked up a flow where the operator picks “forward to MSP X” and the lead’s contact details immediately go over the wire. Got two paragraphs into the privacy review and stopped — that’s a clear-cut case of “passing personal data to a third party without an Art. 6 lawful basis.” Even if the lead had consented to “MSPercury processing” in our generic onboarding ToS, that consent doesn’t cover handing the data to a peer organisation they’ve never heard of.

So we built the state machine into the database:

pending_lead_consent → lead_consent_given → accepted
                    → lead_consent_denied
                    → expired (after 14d)

Three explicit gates between “operator wants to forward” and “receiver MSP sees contact data”:

  1. The operator initiates → row created, status pending_lead_consent, no data leaves their tenant DB.
  2. The lead clicks the confirmation link in the email → status flips to lead_consent_given, IP + timestamp + user-agent stored as Art. 7 GDPR proof. The receiver MSP sees an anonymised preview in their inbox (industry / region / score, no name, no email).
  3. The receiver MSP accepts → only now do we duplicate the customer record into their workspace. Or they decline, and the lead stays with the original sender.

Each transition has timestamps. The consent token is single-use, expires after 14 days, lives nowhere on the customer side except in the email we sent. The /forward/confirm/[token] page works without a login — it’s a token-only public page styled like the customer portal, branded with the requesting MSP’s name so the lead recognises the context.

The receiver’s inbox shows incoming forwards as cards: “From [MSP X] · ●●●●●● · Cologne · Linux skills missing · ‘Customer has 30 endpoints, looking for proactive monitoring’.” That’s enough for the receiver to decide if they want it. Click accept → contact details unlock and the lead lands as a fresh row in their kanban.

This three-step gate is more friction than a money-driven marketplace would tolerate. For a peer-network it’s the right amount: the lead chooses, then the receiver chooses, both sides in a clear lawful basis the whole way.

How smart-match works

The lost-reason catalog isn’t just labels — every reason carries a MatchTag describing what to compare against in a partner’s specialty:

ReasonMatch dimension
TOO_FAR, OUT_OF_SERVICE_AREApartner serves the lead’s region
LINUX_MISSING, MAC_MISSING, CLOUD_FOCUS_MISMATCHpartner has the relevant skill in their stack
WANTS_MORE_HANDS_ON, WANTS_24X7partner offers the matching service model
INDUSTRY_SPECIFICpartner covers the lead’s industry vertical
CAPACITY_FULLfilter out partners also at capacity

The scorer applies weights — region match: +30, skill match: +25, service-model match: +20, industry match: +15, generic shared-region tiebreaker: +10 — and adds a small capacity bonus (+5 for “open”, +2 for “selective”). Partners with capacityStatus = 'full' get filtered out automatically; full-house MSPs don’t get suggested.

Dead simple, deliberately. Operators can read the score in their head. We can tune weights in one place. No black-box AI ranking that’s hard to explain when a partner asks “why did you suggest me for this lead.” The full code is at src/lib/network/smart-match.ts for anyone curious.

Where we are and what’s still ahead

What’s live as of this deploy:

  • Specialty profile editor at /settings/specialty
  • Partner directory with filters at /network
  • Network inbox at /network/inbox (incoming partnership requests + forwarded leads)
  • Lead-forward subpage on every customer detail
  • Public consent confirmation at /forward/confirm/[token]
  • All seven server actions wired (specialty save, request/respond/revoke partnership, initiate/accept/decline forward)
  • Four transactional email templates for the flow
  • Privacy policy section 11 documenting the whole mechanism
  • Karma event recording + on-demand recompute
  • All migrations idempotent, schema seeded with karma=50 for every existing org

Deferred to a follow-up sprint:

  • The force-directed SVG spider-web visualization of the network (1° + 2° + 3° degree connections). Meanwhile, the directory and inbox give you the actionable view; the visualization is more for showing-off than for getting work done.
  • Karma cron as a systemd timer doing nightly recomputes. Today the recompute runs after every relevant action and lazily on the partner-detail page, which scales fine for early access.
  • Bilateral rating system (1–5 stars after a forward closes) — comes back when we have enough partnerships to rate.

The shipped sister features

This deploy also bundles the work from earlier in the week — the MSPercury you’re looking at is also the one that:

  • Migrated from Pay-What-You-Want to fixed-price subscriptions via Stripe, with grandfathered Early Access for existing tenants. Webhook live, customer portal connected, all six Stripe lifecycle events handled. Pricing page rewritten in DE/EN/ES.
  • Last-contact indicator on the customer list — aggregates five outbound channels (operator quote messages, sent service reports, maintenance windows, customer-visible status updates, sent agreements) into a single staleness badge.
  • Operator send-later for quote replies — schedule a draft for tomorrow 09:00 from the composer, an in-process worker fires the email at the right moment.
  • iOS PWA notification bell finally tappable (was three stacked bugs: duplicated inline-script attaching listeners twice, badge intercepting clicks, panel positioning blowing past the viewport edge — postmortem in last Monday’s blog).

If you’re looking at MSPercury for the first time today, the elevator pitch is unchanged from what we built it for: one tool that takes a deal from on-site CheckUp through to PDF quote through to the entire customer-comms life-cycle, in your brand, on your domain. The network we shipped this week is what makes that one tool feel less alone — when a customer doesn’t fit you, you have a structured way to send them somewhere they will, and the loop comes back around.

— Lucas