Content categories
Content Categories give Arbitex policy rules semantic context about the subject matter of an AI conversation. Where DLP rules detect what kind of data is present (PII, credentials, financial account numbers), Content Categories detect what topic or domain the conversation is about — legal strategy, financial analysis, security research, and more.
Combining DLP findings with category context enables richer enforcement:
IF content_categories includes "financial.investment" AND dlp.finding = "material-nonpublic"THEN action = block, reason = "Potential insider trading risk"
IF content_categories includes "security.offensive" AND user.group ≠ "red-team"THEN action = block, reason = "Offensive security content restricted"How classification works
Section titled “How classification works”Content Categories v1 uses a keyword-based classification engine (L1 classifier). Classification is deterministic, CPU-only, and produces binary match results — no GPU infrastructure is required.
Classification pipeline
Section titled “Classification pipeline”Each incoming conversation turn goes through the following steps:
- Empty check — If the turn text is empty or whitespace-only, classification returns an empty list immediately.
- Category loading — The engine loads all
enabled=truecategories from the database. If anorg_idis provided, org-scoped override rows are merged with global builtins (org overrides shadow global rows for the same slug). - Keyword scan — The engine lowercases the input text once, then iterates through each category’s keyword list. For each keyword, it checks whether
keyword.lower()appears as a substring of the lowercased text. - Binary match — On the first keyword match for a category, that category’s slug is added to the matched set. Only one keyword match per category is needed — the engine breaks after the first hit and moves to the next category.
- Parent propagation — When a subcategory matches (e.g.,
legal.contracts), its parent domain slug (e.g.,legal) is automatically added to the matched set. This enables domain-level policy conditions without requiring separate keyword matches on the parent. - Result — The engine returns a deduplicated, lexicographically sorted list of matched category slugs.
Classification characteristics
Section titled “Classification characteristics”| Characteristic | Value |
|---|---|
| Matching type | Case-insensitive substring |
| Scoring | Binary (match / no match) — no confidence scores |
| Multi-category | A single turn can match multiple categories |
| Parent propagation | Automatic — matching legal.contracts also yields legal |
| Determinism | Identical input + keywords = identical output |
| Latency | Single-digit to low double-digit milliseconds |
| Infrastructure | CPU-only, in-process with the DLP pipeline |
Graceful degradation
Section titled “Graceful degradation”Classification runs as a non-critical enrichment step in the intake pipeline (Stage 3, before policy evaluation). If classification fails for any reason, it degrades gracefully to an empty list — policy rules that depend on content categories simply do not match, and the request proceeds without category context.
Enabling content categories
Section titled “Enabling content categories”Content Categories are seeded automatically on platform startup when CONTENT_CATEGORY_AUTO_SEED=true (the default). The auto-seeder is idempotent — it checks existence by (slug, org_id IS NULL) before inserting and never overwrites existing keyword lists. Admin keyword edits survive server restarts.
No additional feature flag or configuration is required to activate content category classification. Once the builtin taxonomy is seeded, the classifier runs automatically on all incoming conversation turns.
Category taxonomy
Section titled “Category taxonomy”Categories follow a two-level domain.subcategory hierarchy using dot-notation slugs. The built-in taxonomy spans 8 domains and 26 subcategories (34 total nodes including domain parents).
Slug format
Section titled “Slug format”Slugs must match ^[a-z][a-z0-9_.]*$:
- Start with a lowercase letter
- May contain lowercase letters, digits, underscores, and dots
- Dots encode the hierarchy:
legalis the parent domain,legal.contractsis a subcategory - Maximum 100 characters
- Slugs are stable policy identifiers — builtin slugs cannot be changed after creation
Domain summary
Section titled “Domain summary”| Domain | Subcategories | Description |
|---|---|---|
legal |
4 | Contracts, litigation, regulatory compliance, intellectual property |
financial |
4 | Investment advice, trading, earnings, cryptocurrency |
security |
3 | Offensive security, defensive security, social engineering |
medical |
4 | Diagnosis, prescriptions, clinical data, mental health |
political |
3 | Elections, government policy, political figures |
adult |
2 | Explicit sexual content, graphic violence |
competitive |
3 | Competitor names, competitor pricing, competitive strategy |
safety |
3 | Jailbreak attempts, prompt extraction, off-topic content |
Full taxonomy reference
Section titled “Full taxonomy reference”legal — Legal & Compliance
Section titled “legal — Legal & Compliance”Domain keywords: legal, attorney, counsel, court, statute, regulation
| Slug | Name | Keywords |
|---|---|---|
legal.contracts |
Contract Drafting | NDA, non-disclosure agreement, SOW, statement of work, master service agreement, MSA, indemnification, breach of contract, terms and conditions, service level agreement, SLA |
legal.litigation |
Litigation | lawsuit, deposition, discovery request, court filing, subpoena, expert witness, plaintiff, defendant, motion to dismiss, summary judgment |
legal.regulatory |
Regulatory Compliance | GDPR, HIPAA, SEC filing, compliance, regulatory, data protection, privacy policy, consent form, audit finding, remediation plan |
legal.ip |
Intellectual Property | patent, trademark, copyright, trade secret, prior art, infringement, licensing agreement, royalty, intellectual property |
financial — Financial
Section titled “financial — Financial”Domain keywords: financial, investment, trading, earnings, revenue, profit
| Slug | Name | Keywords |
|---|---|---|
financial.investment |
Investment Advice | stock pick, investment advice, portfolio, buy recommendation, sell recommendation, asset allocation, risk tolerance, diversification, mutual fund, ETF |
financial.trading |
Trading | trade order, derivatives, options strategy, put option, call option, futures contract, short selling, margin trading, stop loss, limit order |
financial.earnings |
Earnings | earnings report, revenue guidance, forecast, quarterly results, annual report, EPS, earnings per share, guidance, outlook, EBITDA |
financial.crypto |
Cryptocurrency | cryptocurrency, bitcoin, ethereum, blockchain, DeFi, decentralized finance, token, NFT, crypto wallet, mining |
security — Security & Hacking
Section titled “security — Security & Hacking”Domain keywords: security, hacking, exploit, vulnerability, CVE
| Slug | Name | Keywords |
|---|---|---|
security.offensive |
Offensive Security | exploit, malware, shellcode, CVE weaponization, payload, reverse shell, privilege escalation, buffer overflow, zero day, remote code execution |
security.defensive |
Defensive Security | hardening, forensics, SIEM, penetration test, vulnerability scan, incident response, threat hunting, security audit, firewall rule, intrusion detection |
security.social_eng |
Social Engineering | phishing, pretexting, social engineering, spear phishing, vishing, smishing, impersonation, credential harvesting, baiting |
medical — Medical & Health
Section titled “medical — Medical & Health”Domain keywords: medical, health, clinical, patient, diagnosis, treatment
| Slug | Name | Keywords |
|---|---|---|
medical.diagnosis |
Medical Diagnosis | diagnosis, differential diagnosis, symptoms, prognosis, medical condition, disease, pathology, clinical finding, diagnostic criteria |
medical.prescription |
Prescriptions | prescription, dosage, medication, drug interaction, pharmacy, controlled substance, side effect, contraindication, formulary |
medical.clinical |
Clinical Data | clinical trial, patient data, EHR, electronic health record, informed consent, adverse event, clinical study, protocol, IRB |
medical.mental_health |
Mental Health | therapy, counseling, mental health, depression, anxiety, self-harm, suicide, psychiatric, psychotherapy, behavioral health |
political — Political
Section titled “political — Political”Domain keywords: political, election, government, legislation, policy
| Slug | Name | Keywords |
|---|---|---|
political.elections |
Elections | election, candidate, voting, ballot, poll, election integrity, campaign, primary, electoral |
political.policy |
Government Policy | legislation, government policy, executive order, regulation, congressional, senate, bill, amendment, federal policy |
political.figures |
Political Figures | president, senator, congressman, prime minister, governor, political leader, head of state, cabinet member |
adult — Adult / Explicit
Section titled “adult — Adult / Explicit”Domain keywords: explicit, NSFW, adult content
| Slug | Name | Keywords |
|---|---|---|
adult.explicit |
Explicit Sexual Content | explicit sexual, pornography, sexually explicit, adult entertainment, erotic |
adult.violence |
Graphic Violence | graphic violence, gore, torture, mutilation, brutal, gruesome |
competitive — Competitive Intelligence
Section titled “competitive — Competitive Intelligence”Domain keywords: competitor, competitive, market intelligence, competitive analysis
| Slug | Name | Keywords |
|---|---|---|
competitive.names |
Competitor Names | competitor name, competitor product, rival company, competing product, alternative vendor |
competitive.pricing |
Competitor Pricing | competitor pricing, price comparison, competitor discount, pricing intelligence, price undercutting |
competitive.strategy |
Competitive Strategy | merger, acquisition, battlecard, win loss analysis, competitive strategy, market share, SWOT |
safety — Prompt Safety
Section titled “safety — Prompt Safety”Domain keywords: jailbreak, prompt injection, system prompt
| Slug | Name | Keywords |
|---|---|---|
safety.jailbreak |
Jailbreak Attempts | ignore previous instructions, DAN, jailbreak, do anything now, bypass restrictions, override safety, ignore all rules, pretend you are |
safety.prompt_leak |
Prompt Extraction | system prompt, what are your instructions, show me your prompt, reveal your instructions, initial instructions, instruction extraction |
safety.off_topic |
Off-Topic Content | off topic, unrelated, not relevant, outside scope |
Global vs. org-scoped categories
Section titled “Global vs. org-scoped categories”Content categories support a two-tier scoping model:
Global (builtin) categories
Section titled “Global (builtin) categories”- Seeded automatically at startup (
org_id IS NULL) - Slug and parent_slug are immutable
- Only
keywordsandenabledare writable via the API - Deleting a global category soft-deletes it (sets
enabled=false) — it remains in the taxonomy but is excluded from classification - 34 nodes ship by default (8 domains + 26 subcategories)
Org-scoped overrides
Section titled “Org-scoped overrides”- Created with an
org_idvalue to scope the category to a specific organization - Shadows the global row for the same slug within that org
- All fields are writable (
name,description,keywords,enabled) - Deleting an org-scoped category hard-deletes it (the row is removed, and the global builtin resumes for that slug)
- Org-scoped keyword lists completely replace (not augment) the global keyword list for that slug
Creating a custom subcategory
Section titled “Creating a custom subcategory”Organizations can create entirely new subcategories under any existing domain:
curl -X POST https://api.arbitex.example.com/api/v1/admin/content-categories \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "slug": "legal.sec_filings", "parent_slug": "legal", "name": "SEC Filings", "description": "10-K, 10-Q, 8-K filings and related SEC disclosure content", "keywords": ["10-K", "10-Q", "8-K", "proxy statement", "SEC disclosure", "annual report"], "org_id": "your-org-uuid" }'Policy engine integration
Section titled “Policy engine integration”When content categories are active, the classifier populates a content_categories list on the policy evaluation context. Policy rules can include a content_categories condition to match against this list.
Condition format
Section titled “Condition format”The content_categories condition is an array of slugs. The rule matches if any slug in the condition list matches any category in the context (OR logic across the condition list).
Matching behavior
Section titled “Matching behavior”The policy engine supports both exact match and prefix match:
"legal.contracts"— matches onlylegal.contractsin the context"legal"— matcheslegal,legal.contracts,legal.litigation,legal.regulatory,legal.ip(any slug that equals"legal"or starts with"legal.")
This means you can write domain-level rules without listing every subcategory:
{ "name": "Block all medical content for general staff", "conditions": { "content_categories": ["medical"], "user_groups_exclude": ["clinical-staff"] }, "action": "block", "reason": "Medical AI restricted to clinical staff"}Example: subcategory-level rule
Section titled “Example: subcategory-level rule”{ "name": "Flag offensive security content", "conditions": { "content_categories": ["security.offensive"] }, "action": "flag", "severity": "high", "reason": "Potential offensive security use"}Example: multi-category OR condition
Section titled “Example: multi-category OR condition”{ "name": "High-sensitivity content monitoring", "conditions": { "content_categories": ["financial.trading", "security.offensive", "adult"] }, "action": "flag", "severity": "medium", "reason": "High-sensitivity content detected"}This rule triggers if the conversation matches any of the three listed categories (OR logic).
Combining with DLP conditions
Section titled “Combining with DLP conditions”Category conditions combine with other policy conditions using standard AND logic:
{ "name": "Investment research + MNPI — critical block", "conditions": { "content_categories": ["financial.investment"], "dlp_findings": ["material-nonpublic-information"] }, "action": "block", "alert": { "severity": "critical", "channels": ["compliance-team", "legal-team"] }, "reason": "Potential MNPI in investment research context"}Policy simulation
Section titled “Policy simulation”The policy simulate endpoint accepts a content_categories override — when provided, these slugs bypass the classifier and are used directly for simulation. This allows testing policy rules without requiring actual conversation content:
curl -X POST https://api.arbitex.example.com/api/v1/admin/policies/simulate \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "prompt": "Show me the Q3 earnings forecast", "content_categories": ["financial.earnings"], "user_id": "test-user-uuid" }'Use cases
Section titled “Use cases”Use case 1: Role-based medical content restriction
Section titled “Use case 1: Role-based medical content restriction”Scenario: A healthcare company deploys Arbitex as a productivity assistant. Clinical staff are allowed to use the medical AI persona; general employees are not.
{ "name": "Medical content — general employees blocked", "conditions": { "content_categories": ["medical"], "user_groups_exclude": ["clinical-staff"] }, "action": "block", "reason": "Medical AI restricted to clinical staff"}Keyword tuning tip: The medical domain keyword alone matches broadly. If you need tighter control, write rules against specific subcategories like medical.diagnosis or medical.prescription instead of the domain parent.
Use case 2: Competitive intelligence restriction
Section titled “Use case 2: Competitive intelligence restriction”Scenario: An enterprise wants to prevent employees from using AI to analyze competitor strategies unless they are in the competitive intelligence group.
{ "name": "Competitive intel — restrict to CI team", "conditions": { "content_categories": ["competitive"], "user_groups_exclude": ["competitive-intel"] }, "action": "block", "reason": "Competitive intelligence AI use restricted to CI team members"}Use case 3: Prompt safety detection
Section titled “Use case 3: Prompt safety detection”Scenario: A security team wants to detect and flag jailbreak attempts and prompt extraction across all users.
{ "name": "Prompt safety — flag jailbreak and extraction", "conditions": { "content_categories": ["safety.jailbreak", "safety.prompt_leak"] }, "action": "flag", "severity": "high", "alert": { "channels": ["security-ops"] }, "reason": "Potential prompt manipulation detected"}Use case 4: Financial content with DLP combination
Section titled “Use case 4: Financial content with DLP combination”Scenario: A financial services firm wants to block AI use for investment research when material non-public information is detected.
{ "name": "Investment + MNPI — block and alert", "conditions": { "content_categories": ["financial.investment"], "dlp_findings": ["material-nonpublic-information"] }, "action": "block", "alert": { "severity": "critical", "channels": ["compliance-team", "legal-team"] }, "reason": "Potential MNPI in investment research context"}Use case 5: Adult content enforcement
Section titled “Use case 5: Adult content enforcement”Scenario: An organization wants to block all adult/explicit content across all users with no exceptions.
{ "name": "Adult content — universal block", "conditions": { "content_categories": ["adult"] }, "action": "block", "reason": "Adult content is prohibited"}API reference
Section titled “API reference”The content categories admin API allows programmatic management of your category configuration. All endpoints require an admin-scoped API token.
For full request/response schemas, see API reference — batch 43.
Endpoints
Section titled “Endpoints”| Method | Path | Description |
|---|---|---|
GET |
/api/v1/admin/content-categories |
List all categories with optional filters |
POST |
/api/v1/admin/content-categories |
Create a custom category |
GET |
/api/v1/admin/content-categories/{slug} |
Fetch a single category by slug |
PUT |
/api/v1/admin/content-categories/{slug} |
Update a category (keywords, enabled, name for custom) |
DELETE |
/api/v1/admin/content-categories/{slug} |
Soft-delete builtin or hard-delete custom category |
GET |
/api/v1/admin/content-categories/stats |
Category keyword counts and enabled status |
List all categories
Section titled “List all categories”curl https://api.arbitex.example.com/api/v1/admin/content-categories \ -H "Authorization: Bearer $ADMIN_TOKEN"Returns all 34 builtin nodes plus any org-defined custom categories. Supports filtering by enabled, parent slug, and org_id. Paginated with limit (default 100, max 1000) and offset.
Create a custom category
Section titled “Create a custom category”curl -X POST https://api.arbitex.example.com/api/v1/admin/content-categories \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "slug": "competitive.battlecards", "parent_slug": "competitive", "name": "Battlecard Content", "description": "Internal competitive battlecard material", "keywords": ["battlecard", "competitive positioning", "win loss"], "org_id": "your-org-uuid" }'Returns 201 Created with the full category response. Returns 409 Conflict if a category with the same (slug, org_id) already exists.
Update category keywords
Section titled “Update category keywords”curl -X PUT https://api.arbitex.example.com/api/v1/admin/content-categories/security.offensive \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "keywords": ["exploit", "malware", "shellcode", "reverse shell", "privilege escalation", "zero day", "remote code execution", "ransomware", "rootkit"] }'Disable a builtin category
Section titled “Disable a builtin category”curl -X DELETE https://api.arbitex.example.com/api/v1/admin/content-categories/adult.violence \ -H "Authorization: Bearer $ADMIN_TOKEN"For builtin categories, DELETE performs a soft-delete (sets enabled=false). The category remains in the taxonomy but is excluded from classification. Returns 200 with the updated category body.
Get category statistics
Section titled “Get category statistics”curl https://api.arbitex.example.com/api/v1/admin/content-categories/stats \ -H "Authorization: Bearer $ADMIN_TOKEN"Returns keyword count and enabled status for all global categories, sorted by slug.
Troubleshooting
Section titled “Troubleshooting”Categories not matching expected content
Section titled “Categories not matching expected content”- Check keyword coverage — The classifier uses substring matching, so the keyword must appear literally in the conversation text. Review the keyword list for the category and compare against the actual text being classified.
- Check for substring collisions — A keyword like
"legal"matches"paralegal"and"illegal". Use more specific multi-word phrases to reduce false positives. - Verify the category is enabled — Disabled categories are excluded from classification. Check with
GET /api/v1/admin/content-categories/{slug}and confirm"enabled": true. - Check org overrides — If you have an org-scoped override for a category, it replaces the global keyword list entirely. The override may have a different (or empty) keyword list.
Policy rule not triggering on expected category
Section titled “Policy rule not triggering on expected category”- Confirm the category slug in your policy condition matches the classifier output. The classifier returns dot-notation slugs (e.g.,
security.offensive), not slash-separated paths. - Verify prefix matching behavior. A condition slug of
"security"matchessecurity,security.offensive,security.defensive, andsecurity.social_eng. A condition slug of"security.offensive"matches onlysecurity.offensive. - Test with policy simulation. Use the simulate endpoint with an explicit
content_categorieslist to verify your rule conditions fire correctly, independent of the classifier.
Custom category not matching
Section titled “Custom category not matching”Custom categories depend entirely on keywords for classification:
- Verify keywords are registered — A custom category with an empty keyword list will never match. Check with
GET /api/v1/admin/content-categories/{slug}. - Review keyword specificity — Short, common keywords will match broadly. Long, multi-word phrases will match narrowly. Aim for 5–15 specific terms per category.
- Verify the parent slug exists — A custom subcategory must reference an existing global domain as its
parent_slug. If the parent does not exist, creation fails with404.
Negative keyword lists
Section titled “Negative keyword lists”The political.elections and political.figures subcategories use negative keyword lists to reduce false positives. For example, “job candidate” and “marketing campaign” do not trigger political.elections, and “company president” does not trigger political.figures.
Understanding binary vs. weighted classification
Section titled “Understanding binary vs. weighted classification”Content Categories v1 uses binary matching — a keyword either appears as a substring or it does not. There are no keyword weights, confidence scores, or score normalization. A single keyword match is sufficient to trigger a category.
This differs from the planned L2 DeBERTa classifier, which will provide confidence scores and threshold-based classification. For v1, tuning is done by choosing keywords carefully:
- Increase precision — Use longer, more specific multi-word phrases
- Increase recall — Add more keyword synonyms and variations
- Reduce false positives — Remove short or ambiguous single-word keywords
Related documentation
Section titled “Related documentation”- Policy Engine — Deep Dive
- DLP Event Monitoring
- DLP Pipeline Configuration
- API reference — batch 43
- Platform Admin API
Viewing category matches in DLP events
Section titled “Viewing category matches in DLP events”When a policy rule fires on a content category match, the category information is recorded in the audit log. This allows compliance teams to monitor which categories are triggering policy actions.
How category matches appear in audit events
Section titled “How category matches appear in audit events”Content categories are recorded in the match_reason field of the audit event’s extra_metadata. When a category-based rule fires:
{ "action": "pipeline_evaluation_complete", "extra_metadata": { "action_taken": "BLOCK", "matched_rule_id": "rule-uuid-here", "match_reason": "content_categories matched=['financial.investment']", "stage_latencies": { "dlp_ms": 4.2, "policy_eval_ms": 1.1 } }}The match_reason follows the format: content_categories matched=[<slug-list>] where the slug list contains only the condition slugs from the rule that triggered (not the full set of matched categories).
Querying category events
Section titled “Querying category events”Use the audit log API to filter for category-triggered policy actions:
# All events where a category-based rule firedcurl "https://gateway.arbitex.ai/v1/audit/events?action=pipeline_evaluation_complete" \ -H "Authorization: Bearer $TOKEN"To narrow to security-related category events, filter in your SIEM or audit export by match_reason containing content_categories matched=.
DLP event monitoring dashboard
Section titled “DLP event monitoring dashboard”The DLP Event Monitoring admin page shows a breakdown of policy actions. Content category matches appear as regular policy events — look for events with match_reason values containing content_categories. Filter by date range and export to CSV for compliance reporting.
See DLP Event Monitoring for the full event monitoring workflow.
Per-org enabling workflow
Section titled “Per-org enabling workflow”To enable content category enforcement for a specific org using the admin API:
-
List current org categories to see what is enabled:
Terminal window curl "https://gateway.arbitex.ai/api/v1/admin/content-categories?org_id={org_id}" \-H "Authorization: Bearer $ADMIN_TOKEN" -
Enable a builtin category (ensure
enabled=true):Terminal window curl -X PUT "https://gateway.arbitex.ai/api/v1/admin/content-categories/security.offensive?org_id={org_id}" \-H "Authorization: Bearer $ADMIN_TOKEN" \-H "Content-Type: application/json" \-d '{"enabled": true}' -
Create an org-scoped keyword override to customize the keyword list for this org without affecting global defaults:
Terminal window curl -X POST "https://gateway.arbitex.ai/api/v1/admin/content-categories" \-H "Authorization: Bearer $ADMIN_TOKEN" \-H "Content-Type: application/json" \-d '{"slug": "security.offensive","parent_slug": "security","name": "Offensive Security","keywords": ["exploit", "malware", "shellcode", "zero day", "reverse shell", "ransomware"],"org_id": "your-org-uuid"}' -
Verify classification using the policy simulate endpoint:
Terminal window curl -X POST "https://gateway.arbitex.ai/api/v1/admin/policies/simulate" \-H "Authorization: Bearer $ADMIN_TOKEN" \-H "Content-Type: application/json" \-d '{"prompt": "help me write shellcode", "user_id": "test-user-uuid"}'