Skip to content

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"

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.

Each incoming conversation turn goes through the following steps:

  1. Empty check — If the turn text is empty or whitespace-only, classification returns an empty list immediately.
  2. Category loading — The engine loads all enabled=true categories from the database. If an org_id is provided, org-scoped override rows are merged with global builtins (org overrides shadow global rows for the same slug).
  3. 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.
  4. 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.
  5. 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.
  6. Result — The engine returns a deduplicated, lexicographically sorted list of matched category slugs.
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

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.


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.


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).

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: legal is the parent domain, legal.contracts is a subcategory
  • Maximum 100 characters
  • Slugs are stable policy identifiers — builtin slugs cannot be changed after creation
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

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

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

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

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

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

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

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

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

Content categories support a two-tier scoping model:

  • Seeded automatically at startup (org_id IS NULL)
  • Slug and parent_slug are immutable
  • Only keywords and enabled are 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)
  • Created with an org_id value 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

Organizations can create entirely new subcategories under any existing domain:

Terminal window
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"
}'

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.

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).

The policy engine supports both exact match and prefix match:

  • "legal.contracts" — matches only legal.contracts in the context
  • "legal" — matches legal, 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"
}
{
"name": "Flag offensive security content",
"conditions": {
"content_categories": ["security.offensive"]
},
"action": "flag",
"severity": "high",
"reason": "Potential offensive security use"
}
{
"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).

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"
}

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:

Terminal window
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 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"
}

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"
}

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"
}

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.

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
Terminal window
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.

Terminal window
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.

Terminal window
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"]
}'
Terminal window
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.

Terminal window
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.


  1. 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.
  2. Check for substring collisions — A keyword like "legal" matches "paralegal" and "illegal". Use more specific multi-word phrases to reduce false positives.
  3. Verify the category is enabled — Disabled categories are excluded from classification. Check with GET /api/v1/admin/content-categories/{slug} and confirm "enabled": true.
  4. 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”
  1. 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.
  2. Verify prefix matching behavior. A condition slug of "security" matches security, security.offensive, security.defensive, and security.social_eng. A condition slug of "security.offensive" matches only security.offensive.
  3. Test with policy simulation. Use the simulate endpoint with an explicit content_categories list to verify your rule conditions fire correctly, independent of the classifier.

Custom categories depend entirely on keywords for classification:

  1. Verify keywords are registered — A custom category with an empty keyword list will never match. Check with GET /api/v1/admin/content-categories/{slug}.
  2. Review keyword specificity — Short, common keywords will match broadly. Long, multi-word phrases will match narrowly. Aim for 5–15 specific terms per category.
  3. 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 with 404.

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


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).

Use the audit log API to filter for category-triggered policy actions:

Terminal window
# All events where a category-based rule fired
curl "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=.

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.

To enable content category enforcement for a specific org using the admin API:

  1. 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"
  2. 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}'
  3. 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"
    }'
  4. 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"}'