OpenRouter Compliance Review
Overview
OpenRouter is a proxy that routes requests to upstream providers (OpenAI, Anthropic, Google, etc.). Compliance depends on both OpenRouter's data handling and the selected provider's policies. Key considerations: data transit through OpenRouter infrastructure, provider-specific data retention, model selection for regulated data, and audit trail requirements.
Prerequisites
- An OpenRouter API key (
sk-or-v1-...) exported as OPENROUTER_API_KEY — see the openrouter-install-auth skill for setup
- Python 3.8+ with the OpenAI SDK for provider-pinned requests and the automated checker in the references
curl and jq to run the Compliance Audit Script
- An existing OpenRouter integration to review — the audit script scans its source tree for hardcoded
sk-or-v1- keys
- Knowledge of which regimes apply (SOC2, GDPR, HIPAA) and how your data is classified
Instructions
- Work through the four areas of the Compliance Checklist —
data_handling, access_control, audit_trail, and provider_selection — recording pass/fail per item.
- Classify each workload with the Data Classification Matrix (Public → Internal → Confidential → Restricted/PHI) to determine allowed providers and required controls.
- Pin regulated traffic per Provider Routing for Compliance: set
provider.order plus allow_fallbacks: False, then verify response.model confirms the approved provider actually served the request.
- For data-sovereignty requirements, configure BYOK per BYOK for Data Sovereignty so inference runs on your own provider account and OpenRouter only routes.
- Run the Compliance Audit Script: key label/limit check via
GET /api/v1/auth/key, a free-tier warning (free tier is unsuitable for regulated data), and the hardcoded-key scan.
- Document the data flow for auditors — client → OpenRouter (routing) → provider (inference) — per Enterprise Considerations.
Compliance Checklist
COMPLIANCE_CHECKLIST = {
"data_handling": [
"Verify OpenRouter does NOT train on your data (confirmed in their privacy policy)",
"Confirm provider-level data policies (OpenAI, Anthropic, Google each differ)",
"Document data flow: your app -> OpenRouter -> provider -> OpenRouter -> your app",
"Identify if prompts contain PII, PHI, or regulated data",
"Implement PII redaction before sending to API",
],
"access_control": [
"Use per-service API keys (not shared keys)",
"Set credit limits per key to isolate blast radius",
"Rotate keys on a 90-day schedule",
"Store keys in secrets manager (not .env files in repos)",
"Enable management keys for programmatic key provisioning",
],
"audit_trail": [
"Log every API call with generation_id, model, user_id, cost",
"Hash prompts (SHA-256) instead of logging raw content",
"Retain audit logs per regulation (90d operational, 7yr financial)",
"Ship logs to append-only storage (S3, immutable DB)",
],
"provider_selection": [
"Route regulated data only to compliant providers",
"Use provider routing to exclude non-compliant providers",
"Document which models are approved for which data classifications",
"Test that fallback routing doesn't route to unapproved providers",
],
}
Provider Routing for Compliance
import os
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"},
)
# Route ONLY to specific providers (e.g., Anthropic for SOC2)
response = client.chat.completions.create(
model="anthropic/claude-3.5-sonnet",
messages=[{"role": "user", "content": "Analyze this contract..."}],
max_tokens=2048,
extra_body={
"provider": {
"order": ["Anthropic"], # Only Anthropic's infrastructure
"allow_fallbacks": False, # Do NOT fall back to other providers
},
},
)
# Verify which provider actually served the request
print(f"Served by: {response.model}") # Should match anthropic/claude-3.5-sonnet
Data Classification Matrix
| Classification |
Allowed Providers |
Controls |
| Public |
Any (including :free) |
Standard logging |
| Internal |
Tier 1 (OpenAI, Anthropic, Google) |
Audit logging, key limits |
| Confidential |
Anthropic, OpenAI (API-only) |
PII redaction, no free models |
| Restricted/PHI |
BYOK only or self-hosted |
Full audit, encryption at rest |
BYOK for Data Sovereignty
# Bring Your Own Key -- requests go directly to provider
# OpenRouter acts as router only; data doesn't persist on OpenRouter
response = client.chat.completions.create(
model="openai/gpt-4o",
messages=[{"role": "user", "content": "Process this..."}],
max_tokens=1024,
extra_body={
"provider": {
"order": ["OpenAI"],
"allow_fallbacks": False,
},
},
# With BYOK, configure your provider key in OpenRouter dashboard
# Data flows: your app -> OpenRouter (routing only) -> OpenAI (your account)
)
Compliance Audit Script
#!/bin/bash
echo "=== OpenRouter Compliance Audit ==="
# 1. Verify API key has credit limit set
echo "1. Key configuration:"
curl -s https://openrouter.ai/api/v1/auth/key \
-H "Authorization: Bearer $OPENROUTER_API_KEY" | \
jq '{label: .data.label, limit: .data.limit, is_free_tier: .data.is_free_tier}'
# 2. Check if using free tier (not suitable for regulated data)
IS_FREE=$(curl -s https://openrouter.ai/api/v1/auth/key \
-H "Authorization: Bearer $OPENROUTER_API_KEY" | jq -r '.data.is_free_tier')
[ "$IS_FREE" = "true" ] && echo "WARNING: Free tier. Not suitable for regulated data."
# 3. Scan for hardcoded keys in source
FOUND=$(grep -r "sk-or-v1-" --include="*.py" --include="*.ts" --include="*.js" . 2>/dev/null | grep -v node_modules | wc -l)
echo "Hardcoded keys found: $FOUND"
Output
- A pass/fail/warn compliance report from the automated checker in the references, one line per control (API key storage, HTTPS enforcement, max_tokens, error handling, audit logging)
- Key-configuration JSON (
label, limit, is_free_tier) plus a free-tier warning and a count of hardcoded keys found in source, from the Compliance Audit Script
- A provider-pinned client configuration (
provider.order + allow_fallbacks: False) that cannot route regulated data to unapproved providers
- A filled-in markdown compliance checklist (template in the references) covering security, data privacy, reliability, observability, and cost controls
Examples
Running run_compliance_review() from the references against a healthy integration:
Compliance: 5/5 passed, 0 failed, 0 warnings
[OK] api_key_storage: Key loaded from environment variable
[OK] https_enforcement: HTTPS enforced
[OK] max_tokens: max_tokens set to 500
[OK] error_handling: Error handling present
[OK] audit_logging: Audit logging configured
Any [FAIL] line maps to a checklist item above — fix it and re-run until clean. More worked examples: references/examples.md.
Error Handling
| Error |
Cause |
Fix |
| Request routed to unapproved provider |
allow_fallbacks: true (default) |
Set allow_fallbacks: false with explicit order |
| Key exposed in logs |
Raw API key logged |
Add PII redaction for sk-or-v1-* pattern |
| No audit trail for request |
Logging middleware bypassed |
Make audit logging a required wrapper |
| Free model used for regulated data |
No model allowlist |
Implement model allowlist in client wrapper |
Enterprise Considerations
- OpenRouter does not train on API data, but upstream providers may have different terms for API vs consumer use
- Use
provider.order + allow_fallbacks: false to guarantee data only flows to approved providers
- BYOK eliminates OpenRouter as a data processor for inference (routing metadata still transits)
- Document the data flow diagram for auditors: client -> OpenRouter (routing) -> provider (inference)
- Implement client-side PII redaction as defense-in-depth
- Consider self-hosted or VPC deployments for restricted/PHI data
References
Source: jeremylongshore/claude-code-plugins-plus-skills → skills/.curated/openrouter-compliance-review/SKILL.md
Also appears in: jeremylongshore/claude-code-plugins-plus-skills/plugins/saas-packs/openrouter-pack/skills/openrouter-compliance-review/SKILL.md
1---
2name: openrouter-compliance-review
3description: "'Review OpenRouter integration for regulatory compliance (SOC2, GDPR, HIPAA). Use when preparing for audits, evaluating data handling, or documenting compliance posture. Triggers: ''openrouter compliance'', ''openrouter gdpr'', ''openrouter soc2'', ''openrouter data residency''. '"
4allowed-tools: "Read, Write, Edit, Grep, Bash(python3:*), Bash(curl:*), Bash(jq:*)"
5category: security-and-compliance
6source_repo: jeremylongshore/claude-code-plugins-plus-skills
7source_path: "skills/.curated/openrouter-compliance-review/SKILL.md"
8source_url: https://github.com/jeremylongshore/claude-code-plugins-plus-skills/blob/HEAD/skills/.curated/openrouter-compliance-review/SKILL.md
9---
10
11# OpenRouter Compliance Review
12
13## Overview
14
15OpenRouter is a proxy that routes requests to upstream providers (OpenAI, Anthropic, Google, etc.). Compliance depends on both OpenRouter's data handling and the selected provider's policies. Key considerations: data transit through OpenRouter infrastructure, provider-specific data retention, model selection for regulated data, and audit trail requirements.
16
17## Prerequisites
18
19- An OpenRouter API key (`sk-or-v1-...`) exported as `OPENROUTER_API_KEY` — see the `openrouter-install-auth` skill for setup
20- Python 3.8+ with the OpenAI SDK for provider-pinned requests and the automated checker in the references
21- `curl` and `jq` to run the Compliance Audit Script
22- An existing OpenRouter integration to review — the audit script scans its source tree for hardcoded `sk-or-v1-` keys
23- Knowledge of which regimes apply (SOC2, GDPR, HIPAA) and how your data is classified
24
25## Instructions
26
271. Work through the four areas of the Compliance Checklist — `data_handling`, `access_control`, `audit_trail`, and `provider_selection` — recording pass/fail per item.
282. Classify each workload with the Data Classification Matrix (Public → Internal → Confidential → Restricted/PHI) to determine allowed providers and required controls.
293. Pin regulated traffic per Provider Routing for Compliance: set `provider.order` plus `allow_fallbacks: False`, then verify `response.model` confirms the approved provider actually served the request.
304. For data-sovereignty requirements, configure BYOK per BYOK for Data Sovereignty so inference runs on your own provider account and OpenRouter only routes.
315. Run the Compliance Audit Script: key label/limit check via `GET /api/v1/auth/key`, a free-tier warning (free tier is unsuitable for regulated data), and the hardcoded-key scan.
326. Document the data flow for auditors — client → OpenRouter (routing) → provider (inference) — per Enterprise Considerations.
33
34## Compliance Checklist
35
36```python
37COMPLIANCE_CHECKLIST = {
38 "data_handling": [
39 "Verify OpenRouter does NOT train on your data (confirmed in their privacy policy)",
40 "Confirm provider-level data policies (OpenAI, Anthropic, Google each differ)",
41 "Document data flow: your app -> OpenRouter -> provider -> OpenRouter -> your app",
42 "Identify if prompts contain PII, PHI, or regulated data",
43 "Implement PII redaction before sending to API",
44 ],
45 "access_control": [
46 "Use per-service API keys (not shared keys)",
47 "Set credit limits per key to isolate blast radius",
48 "Rotate keys on a 90-day schedule",
49 "Store keys in secrets manager (not .env files in repos)",
50 "Enable management keys for programmatic key provisioning",
51 ],
52 "audit_trail": [
53 "Log every API call with generation_id, model, user_id, cost",
54 "Hash prompts (SHA-256) instead of logging raw content",
55 "Retain audit logs per regulation (90d operational, 7yr financial)",
56 "Ship logs to append-only storage (S3, immutable DB)",
57 ],
58 "provider_selection": [
59 "Route regulated data only to compliant providers",
60 "Use provider routing to exclude non-compliant providers",
61 "Document which models are approved for which data classifications",
62 "Test that fallback routing doesn't route to unapproved providers",
63 ],
64}
65```
66
67## Provider Routing for Compliance
68
69```python
70import os
71from openai import OpenAI
72
73client = OpenAI(
74 base_url="https://openrouter.ai/api/v1",
75 api_key=os.environ["OPENROUTER_API_KEY"],
76 default_headers={"HTTP-Referer": "https://my-app.com", "X-Title": "my-app"},
77)
78
79# Route ONLY to specific providers (e.g., Anthropic for SOC2)
80response = client.chat.completions.create(
81 model="anthropic/claude-3.5-sonnet",
82 messages=[{"role": "user", "content": "Analyze this contract..."}],
83 max_tokens=2048,
84 extra_body={
85 "provider": {
86 "order": ["Anthropic"], # Only Anthropic's infrastructure
87 "allow_fallbacks": False, # Do NOT fall back to other providers
88 },
89 },
90)
91
92# Verify which provider actually served the request
93print(f"Served by: {response.model}") # Should match anthropic/claude-3.5-sonnet
94```
95
96## Data Classification Matrix
97
98| Classification | Allowed Providers | Controls |
99|---------------|-------------------|----------|
100| Public | Any (including `:free`) | Standard logging |
101| Internal | Tier 1 (OpenAI, Anthropic, Google) | Audit logging, key limits |
102| Confidential | Anthropic, OpenAI (API-only) | PII redaction, no free models |
103| Restricted/PHI | BYOK only or self-hosted | Full audit, encryption at rest |
104
105## BYOK for Data Sovereignty
106
107```python
108# Bring Your Own Key -- requests go directly to provider
109# OpenRouter acts as router only; data doesn't persist on OpenRouter
110response = client.chat.completions.create(
111 model="openai/gpt-4o",
112 messages=[{"role": "user", "content": "Process this..."}],
113 max_tokens=1024,
114 extra_body={
115 "provider": {
116 "order": ["OpenAI"],
117 "allow_fallbacks": False,
118 },
119 },
120 # With BYOK, configure your provider key in OpenRouter dashboard
121 # Data flows: your app -> OpenRouter (routing only) -> OpenAI (your account)
122)
123```
124
125## Compliance Audit Script
126
127```bash
128#!/bin/bash
129echo "=== OpenRouter Compliance Audit ==="
130
131# 1. Verify API key has credit limit set
132echo "1. Key configuration:"
133curl -s https://openrouter.ai/api/v1/auth/key \
134 -H "Authorization: Bearer $OPENROUTER_API_KEY" | \
135 jq '{label: .data.label, limit: .data.limit, is_free_tier: .data.is_free_tier}'
136
137# 2. Check if using free tier (not suitable for regulated data)
138IS_FREE=$(curl -s https://openrouter.ai/api/v1/auth/key \
139 -H "Authorization: Bearer $OPENROUTER_API_KEY" | jq -r '.data.is_free_tier')
140[ "$IS_FREE" = "true" ] && echo "WARNING: Free tier. Not suitable for regulated data."
141
142# 3. Scan for hardcoded keys in source
143FOUND=$(grep -r "sk-or-v1-" --include="*.py" --include="*.ts" --include="*.js" . 2>/dev/null | grep -v node_modules | wc -l)
144echo "Hardcoded keys found: $FOUND"
145```
146
147## Output
148
149- A pass/fail/warn compliance report from the automated checker in the references, one line per control (API key storage, HTTPS enforcement, max_tokens, error handling, audit logging)
150- Key-configuration JSON (`label`, `limit`, `is_free_tier`) plus a free-tier warning and a count of hardcoded keys found in source, from the Compliance Audit Script
151- A provider-pinned client configuration (`provider.order` + `allow_fallbacks: False`) that cannot route regulated data to unapproved providers
152- A filled-in markdown compliance checklist (template in the references) covering security, data privacy, reliability, observability, and cost controls
153
154## Examples
155
156Running `run_compliance_review()` from the references against a healthy integration:
157
158```text
159Compliance: 5/5 passed, 0 failed, 0 warnings
160 [OK] api_key_storage: Key loaded from environment variable
161 [OK] https_enforcement: HTTPS enforced
162 [OK] max_tokens: max_tokens set to 500
163 [OK] error_handling: Error handling present
164 [OK] audit_logging: Audit logging configured
165```
166
167Any `[FAIL]` line maps to a checklist item above — fix it and re-run until clean. More worked examples: `references/examples.md`.
168
169## Error Handling
170
171| Error | Cause | Fix |
172|-------|-------|-----|
173| Request routed to unapproved provider | `allow_fallbacks: true` (default) | Set `allow_fallbacks: false` with explicit `order` |
174| Key exposed in logs | Raw API key logged | Add PII redaction for `sk-or-v1-*` pattern |
175| No audit trail for request | Logging middleware bypassed | Make audit logging a required wrapper |
176| Free model used for regulated data | No model allowlist | Implement model allowlist in client wrapper |
177
178## Enterprise Considerations
179
180- OpenRouter does not train on API data, but upstream providers may have different terms for API vs consumer use
181- Use `provider.order` + `allow_fallbacks: false` to guarantee data only flows to approved providers
182- BYOK eliminates OpenRouter as a data processor for inference (routing metadata still transits)
183- Document the data flow diagram for auditors: client -> OpenRouter (routing) -> provider (inference)
184- Implement client-side PII redaction as defense-in-depth
185- Consider self-hosted or VPC deployments for restricted/PHI data
186
187## References
188
189- Examples | Errors
190- [Privacy Policy](https://openrouter.ai/privacy) | [Provider Routing](https://openrouter.ai/docs/features/provider-routing)
191
192---
193
194**Source:** [`jeremylongshore/claude-code-plugins-plus-skills`](https://github.com/jeremylongshore/claude-code-plugins-plus-skills) → `skills/.curated/openrouter-compliance-review/SKILL.md`
195
196**Also appears in:** `jeremylongshore/claude-code-plugins-plus-skills/plugins/saas-packs/openrouter-pack/skills/openrouter-compliance-review/SKILL.md`