Multi-Tenant Dark Web Monitoring: 50 Clients, One Integration
Picture a Tuesday. A breach of a popular project-management tool lands in our index that morning, and eleven of your clients have staff addresses in it. One of those clients runs three domains, and two others belong to the same group but pay you separately.
Who gets which ticket?
That question is what multi-tenant dark web monitoring comes down to for an MSP or MSSP. One account, one API key and one signed webhook watch every client domain you onboard. A small table on your side, client to domains, turns each hit into the right client's ticket, channel and report.
(A tenant here is one of your clients. In xonThreatIntel+ the thing we monitor is the domain, so a client is simply the set of domains you've mapped to them.)
We're XposedOrNot. We run a public breach index, 794 breaches and 11.7 billion+ records as of 20 October 2026, counted by our live API, and xonThreatIntel+, the partner product MSSPs resell. What follows is the build we watch partners do in their first week, including the two places it usually trips.

What does multi-tenant mean when the tenant is a client?
Most pages on the subject stop at "one dashboard, separate clients". Fine as far as it goes. But whether that holds at client 50 depends on the plumbing underneath, and the plumbing is mostly yours.
On our side the model is deliberately flat. Your account holds a domain quota. One key reads every domain on it, and the console lets your team flip between all approved domains and any single client domain.
There's no client object in the API. The domain is the unit.
So where does the client live? In a map you already half-own.
Your PSA (the professional services automation tool most MSPs ticket and bill from) knows each client's company ID, contacts and contract tier. The map adds one column. Which domains belong to whom.
| client_id | domains | PSA company | alert contact | service tier |
|---|---|---|---|---|
| C-014 | northwind.example, northwind-hr.example | 1042 | [email protected] | Managed security |
| C-022 | fabrikam.example | 1107 | [email protected] | Help desk only |
| C-031 | contoso-group.example, contoso-retail.example, contoso-labs.example | 1213 | [email protected] | Managed security |
Three clients, six domains, six quota slots. When you size the plan, it's the domains you count (we'll come back to that).
Why one integration and not fifty?
Because fifty of anything is a job. A 2025 survey of 200 US MSPs found they look after 50 clients on average, and most run four security tools from four vendors.
Ninety-four percent were looking for a single platform. (Cynet ran it, and Cynet sells to MSPs, so read it as a direction.)
Fifty separate accounts would mean fifty keys to store, fifty webhook secrets to rotate and fifty places where a setting drifts. One integration means one of each.
There's a security reason too, and it matters more than tidiness.
The joint MSP advisory from CISA, the NSA, the FBI and four allied agencies (AA22-131A, May 2022 ) is blunt about it. Attackers "can use a vulnerable MSP as an initial access vector to multiple victim networks". MSPs should "segregate customer data sets" and never reuse admin credentials across customers.
Our partner key isn't an admin credential on anyone's network. It reads exposure facts. All of your clients' exposure facts, though.
So keep it in a vault and rotate it on a schedule. Put 2FA on every console user, keep Admin and Analyst roles apart, and actually read the activity log.
The segregation itself lives in your map, and in who on your team sees which rows.
How many alerts will 50 clients actually produce?
Fewer than people fear. More than one inbox can handle without rules.
We counted our own additions. In the 52 weeks to 19 October 2026 we added 149 breaches to the catalog, about 2.9 a week. The median week brought two, seven weeks brought none, and the busiest single week brought nine.

Every new breach gets checked against every domain you monitor. With 50 single-domain clients that's 7,450 client-against-breach checks over the year. Most new breaches miss most clients, though a big scrape or a stealer-log set can touch nearly every business domain at once.
Either way, the integration's job is to turn hits into a sensible number of tickets.
Not every hit deserves the same reaction, either. Here's what those 149 breaches looked like.
| Of the 149 breaches added in the 52 weeks to 19 October 2026 | Count | Share |
|---|---|---|
| Exposed passwords in some form | 38 | 26% |
| Of those 38, passwords in plain text or easy to crack | at least 20 | |
| Breach happened more than a year before it reached our index | 44 | 30% |
| Median gap between the breach and indexing | 73 days |
The 20 is a floor, since six of the 38 don't say how the passwords were stored.
And the last row matters for every promise you make a client. Your alert clock starts when a breach lands in our index, not when the data was stolen. We wrote up what MSSPs can and can't promise around exactly that gap.
Why bother watching at all, then? Look at the Verizon 2025 DBIR . Of 503 ransomware victims it checked against infostealer and credential dumps, 54% had their domains in those dumps.
And 40% had corporate email addresses among the stolen credentials.
Credentials tied to a client's domain are an early warning. A per-client watch on every breach we index is one way that warning reaches your desk first, instead of the client's.
Onboarding 50 client domains in an afternoon
The step-by-step (upload file, batch status, ready_for_query, the since bookmark) is in our vendor-domain walkthrough, and it's the same API. What changes with a client book is the arithmetic.
Quota first. Before you build the file, GET /v2/partner/quota says how many slots are left. Fifty clients go in one file easily (the cap's 500). Just know it's all or nothing, so one domain over quota and the lot bounces.
Pending domains hold a slot. So does a deleted one, until the UTC month ends. If a client leaves mid-month and a new one signs the same week, you'll want a spare slot, or you'll be waiting for the 1st.
Very large domains wait for a person. Anything with more than 100,000 breach records goes to admin review, typically within 24 hours. Everything else is usually approved in minutes.
Then key your integration on ready_for_query, not on "approved". Approved means the domain resolved. Ready means we've finished building its breach data.
One more thing, and it's the first place partners trip. A client's first result is their whole history, every old breach their staff ever turned up in.
Our advice? Call it the baseline and send it as the client's first report. Keep the as_of watermark it came with, because only what turns up after that is news.
We've seen what happens otherwise. Client 12 gets 40 tickets on day one, most of them about some forum that leaked in 2016.
One breach, eleven clients: attribution and dedupe
Back to the Tuesday.
When a newly indexed breach touches several of your domains, the account webhook sends one signed notification. It lists each affected domain with its own counts, and never an email address. Trimmed to three domains, it looks like this:
{
"event": "new_findings",
"as_of": "2026-10-20T11:42:07Z",
"domains": [
{"domain": "northwind.example", "new_breaches": ["SampleProjectTool"], "new_records": 6},
{"domain": "northwind-hr.example", "new_breaches": ["SampleProjectTool"], "new_records": 2},
{"domain": "fabrikam.example", "new_breaches": ["SampleProjectTool"], "new_records": 14}
]
}Three domains in that example, but only two clients. And this is the second trip. Northwind should get one ticket covering both of its domains, not two tickets that two technicians pick up separately.
So the fan-out goes domain to client, then groups. A dozen lines does it:
def fan_out(payload, client_map, seen):
if payload.get("event") != "new_findings":
return # verification pings and other events
by_ticket = {}
for d in payload["domains"]:
client = client_map.get(d["domain"])
if client is None:
flag_unmapped(d["domain"]) # quota slot with no client row: fix the map, never drop
continue
for breach in d["new_breaches"]:
by_ticket.setdefault((client.id, breach), []).append(d["domain"])
for key, domains in by_ticket.items():
if seen.claim(key): # insert under a unique constraint; False if already there
open_ticket(key, domains, payload["as_of"])The dedupe key is client plus breach name.
Keep it in a store with a unique constraint, and write it before the ticket exists. A retry landing in the same second then still finds it. That matters because we make up to three delivery attempts on connection errors, timeouts, 429s and 5xx answers.
If you also poll with since, run those results through the same claim, and the ticket pulls its per-domain counts fresh from domain-summary.
And the unmapped branch matters more than it looks. A domain on your quota with no row in your map is a client you're paying to monitor and nobody is reading. Flag it, loudly.
Verify the signature before any of this runs. It's an HMAC-SHA256 over the timestamp, a dot and the raw body, and the webhook reference has working code. Then answer with a 2xx fast and do the fan-out on a queue.

Routing: from notification to the right desk
The notification tells you something new arrived. The detail comes from one domain-summary call per domain.
Three of its fields decide where the ticket goes. How recent the newest breach is, whether a readable password was in it (password_risk), and whether any executives were (Seniority_Summary).
At 25 calls a second per key, even the busiest week we saw (nine new breaches) is a few seconds of API time across 50 clients.
A priority rule we'd start from, then tune per client:
| Newest exposure | Readable password? | Executives in it? | Priority | Who picks it up |
|---|---|---|---|---|
| Indexed this week, breach under 12 months old | Yes | Any | P1 | SOC, same day; help desk forces resets |
| Indexed this week, breach under 12 months old | No | Yes | P2 | SOC; phishing warning to the named execs |
| Indexed this week, breach older than 12 months | Yes | Any | P3 | Help desk confirms resets at next touch |
| Anything else | No | No | P4 | Logged to the client's monthly report |
Notice the split in the last column. Most of you run a help desk and a security desk under one roof, the MSP side and the MSSP side, and both read the same feed.
They just do different things with it. The help desk resets. The SOC asks whether anyone logged in with the old password.
Per-client tuning happens at two levels. On our side, alert rules can be set per client domain. Hold a help-desk-only client to critical findings and let a managed-security client hear about everything.
On your side, the client map's service-tier column decides which queue each ticket lands in.
Slack and Teams delivery works on xonThreatIntel+ too, which suits the team channel where the SOC watches for P1s. We'd still let the ticket come from the webhook, because a chat message can't be assigned, dated and closed. Our routing guide covers pushing a webhook into a channel, and the first 48 hours after a client domain is hit covers what the SOC does once the P1 lands.
Oxford researchers put the cost of getting this wrong on record.
They interviewed analysts across seven SOCs (Alahmadi, Axon and Martinovic, USENIX Security 2022 ). One said: "We know 99% of the alarms we generate are false positives, but we still have to look at them." The bigger finding was that alarms arrive without context (whose asset, whose network, who owns it).
Attribution is the context. A ticket that already says "Northwind, two domains, readable passwords, one director" doesn't need an analyst to work out whose problem it is.
Per-client reports without fifty exports
The console sends a scheduled report weekly, monthly or quarterly. One report covers every approved client domain, with a CSV that carries the client domain on every row. It goes to your own team, never to your clients.
That's on purpose. You decide what each client sees.
Split the CSV by the domain column, join it to your map, and you have one file per client. The files you build from the split carry whatever name and branding you put on them, and the partner agreement covers delivering them under your brand (what that includes, and what we store).

Is it worth the effort? Sophos surveyed 800 MSPs in 2026. They said nearly half their customers (46%) lean on them as an acting CISO, and only 31% could turn out reports quickly through a fully automated process.
Sophos sells to MSPs as well, so same caveat as before. What partners tell us points the same way, and the report is where a client sees the monitoring they're paying for.
What goes in one, in our experience:
- New findings this period, with both dates: when the breach happened and when it reached the index.
- What you did about each one, and when.
- Anything still open.
- The baseline from onboarding, kept short and clearly labelled as history.
Data handling, contracts and offboarding
What flows through all of this is exposure facts: which address, which breach, what kind of data, when. Never the stolen records themselves. That's what lets a client's legal team say yes.
Under our data processing agreement you can act as processor for your clients, with us as your sub-processor. We keep the monitored domains, alert settings and results for the life of your subscription, and return or delete them within 30 days of it ending.
Three habits we'd add on your side. Name the domain monitoring in each client's contract. Keep address-level rows inside the team that acts on them, since a list of a client's exposed staff addresses is personal data about their people.
And when a client leaves, close it out properly. Send the final report and remove their domains (the slot frees at the end of the UTC month). Then delete their rows from your map the same day, so nothing new routes to a client you no longer serve.
What does multi-tenant monitoring cost per client?
Our prices are public. xonThreatIntel+ is $99 a month for 10 domains, $243 for 25 and $447 for 50, flat monthly with no per-user fees. Webhooks and JSON feeds start at the $243 tier, which, after everything above, is the tier a client book actually needs.
Divide it out and 50 domains cost $8.94 each a month. Past 50 we price it with you, and there's a 30-day money-back guarantee on every plan.
What can you charge? That one's for you and your market. What we can say is that the report is the part of the service a client can see, so a dated, per-client finding with a dated response next to it is what they're paying for.
One free starting point before any of it. Your own company's domain is free on the community side at xposedornot.com/domain .
Start there. You'll read the result the way a client reads theirs.
Your multi-tenant setup checklist
- Client map built: client ID, domains, PSA company, alert contact, service tier.
- Plan sized by domains, not clients, with one spare slot for mid-month swaps.
- Quota checked, then one upload file; integration keyed on
ready_for_query. - First result filed as each client's baseline;
as_ofsaved before anything counts as new. - Webhook signature verified, 2xx returned fast, fan-out on a queue.
- Dedupe key set to client plus breach name.
- Unmapped-domain flag goes to a named person.
- Priority rule written down, per-client alert rules set to match each service tier.
- Monthly report split by domain, branded, sent with both dates on every finding.
- Monitoring named in each client contract; DPA signed.
- Offboarding steps written: final report, domains removed, map rows deleted.
- Your own domain checked first, free.
Quick answers
What is multi-tenant dark web monitoring?
One service watching lots of clients' domains, where every finding lands on the right client. In xonThreatIntel+ that's one account, one API key and one signed webhook for your whole book, and your own client-to-domain map does the sorting into tickets and reports.
Does an MSP need one account per client?
No. A single xonThreatIntel+ account holds the domain quota for all of them, and one key reads every domain on it. A client is just their domains, so somebody with three domains takes three slots.
How does one breach alert get attributed to the right client?
The webhook notification lists each affected domain with its own counts, and your integration looks each domain up in your client map. Group the domains by client and open one ticket per client per breach, keyed on client plus breach name, so redeliveries never double up.
How much does dark web monitoring cost per client for an MSP?
xonThreatIntel+ is $99 a month for 10 domains, $243 for 25 and $447 for 50, with custom pricing above that. At 50 domains it works out to $8.94 per domain a month, with no per-user fees.
Why does a new client open dozens of tickets on day one?
Because their first result is history: every old breach their staff ever showed up in. So send it as a baseline report, hang on to the as_of watermark it came with, and start tickets after that.
How many new breaches will 50 clients be checked against?
We added 149 to our index in the 52 weeks up to 19 October 2026. That's under three a week on average, though one week brought nine and seven brought none. Which clients each one touches comes down to their domains, and now and then a big scrape lands on nearly all of them.
Where the numbers came from
- Catalog figures are ours, from the public endpoint at api.xposedornot.com/v1/breaches. When we pulled it on 20 October 2026 it had 794 breaches in it (11,717,004,638 records). For the year, we kept only what was added from 21 October 2025 up to 19 October 2026, then counted by week, by password field and by how late each one showed up. Records, because one address can turn up in plenty of breaches (how we count).
- 7,450 is just multiplication: 50 single-domain clients, 149 new breaches.
- Cynet's Managed Security Snapshot 2025 (23 July 2025) is where the 50-client average, the four tools and the 94% come from. Global Surveyz asked 200 US MSPs, for a vendor.
- CISA, NSA and FBI wrote AA22-131A with the UK, Australian, Canadian and New Zealand agencies, and published it on 11 May 2022. Both quotes are from it.
- Page 11 of the 2025 Verizon DBIR has the 54% and the 40% (the 503 ransomware victims are counted in Figure 62).
- The analyst quote comes from Alahmadi, Axon and Martinovic, "99% False Positives" , USENIX Security, August 2022. That 99% is one person's estimate, and the paper says so.
- Sophos published the 46% and 31% in MSP Perspectives 2026 (800 MSPs, seven countries, Vanson Bourne doing the asking in April 2026).
- Anything about how the API behaves, we checked against our own docs again on 20 October 2026, retries and slot rules included. Onboarding Domains , Checking Breaches , Webhook , Reports and Domains , where the review and approval times live. Roles, 2FA and the activity log are on the users , security and activity log pages.
- Prices are off the xonThreatIntel+ partner page as it stood on 21 October 2026. Retention terms are in the DPA.
- Northwind, Fabrikam, Contoso and "SampleProjectTool" are invented, as are the payload values, the code and the priority table. They match no client and nothing in the catalog.