Skip to main content
Didit Raises $7.5M to Build the Infrastructure for Identity and Fraud
Didit
Back to blog
Blog · August 4, 2026

Blocklist Propagation: Making One Confirmed Abuse Case Kill the Whole Network

Banning an account removes one head from the hydra. Blocklisting from a session auto-extracts every identifier it touched — face, document, phone, email, IP, device — across 12 entry types, so the next account fails at the door.

By DiditUpdated
ai-api-abuse-blocklist-propagation.png

There is a specific moment where most abuse programmes leak value: the moment after you win.

Your traffic layer flags an account. An analyst investigates. The evidence is solid, the case is confirmed, the account is banned. And then, hours later, the same operator is back on new accounts, because the only thing your enforcement touched was a row in a users table.

Anthropic described exactly this dynamic in its February 2026 report on distillation campaigns — a proxy network that "managed more than 20,000 fraudulent accounts simultaneously," replacing them as they were removed. Removal was not the bottleneck. Regeneration was cheaper than removal.

The fix is to make enforcement operate on identifiers instead of accounts. Didit's Lists API is built around one mechanic that does this in a single call.

Key takeaways

  • Blocklisting with reference_session_id makes Didit auto-extract the right value from the session — face, document, phone, email, IP or device — mark the underlying model blocklisted, and link the entry back to the source session.
  • 12 entry types: face, document, phone, email, ip_address, device_fingerprint, wallet_address, bank_account, user, business, country, key.
  • Blocklists are system-created, one per entry type, and immutable. You cannot create them, which is what makes enforcement uniform.
  • Entries take effect immediately at verification time. Deleting an entry unblocks the match.
  • ip_address accepts a CIDR range, so you can blocklist infrastructure rather than a single address.
  • Allowlists across the same 12 types keep trusted developers out of every escalation path.

The two list types that matter

The API has three list types, and the distinction between them is deliberate.

Blocklists are system-created — one per entry type — and immutable. You cannot create them, rename them, or delete them. You add and remove entries. That constraint is a feature: it means "blocklisted" has exactly one meaning across your organisation, and there is no way to end up with four competing face blocklists that different services check inconsistently.

Allowlists are yours to create. Known-good devices, address ranges, business entities and users go here.

Custom lists are for everything else — your own taxonomies, watch groups, review cohorts.

# Find the face blocklist
curl -X GET 'https://verification.didit.me/v3/lists/?list_type=blocklist&entry_type=face' \
  -H 'x-api-key: YOUR_API_KEY'

The mechanic that matters

Here is the ordinary way to blocklist something, and it is the wrong way in almost every real situation:

curl -X POST 'https://verification.didit.me/v3/lists/{list_uuid}/entries/' \
  -H 'x-api-key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{ "value": "203.0.113.44" }'

That blocklists one address. Meanwhile the session you were investigating also carried a face, a document number, a phone, an email and a device fingerprint — every one of them an identifier the operator has to replace before returning.

The better call:

curl -X POST 'https://verification.didit.me/v3/lists/{list_uuid}/entries/' \
  -H 'x-api-key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{ "reference_session_id": "a7f3...c19" }'

Pass reference_session_id and Didit auto-extracts the right value for that list's entry type from the session, marks the underlying model as blocklisted, and links the entry back to the session so the console can show you where it came from.

Three things fall out of that, and all three matter operationally:

No manual extraction. Your analyst does not read a device fingerprint off a screen and retype it. Transcription errors in enforcement data are silent failures — the ban simply does not fire, and nobody finds out.

Provenance is preserved. Every entry links back to the session that justified it. When someone asks in four months why this device is blocked, the answer is one click, not an archaeology project.

Enforcement is repeatable. The same call against each entry-type blocklist covers the session's whole identifier surface.

If a session contains multiple instances of the same type, pass value alongside reference_session_id to disambiguate.

You can also enforce from outside a verification session. reference_object_uuid together with metadata.reference_typetransaction, vendor_user or vendor_business — blocklists from a transaction or from a vendor user or business, and keeps the same link back to the source.

The 12 entry types

Entry typeBlocksNotes
faceThe personAlso loadable directly via face-upload
documentThe credential
phoneThe numberAuto-normalised to E.164
emailThe address
ip_addressThe address or rangeAccepts CIDR, e.g. 10.0.0.0/8
device_fingerprintThe machineMinimum 8 alphanumeric characters
wallet_addressThe on-chain address
bank_accountThe account
userThe user record
businessThe entity
countryThe jurisdiction
keyA custom key

Two are worth calling out for this problem.

ip_address accepts a CIDR range. Blocklisting 203.0.113.0/24 blocks the infrastructure, not one address. When a farming operation runs on a rented subnet, this is the difference between enforcement that scales and enforcement that plays whack-a-mole. Use it carefully — a range covers real users too, and over-broad ranges are how you quietly ban a country's mobile carrier.

face supports direct upload. When you have an image but no session, load it straight in:

curl -X POST 'https://verification.didit.me/v3/lists/{list_uuid}/entries/face-upload/' \
  -H 'x-api-key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{ "image": "<base64-encoded image>" }'

Didit extracts the biometric. From then on, that face is screened at every verification.

What happens after an entry lands

Adding to a system blocklist blocks future matches immediately at verification time. There is no propagation delay and no batch job.

At the next verification, the enforcement surfaces as warnings:

  • FACE_IN_BLOCKLIST — definitive match, verification declined. POSSIBLE_FACE_IN_BLOCKLIST is a borderline match below the hard threshold and should route to review, not decline.
  • IP_ADDRESS_IN_BLOCKLIST / DEVICE_FINGERPRINT_IN_BLOCKLIST — network and device hits.

In Face Search, a blocklist match is the only thing that sets status to "Declined". Every match in the response also carries is_blocklisted, so an investigation shows you immediately which parts of a cluster are already under enforcement and which are still live.

Removal is symmetric: deleting an entry unblocks the matching entity. Enforcement is reversible by design, which matters because over-broad enforcement is a real risk and you need a clean path back.

curl -X DELETE 'https://verification.didit.me/v3/lists/{list_uuid}/entries/{entry_uuid}/' \
  -H 'x-api-key: YOUR_API_KEY'

Allowlists: protecting the developers you want

Enforcement that only escalates eventually strangles the product. The same 12 entry types support allowlists, and they are the pressure valve.

Allowlist the office IP ranges of a design partner. Allowlist the devices of an enterprise customer's engineering team. Allowlist a verified business entity so its users never hit an escalation path. IP_ADDRESS_IN_ALLOWLIST and DEVICE_FINGERPRINT_IN_ALLOWLIST fire when a match occurs, so you can confirm the exemption applied rather than assuming it did.

This is the mechanism that makes aggressive enforcement survivable. You can afford a strict policy on unknown infrastructure precisely because your known-good population is explicitly carved out.

Errors worth handling

  • 400 — the value failed the entry type's validator, the list_type is wrong for the operation (for example, a face-upload against a non-face list), or reference_session_id has no data of the requested type. That last case is common and benign: not every session captures every identifier.
  • 403{"detail": "You do not have permission to perform this action."}

Note that a 400 from "session has no data of that type" is expected when you loop a session across all the entry-type blocklists. Handle it as a skip, not a failure.

A worked enforcement flow

An analyst has confirmed abuse on account acct_8842.

  1. Resolve the cluster first. Face Search on the session's face returns twelve accounts; device and IP correlation pulls in another dozen. Enforcement before resolution bans one account and warns the operator.
  2. Blocklist from the confirmed sessionreference_session_id against the face, device, IP, email, phone and document blocklists. Six calls, no manual extraction, full provenance.
  3. Consider the range. If the network evidence points at rented infrastructure, a CIDR entry covers the subnet. Check what else lives there first.
  4. Act on the cluster according to your own policy — the accounts you already identified do not un-ban themselves.
  5. Verify the enforcement. Re-run the face search. Matches should now carry is_blocklisted: true.
  6. Wait for the regeneration attempt. The next account created on that face, device or subnet declines at verification instead of appearing in your traffic three weeks later.

Step 6 is the whole point. The operator's cost to return is no longer "make a new email address." It is "acquire new hardware, new network, and a new person."

Use cases

AI API platforms converting a confirmed distillation or abuse case into enforcement across every identifier the operator touched.

Trial and credit abuse where the same device and face keep returning for a new free allocation.

Marketplaces blocking removed sellers from re-registering under a new business.

iGaming enforcing self-exclusion, where a returning excluded player is a regulatory failure and face-level enforcement is the only reliable control.

Frequently asked questions

Can I create my own blocklist?

No. Blocklists are system-created, one per entry type, and immutable — you add and remove entries. You can create allowlists and custom lists freely. The constraint keeps "blocklisted" meaning one thing everywhere.

How fast does an entry take effect?

Immediately, at the next verification.

What if I blocklist something by mistake?

Delete the entry and the entity is unblocked. This is why provenance matters — every entry created from a session links back to it, so you can audit what an entry was for before removing it.

Does blocklisting an IP range affect legitimate users on that range?

Yes, and that is the risk. A CIDR entry blocks everything inside it. Use ranges when the evidence points at dedicated infrastructure, use single addresses otherwise, and allowlist known-good ranges first.

Can I blocklist from something that is not a verification session?

Yes. reference_object_uuid plus metadata.reference_type (transaction, vendor_user or vendor_business) covers transactions and vendor users or businesses.

Does this stop model extraction?

No. It stops a specific known actor from re-entering through the identifiers you have enforced against, and it raises the cost of regeneration. Detecting extraction in the first place is your traffic layer's job, and limiting what extraction yields is your model layer's job. This is the enforcement arm of a three-layer defense, not a replacement for the other two.

Ready to get started?

The Lists API is available on every Didit account.

Infrastructure for identity and fraud.

One API for KYC, KYB, Transaction Monitoring, and Wallet Screening. Integrate in 5 minutes.

Ask an AI to summarise this page
Blocklist Propagation Across 12 Entry Types | Didit