Checkpoint Harmony Email API Patterns
Overview
The Checkpoint Harmony Email & Collaboration (Avanan) API provides programmatic access to email security operations including quarantine management, threat detection, policy configuration, and incident management. This skill covers authentication, request patterns, pagination, rate limiting, error handling, and performance optimization.
Authentication
OAuth2 Client Credentials Flow
Checkpoint Harmony Email uses OAuth2 client credentials for API authentication:
Step 1: Obtain Access Token
POST https://cloudinfra-gw.portal.checkpoint.com/auth/external
Content-Type: application/json
{
"clientId": "YOUR_CLIENT_ID",
"accessKey": "YOUR_CLIENT_SECRET"
}
Response:
{
"data": {
"token": "eyJhbGciOiJSUzI1NiIs...",
"expiresIn": 3600,
"csrfToken": "abc123..."
}
}
Step 2: Use Token in Subsequent Requests
GET https://cloudinfra-gw.portal.checkpoint.com/app/hec-api/v1.0/quarantine
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
Content-Type: application/json
Token Management
| Field | Description | |-------|-------------| | token | JWT bearer token for API requests | | expiresIn | Token lifetime in seconds (typically 3600 = 1 hour) | | csrfToken | CSRF token for state-changing operations |
Token Refresh Pattern:
class CheckpointAuth {
constructor(clientId, clientSecret) {
this.clientId = clientId;
this.clientSecret = clientSecret;
this.token = null;
this.expiresAt = 0;
}
async getToken() {
// Refresh 5 minutes before expiry
if (this.token && Date.now() < this.expiresAt - 300000) {
return this.token;
}
const response = await fetch(
'https://cloudinfra-gw.portal.checkpoint.com/auth/external',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
clientId: this.clientId,
accessKey: this.clientSecret
})
}
);
const data = await response.json();
this.token = data.data.token;
this.expiresAt = Date.now() + (data.data.expiresIn * 1000);
return this.token;
}
}
Environment Variables
export CHECKPOINT_CLIENT_ID="your-client-id"
export CHECKPOINT_CLIENT_SECRET="your-client-secret"
export CHECKPOINT_AVANAN_MCP_URL="https://checkpoint-avanan-mcp.wyre.workers.dev/mcp"
Base URLs
API Gateway
| Environment | Base URL | |-------------|----------| | Production (US) | https://cloudinfra-gw.portal.checkpoint.com | | Production (EU) | https://cloudinfra-gw-eu.portal.checkpoint.com | | Production (AP) | https://cloudinfra-gw-ap.portal.checkpoint.com |
API Paths
| Service | Path Prefix | Description | |---------|-------------|-------------| | Authentication | /auth/external | OAuth2 token endpoint | | Email Security | /app/hec-api/v1.0 | Harmony Email & Collaboration API | | Quarantine | /app/hec-api/v1.0/quarantine | Quarantine operations | | Threats | /app/hec-api/v1.0/threats | Threat detection data | | Policies | /app/hec-api/v1.0/policies | Policy management | | Incidents | /app/hec-api/v1.0/incidents | Incident management |
Region Detection
The API region depends on where the tenant was provisioned. Use the portal URL to determine the correct API gateway:
| Portal URL | API Gateway | |-----------|-------------| | portal.checkpoint.com | cloudinfra-gw.portal.checkpoint.com | | portal.checkpoint.com (EU data) | cloudinfra-gw-eu.portal.checkpoint.com | | portal.checkpoint.com (AP data) | cloudinfra-gw-ap.portal.checkpoint.com |
Request Patterns
Standard GET Request
GET /app/hec-api/v1.0/quarantine?startDate=2024-02-01&endDate=2024-02-15&limit=50
Authorization: Bearer <token>
Content-Type: application/json
Standard POST Request
POST /app/hec-api/v1.0/quarantine/release
Authorization: Bearer <token>
Content-Type: application/json
{
"entityIds": ["qe-abc123", "qe-def456"],
"releaseToRecipients": true,
"addToAllowList": false
}
Query Parameters
| Parameter | Type | Description | Default | |-----------|------|-------------|---------| | startDate | string (ISO 8601) | Start of date range filter | Required for list operations | | endDate | string (ISO 8601) | End of date range filter | Required for list operations | | limit | int | Maximum results per page | 50 | | offset | int | Number of results to skip | 0 | | sortBy | string | Field to sort by | detectedDate | | sortOrder | string | ASC or DESC | DESC |
Pagination
Offset-Based Pagination
GET /app/hec-api/v1.0/threats?startDate=2024-02-01&endDate=2024-02-15&limit=50&offset=0
Response:
{
"data": [...],
"pagination": {
"total": 237,
"limit": 50,
"offset": 0,
"hasMore": true
}
}
Pagination Pattern
async function fetchAllPages(endpoint, params) {
const allItems = [];
let offset = 0;
const limit = 100;
let hasMore = true;
while (hasMore) {
const response = await fetch(`${endpoint}?${new URLSearchParams({
...params,
limit: limit.toString(),
offset: offset.toString()
})}`, {
headers: { 'Authorization': `Bearer ${token}` }
});
const data = await response.json();
allItems.push(...data.data);
hasMore = data.pagination.hasMore;
offset += limit;
}
return allItems;
}
Pagination Limits
| Parameter | Maximum | Notes | |-----------|---------|-------| | limit | 200 | Results per page | | Date range | 90 days | Maximum date range per query | | Total results | 10,000 | Maximum results per query (paginate within this) |
Rate Limiting
Rate Limit Thresholds
| Limit Type | Value | Scope | |-----------|-------|-------| | Requests per minute | 60 | Per API key | | Requests per hour | 1,000 | Per API key | | Concurrent requests | 5 | Per API key | | Bulk operations per request | 100 | Per batch operation |
Rate Limit Response
When rate limited, the API returns HTTP 429:
HTTP/1.1 429 Too Many Requests
Retry-After: 30
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1708012800
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Retry after 30 seconds."
}
}
Rate Limit Headers
| Header | Description | |--------|-------------| | X-RateLimit-Limit | Maximum requests per window | | X-RateLimit-Remaining | Remaining requests in current window | | X-RateLimit-Reset | Unix timestamp when the window resets | | Retry-After | Seconds to wait before retrying (on 429) |
Retry Strategy
async function requestWithRetry(url, options, maxRetries = 5) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await fetch(url, options);
if (response.status === 429) {
const retryAfter = parseInt(response.headers.get('Retry-After') || '30');
const jitter = Math.random() * 2000;
await sleep(retryAfter * 1000 + jitter);
continue;
}
if (response.status === 401) {
// Token expired - refresh and retry
options.headers['Authorization'] = `Bearer ${await auth.getToken()}`;
continue;
}
return response;
} catch (error) {
if (attempt === maxRetries - 1) throw error;
// Exponential backoff with jitter
const delay = Math.pow(2, attempt) * 1000 + Math.random() * 1000;
await sleep(delay);
}
}
}
Error Handling
HTTP Status Codes
| Code | Meaning | Action | |------|---------|--------| | 200 | Success | Process response | | 201 | Created | Entity created successfully | | 204 | No Content | Operation succeeded, no body | | 400 | Bad Request | Check request format/values | | 401 | Unauthorized | Refresh token or check credenti