openrouter-data-privacy
Provides PII detection and redaction tools for OpenRouter API interactions to protect sensitive data.
Install
mkdir -p .claude/skills/openrouter-data-privacy && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/9367" && unzip -o skill.zip -d .claude/skills/openrouter-data-privacy && rm skill.zipInstalls to .claude/skills/openrouter-data-privacy
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Implement data privacy controls for OpenRouter API usage. Use when handlingKey capabilities
- →Detect PII types like email, phone, SSN, credit card, API keys, and IP addresses
- →Redact PII from text and replace it with generic placeholders
- →Anonymize PII with unique placeholders for later deanonymization
- →Route workloads to specific OpenRouter providers based on sensitivity
- →Hash logged prompts using SHA-256 for GDPR compliance
How it works
The skill scans input text against predefined regex patterns for PII, then redacts identified sensitive information or replaces it with unique placeholders.
Inputs & outputs
When to use openrouter-data-privacy
- →Redact PII from prompts before API calls
- →Implement GDPR-compliant data handling for LLM workloads
- →Classify sensitivity of API request workloads
About this skill
OpenRouter Data Privacy
Overview
When sending data through OpenRouter to upstream LLM providers, you're responsible for ensuring prompts don't leak PII inappropriately. OpenRouter itself does not train on API data, but each upstream provider has its own data retention and training policies. This skill covers PII detection and redaction, placeholder substitution, provider selection for privacy, and consent tracking.
Prerequisites
- An OpenRouter API key (
sk-or-v1-...) exported asOPENROUTER_API_KEY— see theopenrouter-install-authskill for setup - Python 3.8+ with the OpenAI SDK (
pip install openai) — every pattern in this skill is Python - A sensitivity classification for your workloads (
public/standard/sensitive) soprivacy_aware_completion()can route each one - A list of providers your org approves for sensitive data, to plug into
provider.orderwithallow_fallbacks: False
Instructions
- Start with PII Detection and Redaction: adapt
PII_RULES(email, phone, SSN, credit card,sk-or-v1-API keys, IPs) to your data, then runscan_and_redact()on representative inputs and review thefindingsfor false positives. - When downstream code needs the original values back, use the Placeholder Substitution Pattern instead of plain redaction —
PrivacyProxy.anonymize()before the API call,deanonymize()on the model's reply. - Classify each workload and route it via Provider Selection for Privacy:
privacy_aware_completion()maps sensitivity to a model plus aproviderblock (order: ["Anthropic"],allow_fallbacks: Falsefor standard/sensitive). - Wire the Privacy Middleware into every call path, choosing
block_on_pii=True(raise on detection) orauto_redact=True(scrub and continue) per workload. - Apply the Enterprise Considerations: hash logged prompts (SHA-256) for GDPR right-to-erasure, and use BYOK for the most sensitive workloads.
PII Detection and Redaction
import re
from dataclasses import dataclass
from typing import Optional
@dataclass
class PiiScanResult:
clean_text: str
findings: list[dict]
has_pii: bool
PII_RULES = [
("email", r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'),
("phone", r'\b(?:\+1[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b'),
("ssn", r'\b\d{3}-\d{2}-\d{4}\b'),
("credit_card", r'\b(?:\d{4}[- ]?){3}\d{4}\b'),
("api_key", r'\bsk-or-v1-[a-zA-Z0-9]+\b'),
("ip_address", r'\b(?:\d{1,3}\.){3}\d{1,3}\b'),
]
REPLACEMENTS = {
"email": "[EMAIL]", "phone": "[PHONE]", "ssn": "[SSN]",
"credit_card": "[CARD]", "api_key": "[API_KEY]", "ip_address": "[IP]",
}
def scan_and_redact(text: str) -> PiiScanResult:
"""Scan text for PII and return redacted version with findings."""
findings = []
clean = text
for pii_type, pattern in PII_RULES:
matches = re.findall(pattern, clean)
for match in matches:
findings.append({"type": pii_type, "value_prefix": match[:4] + "..."})
clean = re.sub(pattern, REPLACEMENTS[pii_type], clean)
return PiiScanResult(clean_text=clean, findings=findings, has_pii=len(findings) > 0)
Placeholder Substitution Pattern
import os, uuid
from openai import OpenAI
client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key=os.environ["OPENROUTER_API_KEY"],
default_headers={"HTTP-Referer": "https://my-app.com", "X-Title": "my-app"},
)
class PrivacyProxy:
"""Replace PII with placeholders before API, restore after."""
def __init__(self):
self._map: dict[str, str] = {}
def anonymize(self, text: str) -> str:
"""Replace PII with unique placeholders."""
result = scan_and_redact(text)
if not result.has_pii:
return text
# Use deterministic placeholders for consistent replacement
anonymized = text
for pii_type, pattern in PII_RULES:
for match in re.finditer(pattern, anonymized):
original = match.group()
if original not in self._map:
placeholder = f"[{pii_type.upper()}_{len(self._map)}]"
self._map[placeholder] = original
else:
placeholder = next(k for k, v in self._map.items() if v == original)
anonymized = anonymized.replace(original, placeholder, 1)
return anonymized
def deanonymize(self, text: str) -> str:
"""Restore original values from placeholders."""
result = text
for placeholder, original in self._map.items():
result = result.replace(placeholder, original)
return result
# Usage
proxy = PrivacyProxy()
user_input = "Contact [email protected] or call 555-123-4567"
safe_input = proxy.anonymize(user_input)
# safe_input = "Contact [EMAIL_0] or call [PHONE_1]"
response = client.chat.completions.create(
model="anthropic/claude-3.5-sonnet",
messages=[{"role": "user", "content": safe_input}],
max_tokens=200,
)
# Restore PII in the response if model referenced it
result = proxy.deanonymize(response.choices[0].message.content)
Provider Selection for Privacy
# Force specific provider to control data handling
def privacy_aware_completion(messages, sensitivity="standard"):
"""Route to appropriate provider based on data sensitivity."""
PRIVACY_CONFIG = {
"public": {
"model": "openai/gpt-4o-mini",
"provider": None, # Any provider OK
},
"standard": {
"model": "anthropic/claude-3.5-sonnet",
"provider": {"order": ["Anthropic"], "allow_fallbacks": False},
},
"sensitive": {
"model": "anthropic/claude-3.5-sonnet",
"provider": {"order": ["Anthropic"], "allow_fallbacks": False},
# Add PII redaction as mandatory pre-processing
},
}
config = PRIVACY_CONFIG.get(sensitivity, PRIVACY_CONFIG["standard"])
extra = {}
if config["provider"]:
extra["extra_body"] = {"provider": config["provider"]}
return client.chat.completions.create(
model=config["model"],
messages=messages,
max_tokens=1024,
**extra,
)
Privacy Middleware
class PrivacyMiddleware:
"""Enforce privacy policies before every API call."""
def __init__(self, block_on_pii: bool = False, auto_redact: bool = True):
self.block_on_pii = block_on_pii
self.auto_redact = auto_redact
def process(self, messages: list[dict]) -> list[dict]:
"""Scan and optionally redact PII from all messages."""
processed = []
for msg in messages:
content = msg.get("content", "")
if isinstance(content, str):
result = scan_and_redact(content)
if result.has_pii:
if self.block_on_pii:
raise ValueError(f"PII detected: {[f['type'] for f in result.findings]}")
if self.auto_redact:
msg = {**msg, "content": result.clean_text}
processed.append(msg)
return processed
Output
The privacy flows in this skill produce:
- A
PiiScanResultper scan:clean_textwith placeholders substituted,findings(PII type + first-4-chars value prefix per match), and ahas_piiflag - Anonymized prompts like
"Contact [EMAIL_0] or call [PHONE_1]"plus the placeholder→original map thatdeanonymize()uses to restore values in the response - Chat completions served only by approved providers when the
provider.order+allow_fallbacks: Falseconfig is applied - A
ValueErrorlisting the detected PII types whenPrivacyMiddlewareruns withblock_on_pii=True
Examples
Scanning a support message before it leaves your infrastructure:
result = scan_and_redact("Contact [email protected] or call 555-123-4567")
print(result.clean_text) # Contact [EMAIL] or call [PHONE]
print(result.has_pii) # True
print(result.findings) # [{'type': 'email', 'value_prefix': 'john...'}, {'type': 'phone', ...}]
To keep the values recoverable, run the same input through PrivacyProxy.anonymize() instead, send the placeholder version to the model, then deanonymize() the reply. More worked examples: references/examples.md.
Error Handling
| Error | Cause | Fix |
|---|---|---|
| PII detected in prompt | User input contains sensitive data | Auto-redact or block and prompt user to remove |
| Provider retained data | Using provider with training-on-API-data | Switch to Anthropic or use BYOK |
| Placeholder in response | Model used placeholder literally | Map it back with deanonymize() |
| False positive PII match | Regex too aggressive | Tune patterns; use NLP-based PII detection for accuracy |
Enterprise Considerations
- OpenRouter does not train on API data; check each upstream provider's data use policy separately
- Use
provider.order+allow_fallbacks: falseto ensure data only flows to approved providers - Implement PII redaction as middleware that runs on every request, not optional per-call
- For GDPR right-to-erasure: don't log raw prompts -- hash them (SHA-256)
- Use BYOK for sensitive workloads so data flows directly to the provider under your account
- Build a data classification system that auto-routes based on sensitivity level
References
- Examples | Errors
- Privacy Policy | Provider Routing
When not to use it
- →When the downstream code needs original PII values and placeholder substitution is not used
- →When an OpenRouter provider's data use policy is not checked
- →When the task requires PII detection beyond the defined `PII_RULES`
Prerequisites
Limitations
- →The skill's PII detection relies on predefined regex patterns, which may lead to false positives or negatives.
- →The skill does not train on API data, but upstream providers may have their own data retention policies.
- →The skill does not automatically validate the data use policies of upstream providers.
How it compares
This skill automates PII detection and redaction before sending data to OpenRouter, unlike manual review or direct API submission.
Compared to similar skills
openrouter-data-privacy side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| openrouter-data-privacy (this skill) | 0 | 27d | Review | Intermediate |
| desktop-mac-mcp-host | 0 | 3mo | Review | Intermediate |
| prompt-guard | 1 | 6mo | Review | Intermediate |
| sandboxing-security | 0 | 5mo | Review | Advanced |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by jeremylongshore
View all by jeremylongshore →You might also like
desktop-mac-mcp-host
hushh-labs
Use when implementing or reviewing the local MCP HTTP/SSE host on the Mac, the OpenClaw DataSourceBinding interface, CRT/DAT minting, or the OpenClaw conformance test harness inside the desktop-mac owner family.
prompt-guard
Orchestra-Research
Meta's 86M prompt injection and jailbreak detector. Filters malicious prompts and third-party data for LLM apps. 99%+ TPR, <1% FPR. Fast (<2ms GPU). Multilingual (8 languages). Deploy with HuggingFace or batch processing for RAG security.
sandboxing-security
gitwalter
Input validation and sanitization, output filtering, code execution
mcp-builder
anthropics
Guide for creating high-quality MCP (Model Context Protocol) servers that enable LLMs to interact with external services through well-designed tools. Use when building MCP servers to integrate external APIs or services, whether in Python (FastMCP) or Node/TypeScript (MCP SDK).
copilot-sdk
github
Build agentic applications with GitHub Copilot SDK. Use when embedding AI agents in apps, creating custom tools, implementing streaming responses, managing sessions, connecting to MCP servers, or creating custom agents. Triggers on Copilot SDK, GitHub SDK, agentic app, embed Copilot, programmable agent, MCP server, custom agent.
openrouter-function-calling
jeremylongshore
Implement function/tool calling with OpenRouter models. Use when building agents or structured outputs. Trigger with phrases like 'openrouter functions', 'openrouter tools', 'openrouter agent', 'function calling'.