clay-policy-guardrails
Sets up credit spending limits, input validation, and data guardrails for Clay enrichment pipelines.
Install
mkdir -p .claude/skills/clay-policy-guardrails && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/7859" && unzip -o skill.zip -d .claude/skills/clay-policy-guardrails && rm skill.zipInstalls to .claude/skills/clay-policy-guardrails
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 credit spending limits, data privacy enforcement, and inputKey capabilities
- →Define daily, per-table, and per-batch credit spending limits
- →Enforce hard stops or generate warnings based on credit usage thresholds
- →Identify and block enrichment of sensitive personal data fields
- →Warn about enrichment of fields requiring explicit user consent
- →Validate email addresses for personal or disposable domains
How it works
The skill defines policies for credit spending, data privacy, input validation, and data export, then provides functions to check and enforce these rules on Clay data operations.
Inputs & outputs
When to use clay-policy-guardrails
- →Setting credit spending limits
- →Adding input validation for enrichment
- →Implementing cost control guardrails
- →Configuring alert thresholds
About this skill
Clay Policy Guardrails
Overview
Policy enforcement and guardrails for Clay data enrichment pipelines. Clay processes personal and business data at scale, requiring strict controls around credit spending, data privacy compliance, input validation, and export restrictions.
Prerequisites
- Clay integration in production or pre-production
- Understanding of GDPR/CCPA requirements
- Credit budget defined by management
- Data classification policy for your organization
Instructions
Step 1: Credit Spending Guardrails
// src/clay/policies/credit-policy.ts
interface CreditPolicy {
dailyLimit: number;
perTableLimit: number;
perBatchLimit: number;
alertThresholdPct: number;
hardStopEnabled: boolean;
}
const CREDIT_POLICIES: Record<string, CreditPolicy> = {
conservative: {
dailyLimit: 200,
perTableLimit: 500,
perBatchLimit: 100,
alertThresholdPct: 70,
hardStopEnabled: true,
},
standard: {
dailyLimit: 500,
perTableLimit: 2000,
perBatchLimit: 500,
alertThresholdPct: 80,
hardStopEnabled: true,
},
aggressive: {
dailyLimit: 2000,
perTableLimit: 10000,
perBatchLimit: 2000,
alertThresholdPct: 90,
hardStopEnabled: false, // Alert only, don't stop
},
};
class CreditPolicyEnforcer {
private dailyUsed = 0;
private tableUsage = new Map<string, number>();
constructor(private policy: CreditPolicy) {}
checkBatch(tableId: string, rowCount: number, creditsPerRow: number): {
allowed: boolean;
reason?: string;
} {
const estimated = rowCount * creditsPerRow;
// Batch limit
if (estimated > this.policy.perBatchLimit) {
return {
allowed: false,
reason: `Batch (${estimated} credits) exceeds per-batch limit (${this.policy.perBatchLimit}). Split into smaller batches.`,
};
}
// Daily limit
if (this.dailyUsed + estimated > this.policy.dailyLimit) {
if (this.policy.hardStopEnabled) {
return {
allowed: false,
reason: `Would exceed daily limit: ${this.dailyUsed} + ${estimated} > ${this.policy.dailyLimit}`,
};
}
console.warn(`WARNING: Exceeding daily limit (${this.dailyUsed + estimated}/${this.policy.dailyLimit})`);
}
// Per-table limit
const tableTotal = (this.tableUsage.get(tableId) || 0) + estimated;
if (tableTotal > this.policy.perTableLimit) {
return {
allowed: false,
reason: `Table ${tableId} would exceed limit: ${tableTotal} > ${this.policy.perTableLimit}`,
};
}
// Alert threshold
const dailyPct = ((this.dailyUsed + estimated) / this.policy.dailyLimit) * 100;
if (dailyPct > this.policy.alertThresholdPct) {
console.warn(`Credit alert: ${dailyPct.toFixed(0)}% of daily limit used`);
}
return { allowed: true };
}
recordUsage(tableId: string, credits: number) {
this.dailyUsed += credits;
this.tableUsage.set(tableId, (this.tableUsage.get(tableId) || 0) + credits);
}
}
Step 2: Data Privacy Guardrails
// src/clay/policies/privacy-policy.ts
// Fields that should NEVER be enriched or stored
const BLOCKED_ENRICHMENT_FIELDS = new Set([
'ssn', 'social_security', 'tax_id',
'date_of_birth', 'dob', 'birthday',
'home_address', 'home_phone',
'personal_phone', 'personal_mobile',
'bank_account', 'credit_card',
'medical_history', 'health_records',
'salary', 'compensation',
'political_affiliation', 'religion',
'ethnic_origin', 'sexual_orientation',
]);
// Fields that require explicit consent
const CONSENT_REQUIRED_FIELDS = new Set([
'personal_email', 'phone_number', 'mobile_phone',
]);
interface PrivacyCheckResult {
allowed: boolean;
violations: string[];
warnings: string[];
}
function checkPrivacy(
fieldsToEnrich: string[],
hasExplicitConsent: boolean = false,
): PrivacyCheckResult {
const violations: string[] = [];
const warnings: string[] = [];
for (const field of fieldsToEnrich) {
const normalized = field.toLowerCase().replace(/[\s-]/g, '_');
if (BLOCKED_ENRICHMENT_FIELDS.has(normalized)) {
violations.push(`BLOCKED: "${field}" is a restricted field (never enrich)`);
}
if (CONSENT_REQUIRED_FIELDS.has(normalized) && !hasExplicitConsent) {
warnings.push(`CONSENT: "${field}" requires explicit consent to enrich`);
}
}
return {
allowed: violations.length === 0,
violations,
warnings,
};
}
Step 3: Input Validation Guardrails
// src/clay/policies/input-validation.ts
const PERSONAL_EMAIL_DOMAINS = new Set([
'gmail.com', 'yahoo.com', 'hotmail.com', 'outlook.com', 'icloud.com',
'aol.com', 'protonmail.com', 'mail.com', 'yandex.com', 'gmx.com',
]);
const DISPOSABLE_EMAIL_DOMAINS = new Set([
'tempmail.com', 'guerrillamail.com', 'throwaway.email', 'yopmail.com',
'mailinator.com', '10minutemail.com', 'trashmail.com',
]);
interface ValidationResult {
valid: Record<string, unknown>[];
rejected: { row: Record<string, unknown>; reason: string }[];
stats: {
total: number;
valid: number;
invalidDomain: number;
personalDomain: number;
disposableDomain: number;
missingRequiredField: number;
duplicates: number;
};
}
function validateBatch(
rows: Record<string, unknown>[],
requiredFields: string[] = ['domain'],
): ValidationResult {
const seen = new Set<string>();
const stats = {
total: rows.length, valid: 0, invalidDomain: 0,
personalDomain: 0, disposableDomain: 0, missingRequiredField: 0, duplicates: 0,
};
const valid: Record<string, unknown>[] = [];
const rejected: { row: Record<string, unknown>; reason: string }[] = [];
for (const row of rows) {
// Required fields check
const missing = requiredFields.filter(f => !row[f]);
if (missing.length > 0) {
rejected.push({ row, reason: `Missing required: ${missing.join(', ')}` });
stats.missingRequiredField++;
continue;
}
const domain = String(row.domain || '').toLowerCase().trim();
// Domain validation
if (!domain.includes('.') || domain.length < 4) {
rejected.push({ row, reason: `Invalid domain: "${domain}"` });
stats.invalidDomain++;
continue;
}
// Personal domain filter
if (PERSONAL_EMAIL_DOMAINS.has(domain)) {
rejected.push({ row, reason: `Personal email domain: ${domain}` });
stats.personalDomain++;
continue;
}
// Disposable domain filter
if (DISPOSABLE_EMAIL_DOMAINS.has(domain)) {
rejected.push({ row, reason: `Disposable email domain: ${domain}` });
stats.disposableDomain++;
continue;
}
// Deduplication
const key = `${domain}:${String(row.first_name || '').toLowerCase()}:${String(row.last_name || '').toLowerCase()}`;
if (seen.has(key)) {
stats.duplicates++;
continue;
}
seen.add(key);
valid.push({ ...row, domain });
stats.valid++;
}
return { valid, rejected, stats };
}
Step 4: Export Restrictions
// src/clay/policies/export-policy.ts
type ExportDestination = 'crm' | 'outreach' | 'analytics' | 'csv';
const EXPORT_RULES: Record<ExportDestination, {
allowedFields: string[];
blockedFields: string[];
requiresApproval: boolean;
}> = {
crm: {
allowedFields: ['email', 'first_name', 'last_name', 'company_name', 'job_title', 'icp_score'],
blockedFields: ['personal_email', 'home_address'],
requiresApproval: false,
},
outreach: {
allowedFields: ['email', 'first_name', 'company_name', 'personalized_opener'],
blockedFields: ['phone_number', 'linkedin_url', 'personal_email'],
requiresApproval: false,
},
analytics: {
allowedFields: ['company_name', 'industry', 'employee_count', 'icp_score'],
blockedFields: ['email', 'first_name', 'last_name', 'phone_number'],
requiresApproval: false,
},
csv: {
allowedFields: ['*'], // All fields
blockedFields: ['personal_email', 'home_address', 'ssn'],
requiresApproval: true, // Requires manager approval for CSV export
},
};
function filterForExport(
rows: Record<string, unknown>[],
destination: ExportDestination,
): Record<string, unknown>[] {
const rules = EXPORT_RULES[destination];
return rows.map(row => {
const filtered: Record<string, unknown> = {};
for (const [key, value] of Object.entries(row)) {
if (rules.blockedFields.includes(key)) continue;
if (rules.allowedFields[0] !== '*' && !rules.allowedFields.includes(key)) continue;
filtered[key] = value;
}
return filtered;
});
}
Error Handling
| Issue | Cause | Solution |
|---|---|---|
| Credit overrun | No spending limits enforced | Implement credit policy enforcer |
| PII enrichment violation | No privacy checks | Add blocked field validation |
| Wasted credits on bad data | No input validation | Pre-validate all batches |
| Unauthorized data export | No export restrictions | Implement per-destination field filtering |
Resources
Next Steps
For architecture patterns at scale, see clay-architecture-variants.
When not to use it
- →When no credit budget is defined by management
- →When there is no data classification policy for your organization
Prerequisites
Limitations
- →Credit policy enforcement relies on defined limits
- →Data privacy checks are based on predefined blocked and consent-required fields
- →Input validation is based on predefined email domain lists
How it compares
This skill provides programmatic enforcement of Clay usage policies, contrasting with manual oversight or reactive issue resolution.
Compared to similar skills
clay-policy-guardrails side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| clay-policy-guardrails (this skill) | 1 | 26d | Review | Intermediate |
| custom-workers | 1 | 7mo | Review | Intermediate |
| audit-env-variables | 0 | 5mo | Review | Intermediate |
| file-organizer-dev | 0 | 6mo | 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
custom-workers
ruvnet
Create and run custom background analysis workers with composable phases. Use when you need automated code analysis, security scanning, pattern learning, or API documentation generation.
audit-env-variables
qdhenry
Analyze environment variables in JavaScript/TypeScript projects. Identifies unused variables, infers permission scopes, detects specific services (Stripe, AWS, Supabase), and documents code paths. Includes optional cleanup of unused variables with regression detection. Use when auditing .env files,
file-organizer-dev
kridaydave
Development guide for the File Organizer MCP server codebase. Use when (1) adding new MCP tools, (2) adding new services, (3) modifying existing tools or services, (4) writing tests, (5) fixing security issues, (6) refactoring code, or (7) understanding the architecture. Provides patterns for Zod sc
fix-dependabot-alerts
microsoft
Fix Dependabot security alerts by updating vulnerable npm dependencies. Use when the user mentions "dependabot", "security alerts", "vulnerability", "CVE", or wants to update packages with security issues.
telegram-bot-builder
davila7
Expert in building Telegram bots that solve real problems - from simple automation to complex AI-powered bots. Covers bot architecture, the Telegram Bot API, user experience, monetization strategies, and scaling bots to thousands of users. Use when: telegram bot, bot api, telegram automation, chat bot telegram, tg bot.
playwright-browser-automation
lackeyjb
Complete browser automation with Playwright. Auto-detects dev servers, writes clean test scripts to /tmp. Test pages, fill forms, take screenshots, check responsive design, validate UX, test login flows, check links, automate any browser task. Use when user wants to test websites, automate browser interactions, validate web functionality, or perform any browser-based testing.