Apollo enterprise rbac
'Enterprise role-based access control for Apollo.io.From its SKILL.md
npx -y skills add jeremylongshore/claude-code-plugins-plus-skills --skill apollo-enterprise-rbacAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
What its file declares
Copied from the file, not written here
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
8.8 KB, ~2.1k tokens by cl100k_base, as published. Nobody here has run it
Apollo Enterprise RBAC
Overview
Role-based access control for Apollo.io API integrations. Apollo API keys are all-or-nothing (standard vs master), so RBAC must be implemented in your application layer as a proxy between users and the Apollo API. This skill builds a permission matrix, scoped API key system, Express middleware, and admin audit endpoints.
Prerequisites
- Apollo master API key
- Node.js 18+ with Express
Instructions
Step 1: Define Roles and Permission Matrix
Map Apollo API operations to team roles. Apollo's API has two main categories:
- Read-only: search (free), enrichment (credits)
- Write: contacts CRUD, sequences, deals, tasks
// src/rbac/roles.ts
export type Role = 'viewer' | 'analyst' | 'sales_rep' | 'sales_manager' | 'admin';
export interface Permission {
searchPeople: boolean; // /mixed_people/api_search (free)
searchOrganizations: boolean; // /mixed_companies/search (free)
enrichPerson: boolean; // /people/match (1 credit)
bulkEnrich: boolean; // /people/bulk_match (credits)
enrichOrg: boolean; // /organizations/enrich (1 credit)
manageContacts: boolean; // /contacts CRUD (master key)
manageSequences: boolean; // /emailer_campaigns/* (master key)
manageDeals: boolean; // /opportunities/* (master key)
exportPII: boolean; // download contacts with email/phone
viewAnalytics: boolean; // sequence stats, usage
manageTeam: boolean; // create/revoke scoped keys
}
export const PERMISSIONS: Record<Role, Permission> = {
viewer: {
searchPeople: true, searchOrganizations: true, enrichPerson: false,
bulkEnrich: false, enrichOrg: false, manageContacts: false,
manageSequences: false, manageDeals: false, exportPII: false,
viewAnalytics: true, manageTeam: false,
},
analyst: {
searchPeople: true, searchOrganizations: true, enrichPerson: true,
bulkEnrich: false, enrichOrg: true, manageContacts: false,
manageSequences: false, manageDeals: false, exportPII: false,
viewAnalytics: true, manageTeam: false,
},
sales_rep: {
searchPeople: true, searchOrganizations: true, enrichPerson: true,
bulkEnrich: false, enrichOrg: true, manageContacts: true,
manageSequences: true, manageDeals: true, exportPII: false,
viewAnalytics: false, manageTeam: false,
},
sales_manager: {
searchPeople: true, searchOrganizations: true, enrichPerson: true,
bulkEnrich: true, enrichOrg: true, manageContacts: true,
manageSequences: true, manageDeals: true, exportPII: true,
viewAnalytics: true, manageTeam: true,
},
admin: {
searchPeople: true, searchOrganizations: true, enrichPerson: true,
bulkEnrich: true, enrichOrg: true, manageContacts: true,
manageSequences: true, manageDeals: true, exportPII: true,
viewAnalytics: true, manageTeam: true,
},
};
Step 2: Scoped API Key System
// src/rbac/api-keys.ts
import crypto from 'crypto';
interface ScopedKey {
key: string;
teamId: string;
role: Role;
createdBy: string;
createdAt: string;
expiresAt: string;
}
// In production: store in database
const keys = new Map<string, ScopedKey>();
export function createScopedKey(teamId: string, role: Role, createdBy: string, ttlDays: number = 90): ScopedKey {
const entry: ScopedKey = {
key: `ak_${teamId}_${crypto.randomBytes(16).toString('hex')}`,
teamId, role, createdBy,
createdAt: new Date().toISOString(),
expiresAt: new Date(Date.now() + ttlDays * 86400000).toISOString(),
};
keys.set(entry.key, entry);
return entry;
}
export function resolveKey(apiKey: string): ScopedKey | null {
const entry = keys.get(apiKey);
if (!entry) return null;
if (new Date(entry.expiresAt) < new Date()) { keys.delete(apiKey); return null; }
return entry;
}
export function revokeKey(apiKey: string) { keys.delete(apiKey); }
Step 3: Permission Middleware
// src/rbac/middleware.ts
import { Request, Response, NextFunction } from 'express';
import { PERMISSIONS, Permission } from './roles';
import { resolveKey } from './api-keys';
// Map Apollo API paths to required permissions
const ENDPOINT_PERMISSIONS: Record<string, keyof Permission> = {
'/mixed_people/api_search': 'searchPeople',
'/mixed_companies/search': 'searchOrganizations',
'/people/match': 'enrichPerson',
'/people/bulk_match': 'bulkEnrich',
'/organizations/enrich': 'enrichOrg',
'/contacts': 'manageContacts',
'/emailer_campaigns': 'manageSequences',
'/opportunities': 'manageDeals',
};
export function requirePermission(action: keyof Permission) {
return (req: Request, res: Response, next: NextFunction) => {
const apiKey = req.headers['x-api-key'] as string;
if (!apiKey) return res.status(401).json({ error: 'x-api-key header required' });
const key = resolveKey(apiKey);
if (!key) return res.status(401).json({ error: 'Invalid or expired API key' });
if (!PERMISSIONS[key.role][action]) {
return res.status(403).json({
error: `Permission denied: ${action} requires role upgrade`,
currentRole: key.role,
});
}
(req as any).apolloCtx = { teamId: key.teamId, role: key.role, user: key.createdBy };
next();
};
}
Step 4: Apollo API Proxy with RBAC
// src/rbac/proxy.ts
import express from 'express';
import axios from 'axios';
const app = express();
app.use(express.json());
// Proxy all /apollo/* requests through RBAC
app.all('/apollo/*', (req, res, next) => {
const apolloPath = req.path.replace('/apollo', '');
const matchedKey = Object.keys(ENDPOINT_PERMISSIONS).find((p) => apolloPath.startsWith(p));
if (!matchedKey) return res.status(404).json({ error: 'Unknown Apollo endpoint' });
requirePermission(ENDPOINT_PERMISSIONS[matchedKey])(req, res, next);
}, async (req, res) => {
const apolloPath = req.path.replace('/apollo', '');
try {
const response = await axios({
method: req.method as any,
url: `https://api.apollo.io/api/v1${apolloPath}`,
data: req.body,
params: req.query,
headers: { 'Content-Type': 'application/json', 'x-api-key': process.env.APOLLO_API_KEY! },
});
res.status(response.status).json(response.data);
} catch (err: any) {
res.status(err.response?.status ?? 500).json(err.response?.data ?? { error: err.message });
}
});
Step 5: Admin Endpoints
// src/rbac/admin.ts
import { Router } from 'express';
import { requirePermission } from './middleware';
import { createScopedKey, revokeKey } from './api-keys';
const admin = Router();
admin.use(requirePermission('manageTeam'));
admin.post('/keys', (req, res) => {
const { teamId, role, ttlDays } = req.body;
const ctx = (req as any).apolloCtx;
const key = createScopedKey(teamId, role, ctx.user, ttlDays);
res.json({ key: key.key, role: key.role, expiresAt: key.expiresAt });
});
admin.delete('/keys/:key', (req, res) => {
revokeKey(req.params.key);
res.json({ revoked: true });
});
admin.get('/usage', async (req, res) => {
// Check Apollo's usage stats
const { data } = await axios.get('https://api.apollo.io/api/v1/usage', {
headers: { 'x-api-key': process.env.APOLLO_API_KEY! },
});
res.json(data);
});
export { admin };
Output
- Five-tier role system mapping to Apollo API operations
- Scoped API key creation with configurable TTL and revocation
- Express middleware enforcing per-endpoint permissions
- Apollo API proxy routing all requests through RBAC
- Admin endpoints for key management and usage stats
Error Handling
| Issue | Resolution |
|---|---|
| 403 Permission denied | Check role matrix; request upgrade from admin |
| Key expired | Admin creates new key via POST /keys |
| Wrong role for bulk enrichment | Only sales_manager and admin have bulkEnrich |
| Proxy timeout | Increase timeout, check Apollo API latency |
Resources
Next Steps
Proceed to apollo-migration-deep-dive for migration strategies.
What ships with it: 1 file
12.6 KB alongside SKILL.md
references/
- implementation-guide.md12.6 KB
Gives 0 of the 12 instructions most auth identity skills give in ~2.1k tokens
Counted across 408 of the 432 authors here whose files we hold, read 2026-09-06
- Use parameterized queries for all SQL and database access to prevent SQL injectionin 60 of 408, across 40 files
- Hash passwords with bcrypt or Argon2idin 50 of 408, across 34 files
- Store secrets in Vault or environment variablesin 37 of 408, across 17 files
- Add security headers to all responsesin 28 of 408, across 14 files
- Rate limit public endpoints per clientin 22 of 408, across 8 files
- Store tokens in httpOnly cookiesin 21 of 408, across 16 files
- Use HTTPS in productionin 17 of 408, across 4 files
- Apply rate limiting to all API endpointsin 17 of 408, across 9 files
- Validate all input with Bean Validationin 16 of 408, across 3 files
- Enable JWT or OIDC for stateless authenticationin 15 of 408, across 2 files
- Scan dependencies for CVEsin 15 of 408, across 2 files
- Log audit records for sensitive operationsin 15 of 408, across 2 files
Said here and by no other author read
- map Apollo API operations to team roles in a permission matrix
- create scoped API keys with expiry and revocation
- map each Apollo endpoint to a required permission
- enforce per-endpoint permissions in Express middleware
- proxy all Apollo requests through the RBAC layer
- add admin endpoints for key management and usage stats
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.