Blocklist
The global blocklist holds terms Lumio never shows, whichever channel they come from: profanity, slurs, spam. Every module that checks text asks the same list. Today that is the buyer name on Shopify order events: a name the list hits is dropped silently and the order appears without a name. A channel's own word filter is separate and keeps working.
The list is never public. It is readable only in the admin app and through the admin API (/v1/admin/blocklist… and the admin* GraphQL fields); no web page, widget, extension or WebSocket channel exposes it.
Where to find it
Admin sidebar → Moderation → Blocklist (/blocklist). The sidebar item and the page need blocklist:read; without it the page shows the no-access view.
Permissions
| Action | Permission |
|---|---|
| View the list, search and filter; check a text | blocklist:read |
| Add an entry; import a list | blocklist:create |
| Edit an entry; switch it on or off | blocklist:edit |
| Delete an entry | blocklist:delete |
These are admin-scope permissions, seeded to system_admin; other admin roles get them on Admin Roles. They never appear in a channel's role picker. Every change needs an interactive admin session (admin 2FA applies), so no API or System key can change the list. A control you lack the permission for is hidden; without blocklist:edit a row shows an Active / Off badge instead of the switch.
The list
Entries appear in creation order, oldest first, 50 per page; Load more appends the next page. Search matches part of the term or the internal note, case-insensitively. Filters: kind, category, language (the exact code, such as de) and active or off. Each row shows the term, kind, category, language, status and when it last changed.
Kinds
| Kind | Hits when | Example |
|---|---|---|
Part of a word (substring) | the term appears anywhere, also inside a longer word | badword hits xbadwordx |
Whole word (word) | the term appears as a whole word only | ass hits an ass, not Cassandra |
Wildcard (wildcard) | * stands for any run of characters; everything else is literal | sp*m hits spam and spooom |
Regex (regex) | the regular expression matches, case-insensitive | b[a4]d hits bad and b4d |
Every active entry checks every text, whatever its language. Language and category only sort and filter the list.
Normalisation
Before a text is checked, Lumio normalises it: Unicode NFKC (full-width Bad becomes Bad, ligatures split), lowercase, invisible characters removed (zero-width spaces and joiners, soft hyphens, bidi controls, variation selectors), runs of whitespace collapsed. Part of a word, Whole word and Wildcard terms are normalised the same way, so capitals, full-width letters and hidden characters do not get around them. A Regex pattern is used as written (case is part of its meaning: \D is not \d) and runs on the normalised, lowercased text. There is no automatic leetspeak folding: to catch b4d, write a regex such as b[a4]d. Only the first 256 characters of a text are checked.
Adding an entry
Add entry opens a dialog: kind (each with an explanation), term or pattern, category (optional: profanity, slur, sexual, violence, spam, other), language (optional, a code such as de, en or de-AT), internal note (optional) and Active. A saved entry takes effect on every API instance within seconds, at the latest after 60 seconds.
Lumio refuses an entry, and says why, when:
- the term is empty or longer than 512 characters, or the note longer than 500;
- a regex does not compile (the dialog shows the compiler's message) or is too large: each regex or wildcard may compile to at most 64 KiB, enough for one Unicode class such as
\w, not for a counted repeat such as\w{20}; - it matches too broadly: a regex or wildcard does not name at least two characters that every match must contain, or the entry hits an empty text or one of a fixed set of ordinary names. A character counts in full when the pattern fixes it (
b) and half when it is one of up to four alternatives ([a4]). Sob[a4]d,n[i1]gg,\bbad\w*and the wildcardf*cksave, while\p{Thai},[ğşı],ß,^.{11,15}$or^\S+ \S+ \S+$are refused because they single out a script, a letter group, a length or a word count rather than a term. The set covers short names and initials (Anna, Max M.), accented Latin (Jürgen, José, Łukasz), other scripts (Cyrillic, Greek, Han, Arabic, Hebrew, Devanagari, Hangul, kana), long and hyphenated names (Alexandra-Sophie, a 25-character name), apostrophes, underscores and digits (O'Brien, Sam_99, Jo 2). So.,[^a-z],.{9,}or\dare refused, and a substring or word entry such asanthat hits one of these names is refused too; a real offensive word still saves; - the same kind with the same normalised term already exists ("already on the list");
- the list is full (
max_entries, default 10,000 entries, ormax_pattern_entries, default 500 regex and wildcard entries; both count entries that are switched off); - the list would get too large to check: Lumio compiles the whole active list before it saves, and refuses when the new or changed pattern would not fit together with the others. Nothing is saved. Switch off or delete other regex or wildcard entries first. Switching off and deleting are never refused.
Import
Import takes one term per line, at most 1,000 lines per call. Kind, category and language apply to every line; imported entries are active and have no note. Blank lines are skipped. Each line is checked like a single entry:
- Added: saved.
- Already there: the term is on the list, or appears twice in the import.
- Invalid: refused, with the same reason a single entry would get.
The result shows the totals and each line with its result; by default only the lines that were not added. Invalid lines do not stop the others. If the import would pass a size cap or make the list too large to check, it fails as a whole and saves nothing.
Edit, switch off, delete
The switch in a row turns an entry on or off; an entry that is off stays saved but hits nothing. Edit changes kind, term, category, language, note or status; changing the kind or term is checked like a new entry, switching on is checked against the list size. Delete asks for confirmation and suggests switching off as a pause; a deleted entry stops hitting at once and frees its place in the caps.
Check a text
Check a text shows whether the active list hits a text (up to 256 characters) and which entry hits first: the oldest active entry that matches, with its term, kind and category. It reads the list straight from the database, so it reflects a save made a moment ago. The text is neither stored nor logged. Use it to try a new pattern against real names before you rely on it.
Audit log
Every change is in the System-scope audit log:
| Event | When | Metadata |
|---|---|---|
system:blocklist_entry_created | An entry was added | entry_id, kind, category, pattern |
system:blocklist_entry_updated | An entry was changed or switched on or off | as above, plus changed (the field names) |
system:blocklist_entry_deleted | An entry was deleted | as above |
system:blocklist_entry_imported | An import ran | kind, category, locale and the counts created, duplicate, invalid |
The pattern is recorded so the log answers "who added or removed this term?"; an import records counts only. Checking a text writes no audit row.
API
The REST and GraphQL operations are documented in REST API → Global blocklist and GraphQL → Global blocklist. How a module uses the list: Global blocklist (developer guide).