From proofpoint
Provides Proofpoint API integration patterns: HTTP Basic Auth with service principal/secret, base URLs, rate limits, pagination, errors for TAP SIEM, quarantine, people, URL Defense.
npx claudepluginhub wyre-technology/msp-claude-plugins --plugin proofpointThis skill uses the workspace's default tool permissions.
The Proofpoint APIs provide programmatic access to email security data including threat events, quarantine management, people risk analytics, and URL defense. This skill covers authentication, base URLs, rate limiting, pagination, error handling, and best practices for API integration.
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Migrates code, prompts, and API calls from Claude Sonnet 4.0/4.5 or Opus 4.1 to Opus 4.5, updating model strings on Anthropic, AWS, GCP, Azure platforms.
Compresses source documents into lossless, LLM-optimized distillates preserving all facts and relationships. Use for 'distill documents' or 'create distillate' requests.
The Proofpoint APIs provide programmatic access to email security data including threat events, quarantine management, people risk analytics, and URL defense. This skill covers authentication, base URLs, rate limiting, pagination, error handling, and best practices for API integration.
Proofpoint operates multiple API endpoints, each serving a different product area. All APIs share the same authentication mechanism but have different base URLs and rate limits.
All Proofpoint APIs use HTTP Basic Authentication with a service principal and secret:
GET /v2/siem/all?sinceSeconds=3600
Host: tap-api.proofpoint.com
Authorization: Basic <base64(service_principal:service_secret)>
Content-Type: application/json
Constructing the Authorization Header:
const credentials = Buffer.from(`${servicePrincipal}:${serviceSecret}`).toString('base64');
const headers = {
'Authorization': `Basic ${credentials}`,
'Content-Type': 'application/json'
};
# Using curl
curl -u "SERVICE_PRINCIPAL:SERVICE_SECRET" \
"https://tap-api.proofpoint.com/v2/siem/all?sinceSeconds=3600"
export PROOFPOINT_SERVICE_PRINCIPAL="your-service-principal"
export PROOFPOINT_SERVICE_SECRET="your-service-secret"
export PROOFPOINT_MCP_URL="https://proofpoint-mcp.wyre.workers.dev/mcp"
https://threatinsight.proofpoint.comImportant: Service credentials are scoped to your organization. Each MSP client organization requires its own set of credentials.
| API | Base URL | Description |
|---|---|---|
| TAP SIEM | https://tap-api.proofpoint.com | Threat events, clicks, messages |
| People | https://tap-api.proofpoint.com | VAP reports, top clickers, user risk |
| Quarantine | https://tap-api.proofpoint.com | Quarantine management |
| Forensics | https://tap-api.proofpoint.com | Threat response and investigation |
| URL Defense | https://tap-api.proofpoint.com | URL decoding and analysis |
Note: All APIs currently share the same base URL (tap-api.proofpoint.com) but are versioned and namespaced separately in the path.
| API | Version | Path Prefix | Example |
|---|---|---|---|
| TAP SIEM | v2 | /v2/siem/ | /v2/siem/all?sinceSeconds=3600 |
| People | v2 | /v2/people/ | /v2/people/vap?window=30 |
| Campaign | v1 | /v1/campaign/ | /v1/campaign/{campaignId} |
| Forensics | v2 | /v2/forensics/ | /v2/forensics?threatId={id} |
| URL Defense | v2 | /v2/url/ | /v2/url/decode |
| API | Requests per Hour | Burst Limit | Notes |
|---|---|---|---|
| TAP SIEM | 1000 | 10/sec | Per service principal |
| People | 500 | 5/sec | Per service principal |
| Quarantine | 500 | 5/sec | Per service principal |
| Forensics | 500 | 5/sec | Per service principal |
| URL Defense | 1000 | 10/sec | Per service principal |
HTTP/1.1 200 OK
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 995
X-RateLimit-Reset: 1708012800
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests per window |
X-RateLimit-Remaining | Requests remaining in current window |
X-RateLimit-Reset | Unix timestamp when the window resets |
{
"error": "Rate limit exceeded",
"message": "Too many requests. Please retry after the rate limit window resets.",
"retryAfter": 60
}
async function requestWithRetry(url, options, maxRetries = 5) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const response = await fetch(url, options);
if (response.status === 429) {
const retryAfter = parseInt(response.headers.get('Retry-After') || '60');
const jitter = Math.random() * 5000;
await sleep(retryAfter * 1000 + jitter);
continue;
}
if (response.status >= 500) {
const delay = Math.pow(2, attempt) * 1000 + Math.random() * 1000;
await sleep(delay);
continue;
}
return response;
}
throw new Error(`Request failed after ${maxRetries} retries`);
}
The TAP SIEM API does not use traditional offset-based pagination. Instead, it uses time-based windowing:
GET /v2/siem/all?sinceSeconds=3600
GET /v2/siem/all?sinceTime=2024-02-15T00:00:00Z
GET /v2/siem/all?interval=PT30M/2024-02-15T12:00:00Z
| Parameter | Description | Max |
|---|---|---|
sinceSeconds | Events from N seconds ago to now | 86400 (24h) |
sinceTime | Events from timestamp to now | 24h from now |
interval | ISO 8601 interval (duration/end) | 1 hour window |
GET /v2/quarantine/search?limit=25&offset=0
GET /v2/quarantine/search?limit=25&offset=25
| Parameter | Description | Default | Max |
|---|---|---|---|
limit | Results per page | 25 | 500 |
offset | Starting offset | 0 | - |
GET /v2/people/vap?window=30&size=100&page=1
| Parameter | Description | Default | Max |
|---|---|---|---|
size | Results per page | 100 | 1000 |
page | Page number (1-based) | 1 | - |
| Code | Meaning | Action |
|---|---|---|
| 200 | Success | Process response |
| 204 | No content | No events in the specified window |
| 400 | Bad request | Check request parameters |
| 401 | Unauthorized | Verify service principal and secret |
| 403 | Forbidden | Check API access permissions |
| 404 | Not found | Resource does not exist |
| 429 | Rate limited | Implement backoff and retry |
| 500 | Server error | Retry with exponential backoff |
| 503 | Service unavailable | Retry after brief delay |
{
"error": "Bad Request",
"message": "The sinceSeconds parameter must be between 1 and 86400.",
"status": 400
}
| Error | Cause | Resolution |
|---|---|---|
| 401 with valid credentials | Credentials may be expired | Regenerate in TAP dashboard |
| 403 on People API | License does not include People | Upgrade license or contact Proofpoint |
| 400 on time range | Window exceeds 24 hours | Reduce sinceSeconds to <= 86400 |
| 204 on SIEM query | No events in time window | Normal - no threats in the period |
| 404 on campaign | Campaign ID is invalid or old | Verify ID from TAP event data |
GET /v2/siem/all?format=json&sinceSeconds=3600
Authorization: Basic <credentials>
GET /v2/siem/messages/blocked?format=json&sinceSeconds=3600
Authorization: Basic <credentials>
GET /v2/siem/messages/delivered?format=json&sinceSeconds=3600
Authorization: Basic <credentials>
GET /v2/siem/clicks/permitted?format=json&sinceSeconds=3600
Authorization: Basic <credentials>
GET /v2/people/vap?window=30&size=20
Authorization: Basic <credentials>
GET /v1/campaign/{campaignId}
Authorization: Basic <credentials>
GET /v2/forensics?threatId={threatId}
Authorization: Basic <credentials>
{
"queryEndTime": "2024-02-15T12:00:00Z",
"messagesBlocked": [...],
"messagesDelivered": [...],
"clicksBlocked": [...],
"clicksPermitted": [...]
}
{
"users": [
{
"identity": {
"guid": "abc123",
"customerUserId": null,
"emails": ["user@example.com"],
"name": "John Smith",
"department": "Finance",
"location": "New York",
"title": "CFO",
"vip": true
},
"threatStatistics": {
"attackIndex": 856,
"families": [
{"name": "Emotet", "count": 12},
{"name": "QBot", "count": 8}
]
}
}
],
"totalVapUsers": 150
}
// Good: Poll every 5 minutes for near-real-time
setInterval(() => fetchTAPEvents('sinceSeconds=300'), 5 * 60 * 1000);
// Avoid: Polling every 10 seconds burns rate limit
setInterval(() => fetchTAPEvents('sinceSeconds=10'), 10 * 1000);
// Good: Fetch only new events since last poll
const lastPoll = getLastPollTime();
fetch(`/v2/siem/all?sinceTime=${lastPoll.toISOString()}`);
// Avoid: Always fetching full 24 hours
fetch('/v2/siem/all?sinceSeconds=86400');
Cache data that changes infrequently:
X-RateLimit-Remaining to avoid hitting limitssinceTime is within the 24-hour maximum windowsinceSeconds=300 (5 minutes) when testing