FortressWAF provides a comprehensive REST API for integration and automation. This document details all available endpoints, authentication methods, and usage examples.
Production: https://api.fortresswaf.io/v1
Staging: https://api-staging.fortresswaf.io/v1
Local: https://localhost:8444/v1
Include your API key in the X-API-Key header:
curl -H "X-API-Key: your-api-key" \
https://api.fortresswaf.io/v1/sitesObtain a JWT token and include it in the Authorization header:
# Login to get JWT
curl -X POST https://api.fortresswaf.io/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "admin", "password": "your-password"}'
# Response:
# {
# "access_token": "eyJhbGciOiJIUzI1NiIs...",
# "token_type": "Bearer",
# "expires_in": 3600
# }
# Use JWT token
curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
https://api.fortresswaf.io/v1/sitesAPI requests are rate limited:
| Plan | Requests/minute | Burst |
|---|---|---|
| Free | 60 | 10 |
| Pro | 600 | 100 |
| Enterprise | 6000 | 1000 |
Rate limit headers are included in responses:
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 599
X-RateLimit-Reset: 1640995200
List endpoints support pagination:
# Get first page (default 20 items)
GET /v1/sites
# Get specific page
GET /v1/sites?page=2&per_page=50
# Response includes pagination metadata:
{
"data": [...],
"meta": {
"current_page": 1,
"per_page": 20,
"total": 100,
"total_pages": 5
}
}| Code | Description |
|---|---|
| 400 | Bad Request - Invalid parameters |
| 401 | Unauthorized - Invalid or missing credentials |
| 403 | Forbidden - Insufficient permissions |
| 404 | Not Found - Resource doesn't exist |
| 409 | Conflict - Resource already exists |
| 422 | Unprocessable Entity - Validation error |
| 429 | Too Many Requests - Rate limit exceeded |
| 500 | Internal Server Error |
| 503 | Service Unavailable |
Error response format:
{
"error": {
"code": "validation_error",
"message": "Invalid request parameters",
"details": [
{"field": "domain", "message": "Domain format is invalid"}
]
}
}Login and obtain JWT token.
Request:
{
"username": "admin",
"password": "your-password"
}Response (200):
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "eyJhbGciOiJIUzI1NiIs..."
}Refresh access token.
Request:
{
"refresh_token": "eyJhbGciOiJIUzI1NiIs..."
}Response (200):
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600
}Invalidate current token.
Response (204): No content
Change user password.
Request:
{
"current_password": "old-password",
"new_password": "new-secure-password"
}Response (200):
{
"message": "Password changed successfully"
}List all sites.
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
| page | int | Page number (default: 1) |
| per_page | int | Items per page (default: 20, max: 100) |
| sort | string | Sort field (name, created_at) |
| order | string | Sort order (asc, desc) |
| filter | string | Filter by status (active, paused, deleted) |
Response (200):
{
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "production-app",
"domain": "app.example.com",
"backend_url": "https://internal.example.com",
"status": "active",
"tls_enabled": true,
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-15T12:30:00Z"
}
],
"meta": {
"current_page": 1,
"per_page": 20,
"total": 5,
"total_pages": 1
}
}Create a new site.
Request:
{
"name": "production-app",
"domain": "app.example.com",
"backend_url": "https://internal.example.com",
"backend_host_header": "app.example.com",
"tls_mode": "terminate",
"tls_cert_id": "cert-uuid",
"health_check_url": "/health",
"health_check_interval": 10,
"health_check_timeout": 5,
"is_active": true,
"tags": {
"environment": "production",
"team": "platform"
}
}Response (201):
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "production-app",
"domain": "app.example.com",
"backend_url": "https://internal.example.com",
"status": "active",
"created_at": "2024-01-01T00:00:00Z"
}Get site details.
Response (200):
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "production-app",
"domain": "app.example.com",
"backend_url": "https://internal.example.com",
"backend_host_header": "app.example.com",
"tls_mode": "terminate",
"tls_cert_id": "cert-uuid",
"health_check_url": "/health",
"health_check_interval": 10,
"health_check_timeout": 5,
"is_active": true,
"stats": {
"requests_today": 1500000,
"blocked_today": 1234,
"avg_latency_ms": 15
},
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-15T12:30:00Z"
}Update a site.
Request:
{
"name": "updated-app",
"backend_url": "https://new-backend.example.com",
"is_active": true
}Response (200): Updated site object
Delete a site.
Response (204): No content
Pause site protection.
Response (200):
{
"status": "paused"
}Resume site protection.
Response (200):
{
"status": "active"
}List rules for a site.
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
| page | int | Page number |
| per_page | int | Items per page |
| enabled | bool | Filter by enabled status |
| tag | string | Filter by tag |
Response (200):
{
"data": [
{
"id": "rule-uuid",
"name": "Block SQL Injection",
"priority": 10,
"enabled": true,
"condition": {...},
"action": {...},
"match_count": 1234,
"block_count": 100,
"last_matched": "2024-01-15T12:30:00Z",
"created_at": "2024-01-01T00:00:00Z"
}
],
"meta": {...}
}Create a new rule.
Request:
{
"name": "Block SQL Injection",
"description": "Blocks SQL injection attempts",
"priority": 10,
"enabled": true,
"condition": {
"any": [
{"request_query_sql_injection_score": {"gt": 0.75}},
{"request_body_sql_injection_score": {"gt": 0.75}}
]
},
"action": {
"type": "block",
"status": 403,
"body": "SQL injection detected"
},
"tags": ["sql-injection", "owasp"]
}Response (201): Created rule object
Get rule details.
Response (200): Rule object
Update a rule.
Request: Same as POST
Response (200): Updated rule object
Delete a rule.
Response (204): No content
Validate rule syntax without creating.
Request:
{
"condition": {...},
"action": {...}
}Response (200):
{
"valid": true,
"warnings": []
}Test rule against sample requests.
Request:
{
"rule_id": "rule-uuid",
"test_cases": [
{
"name": "SQL injection test",
"request": {
"method": "GET",
"path": "/search",
"query": "id=1' OR '1'='1"
},
"expected": "match"
}
]
}Response (200):
{
"results": [
{
"name": "SQL injection test",
"matched": true,
"actual": "match"
}
]
}Get rate limit configuration.
Response (200):
{
"global": {
"enabled": true,
"requests_per_minute": 10000,
"burst": 500,
"algorithm": "token_bucket"
},
"per_ip": {
"enabled": true,
"requests_per_minute": 100,
"burst": 20
},
"endpoint_limits": [
{
"path": "/api/auth/login",
"method": "POST",
"requests_per_minute": 5,
"burst": 2
}
]
}Update rate limit configuration.
Request:
{
"global": {
"enabled": true,
"requests_per_minute": 20000,
"burst": 1000
}
}Response (200): Updated rate limit configuration
Add IP exemption.
Request:
{
"ip": "10.0.0.0/8",
"requests_per_minute": 1000000,
"expires_at": "2025-12-31T23:59:59Z"
}Response (201): Created exemption
List all IP lists.
Response (200):
{
"data": [
{
"id": "list-uuid",
"name": "blocked-ips",
"type": "block",
"ip_count": 150,
"created_at": "2024-01-01T00:00:00Z"
}
]
}Create IP list.
Request:
{
"name": "blocked-ips",
"description": "Blocked IP addresses",
"type": "block"
}Response (201): Created IP list
Add IPs to list.
Request:
{
"ips": ["1.2.3.4/32", "5.6.7.0/24"]
}Response (200):
{
"added": 2,
"total": 152
}Remove IPs from list.
Request:
{
"ips": ["1.2.3.4/32"]
}Response (200):
{
"removed": 1,
"total": 151
}List certificates.
Response (200):
{
"data": [
{
"id": "cert-uuid",
"name": "example.com",
"common_name": "example.com",
"expires_at": "2025-01-01T00:00:00Z",
"issuer": "Let's Encrypt",
"created_at": "2024-01-01T00:00:00Z"
}
]
}Upload certificate.
Request (multipart/form-data):
cert: <file>
key: <file>
name: example-com
passphrase: <optional>
Response (201): Created certificate
Delete certificate.
Response (204): No content
List API keys for site.
Response (200):
{
"data": [
{
"id": "key-uuid",
"name": "CI/CD Key",
"key_hint": "fw_prod_abc123...xyz",
"permissions": ["read", "write"],
"last_used": "2024-01-15T12:30:00Z",
"expires_at": "2025-12-31T23:59:59Z",
"created_at": "2024-01-01T00:00:00Z"
}
]
}Create API key.
Request:
{
"name": "CI/CD Key",
"permissions": ["read", "write"],
"expires_at": "2025-12-31T23:59:59Z",
"tags": {"team": "devops"}
}Response (201):
{
"id": "key-uuid",
"name": "CI/CD Key",
"key": "fw_prod_abc123...xyz789",
"permissions": ["read", "write"],
"expires_at": "2025-12-31T23:59:59Z"
}Revoke API key.
Response (204): No content
List virtual patches.
Response (200):
{
"data": [
{
"id": "patch-uuid",
"name": "CVE-2024-1234 Patch",
"cve_id": "CVE-2024-1234",
"priority": 1,
"enabled": true,
"status": "active",
"blocked_attempts": 1234,
"expires_at": "2025-01-01T00:00:00Z",
"created_at": "2024-01-01T00:00:00Z"
}
]
}Create virtual patch.
Request:
{
"name": "CVE-2024-1234 Patch",
"cve_id": "CVE-2024-1234",
"priority": 1,
"enabled": true,
"expires_at": "2025-12-31T23:59:59Z",
"condition": {
"all": [
{"request_path": {"prefix": "/api/vulnerable"}},
{"request_query_sql_injection_score": {"gt": 0.8}}
]
},
"action": {
"type": "block",
"status": 403
}
}Response (201): Created patch
Import patches from vulnerability scanner.
Request:
{
"scanner": "nessus",
"content": "<nessus report>...</nessus>"
}Response (200):
{
"imported": 5,
"skipped": 2,
"errors": []
}Get site statistics.
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
| period | string | Period (1h, 24h, 7d, 30d, custom) |
| start | string | Start datetime (ISO 8601) |
| end | string | End datetime (ISO 8601) |
Response (200):
{
"period": {
"start": "2024-01-14T00:00:00Z",
"end": "2024-01-15T00:00:00Z"
},
"requests": {
"total": 1500000,
"allowed": 1498766,
"blocked": 1234
},
"latency": {
"p50_ms": 10,
"p95_ms": 25,
"p99_ms": 50
},
"bandwidth": {
"bytes_in": 5000000000,
"bytes_out": 15000000000
},
"top_threats": [
{"type": "sql_injection", "count": 500},
{"type": "xss", "count": 300}
]
}Get real-time statistics (WebSocket recommended for live updates).
Response (200):
{
"requests_per_second": 150,
"active_connections": 450,
"blocked_per_minute": 12,
"avg_latency_ms": 15,
"cpu_usage": 0.35,
"memory_usage": 0.6
}Get attack analytics.
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
| period | string | Period |
| group_by | string | Group by (type, ip, path, country) |
Response (200):
{
"data": [
{
"type": "sql_injection",
"count": 500,
"blocked": 450,
"top_ips": [
{"ip": "1.2.3.4", "count": 50}
]
}
]
}Query audit events.
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
| page | int | Page number |
| per_page | int | Items per page |
| start | string | Start time (ISO 8601) |
| end | string | End time |
| action | string | Filter by action (block, allow, challenge) |
| ip | string | Filter by client IP |
| rule_id | string | Filter by rule |
| request_id | string | Filter by request ID |
Response (200):
{
"data": [
{
"id": "event-uuid",
"timestamp": "2024-01-15T12:30:00Z",
"request_id": "req-uuid",
"client_ip": "1.2.3.4",
"request_method": "GET",
"request_path": "/search",
"action": "block",
"rule_id": "rule-uuid",
"ml_score": 0.85,
"bot_score": 0.2
}
],
"meta": {...}
}Get event details.
Response (200):
{
"id": "event-uuid",
"timestamp": "2024-01-15T12:30:00Z",
"request": {
"id": "req-uuid",
"method": "GET",
"path": "/search",
"query": "id=1' OR '1'='1",
"headers": {...},
"body": null,
"size": 256
},
"response": {
"status": 403,
"size": 128
},
"decision": {
"action": "block",
"reason": "rule_match",
"rule_id": "rule-uuid",
"ml_score": 0.85,
"bot_score": 0.2
},
"client": {
"ip": "1.2.3.4",
"country": "US",
"asn": 15169,
"is_tor": false,
"is_vpn": false
}
}List webhooks.
Response (200):
{
"data": [
{
"id": "webhook-uuid",
"url": "https://example.com/webhook",
"events": ["attack.blocked", "attack.challenge_failed"],
"secret": "whsec_...",
"enabled": true,
"created_at": "2024-01-01T00:00:00Z"
}
]
}Create webhook.
Request:
{
"name": "Security Webhook",
"url": "https://example.com/webhook",
"events": ["attack.blocked", "attack.detected"],
"secret": "your-webhook-secret"
}Response (201): Created webhook
Delete webhook.
Response (204): No content
Health check.
Response (200):
{
"status": "healthy",
"version": "2.0.0",
"timestamp": "2024-01-15T12:30:00Z"
}Get version information.
Response (200):
{
"version": "2.0.0",
"build": "abc123",
"commit": "def456",
"release_date": "2024-01-01T00:00:00Z"
}Prometheus metrics.
Response (200): Prometheus text format
curl -X POST https://api.fortresswaf.io/v1/sites \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "my-app",
"domain": "app.example.com",
"backend_url": "https://internal.example.com",
"is_active": true
}'curl -X POST https://api.fortresswaf.io/v1/certificates \
-H "Authorization: Bearer $TOKEN" \
-F "cert=@/path/to/cert.pem" \
-F "key=@/path/to/key.pem" \
-F "name=example-com"curl -X POST https://api.fortresswaf.io/v1/sites/$SITE_ID/rules \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Block SQL Injection",
"priority": 10,
"enabled": true,
"condition": {
"any": [
{"request_query_sql_injection_score": {"gt": 0.75}}
]
},
"action": {
"type": "block",
"status": 403
}
}'curl "https://api.fortresswaf.io/v1/sites/$SITE_ID/stats/attacks?period=24h&group_by=type" \
-H "Authorization: Bearer $TOKEN"curl "https://api.fortresswaf.io/v1/sites/$SITE_ID/events?action=block&limit=50" \
-H "Authorization: Bearer $TOKEN"Webhook payloads for real-time notifications:
{
"event": "attack.blocked",
"timestamp": "2024-01-15T12:30:00Z",
"site_id": "site-uuid",
"data": {
"request_id": "req-uuid",
"attack_type": "sql_injection",
"client_ip": "1.2.3.4",
"path": "/search",
"action": "block",
"ml_score": 0.85
}
}{
"event": "attack.detected",
"timestamp": "2024-01-15T12:30:00Z",
"site_id": "site-uuid",
"data": {
"request_id": "req-uuid",
"attack_type": "sql_injection",
"client_ip": "1.2.3.4",
"action": "challenge",
"ml_score": 0.65
}
}{
"event": "bot.detected",
"timestamp": "2024-01-15T12:30:00Z",
"site_id": "site-uuid",
"data": {
"request_id": "req-uuid",
"client_ip": "1.2.3.4",
"bot_score": 0.9,
"bot_type": "headless_browser"
}
}{
"event": "rate_limit.exceeded",
"timestamp": "2024-01-15T12:30:00Z",
"site_id": "site-uuid",
"data": {
"request_id": "req-uuid",
"client_ip": "1.2.3.4",
"limit_type": "per_ip",
"requests_made": 150,
"limit": 100
}
}