WAF API
Manage web application firewall policies, browse managed rulesets, test a policy against requests before saving it, and read firewall analytics.
The web application firewall runs inside your application through the SDK. A WAF policy decides what it inspects, what it blocks and how: managed rulesets, your own rules, allow and deny lists, rate limits, outbound (egress) controls and behavioral scoring. Each policy applies to the buckets and environments you choose, and connected SDKs receive changes without a restart. See WAF policies for building a policy in the app.
Plan: needs a plan that includes the Web application firewall. Listing, reading and deleting existing policies, and reading analytics, keep working if the plan changes; servers stop enforcing policies when the plan no longer includes the firewall. The number of policies depends on the plan. Permissions: any member, or a user or administrator API token, can read policies, rulesets and analytics and run the simulator. Creating, changing and deleting a policy needs an owner or admin, or an administrator API token.
The WAF policy object
-
sid (string, required): The policy’s identifier (
waf_followed by 27 letters and digits). -
accountSid (string, required): The account.
-
description (string | null): A short description.
-
enabled (boolean): Whether the policy is active. Default
true. -
mode (string):
off,detect(record what would be blocked, block nothing) orblock(enforce). Defaultdetect. -
paranoiaLevel (integer): 1 to 4. Higher levels turn on stricter rules, catching more attacks with more false positives. Default
1. -
anomalyThreshold (integer): The total rule score at or above which a request is blocked. Default
5. -
failMode (string): What happens if the firewall itself errors:
open(let the request through, the default) orclosed(refuse it). -
blockResponseSid (string | null): The block response the policy uses, or
nullfor a response of its own. While set,defaultBlockfollows that response. On a write, omit it to keep what the policy has, or sendnullwith adefaultBlockto give the policy its own. -
defaultBlock (object): The response a blocked request gets, which your servers enforce:
status(default403),body(defaultForbidden, no<),contentType(auto,json,problem+jsonortext; omitted meansauto) andheaders(onlyRetry-After,Cache-ControlandRateLimit-*). For a policy that names ablockResponseSid, it is that response’s content and is ignored on a write. -
managedRuleset (object): The managed ruleset the policy uses:
id(defaultapitraffic-core),version(latestfollows new releases; a version number pins one) andenabled. -
managedRulesetStatus (object | null): What the ruleset currently resolves to:
id,version,resolvedVersion,name,enabledandactiveRuleCount(managed rules running after paranoia and tuning). -
disabledRules (string[]): Managed rule ids turned off for this policy.
-
ruleExclusions (object[]): Scoped exceptions for false positives. Each names a
ruleIdorcategory, and can be limited totargets, argument, header or cookienames(case-insensitive) and apathPatternregular expression. -
rules (object[]): Your own rules:
id,category,message,severity(default4),score(default5),paranoia(1 to 4),enabled,action(score,block,passortag; defaultscore),targets,operator,negate,transformsandtags. Acriteriaoperator’squeryuses the search syntax with the request fields the firewall sees, at most 4,096 characters and no more than 256 spaces in a row. A query your SDK can’t evaluate as written is refused with400: one that doesn’t parse, has alikepattern that isn’t a valid regular expression, or that older SDKs can’t parse or read differently (for example a bare value starting withANDorOR, which they cut in two: quote the value). One stored before is kept while unchanged and sent to your servers turned off, with acriteria-rule-sent-disabledwarning. -
lists (object[]): Allow and deny lists:
id,kind(ip,country,asn,userAgentorpath),effect(allowordeny),valuesandmessage. -
rateLimits (object[]): Rate limits.
rate limit
-
id (string): Identifier.
-
by (string): What is counted:
ip,header,path,tagorcomposite, withkeynaming the header or tag. -
pathPattern (string): Only paths matching this regular expression.
-
limit (integer): Requests allowed per window.
-
windowSeconds (integer): The window.
-
mode (string):
distributed(default),strictorlocal. -
scope (string):
instance(default): each server counts on its own.fleet-approx: each server allows its share oflimitacross the servers running the firewall, assuming load is spread evenly. -
status (integer): Response status when the limit is hit. Default
429. -
message (string): Response message.
-
limits (object): Request size limits:
maxUrlLength,maxHeaderCount,maxHeaderValueLength,maxBodyBytes,maxBodyInspectBytes(8192 to 1048576, default 131072),inspectionLimitScore(0 to 1000, default 0),customerPatternBudgetMs(5 to 500, default 25) andallowedMethods. -
geo (object): The headers your edge sets with the client’s country (
countryHeader, for examplecf-ipcountry) and network (asnHeader), for country and ASN lists. A country header is preferred where a request has it; without it, servers look the client IP up in the IP ranges of the policy’s countries (see Block or allow countries). -
egress (object): Outbound controls: what your application may send, and to where.
egress
-
mode (string):
off(default),detectorblock. -
inspectScope (string): Which calls have their payloads inspected:
model(recognized AI providers and vector stores, the default) orall.inspectDestinationsadds hosts (exact, or*.suffix), up to 200. -
allowedDestinations (string[]): When set, calls in
allowScope(modelorall) may only go to these hosts. Up to 200. -
blockedDestinations (string[]): Hosts that are always refused. Up to 200.
-
blockSecrets (boolean): Detect credentials in outbound payloads. Default
true. -
blockBulkPii (boolean): Detect bulk personal data, at
bulkPiiThreshold(default 25) distinct records. Defaultfalse. -
maxBodyBytes (integer): Refuse outbound payloads larger than this. Default 5242880 (5 MB);
0turns the cap off. -
inspectResponses (boolean): Scan model and tool responses for indirect prompt injection (detection only). Default
false. -
failMode (string):
open(default) orclosed. -
behavior (object): Scoring of client behavior for authorization abuse (object-id enumeration, probing, fan-out), evaluated outside the request path.
behavior
-
enabled (boolean): Default
false. -
action (string):
detect(record findings, the default) orblock(deny the client on every server). -
threshold (integer): Score at which a client is denied, 1 to 500. Default 70.
-
windowSeconds (integer): The window each client is scored over, 30 to 86400. Default 300.
-
denySeconds (integer): How long a deny lasts, 30 to 86400. Default 900.
-
enumerationThreshold (integer): Distinct object ids on one endpoint before enumeration scores. Default 30.
-
authFailureThreshold (integer): 401 and 403 responses in the window before authorization failures score. Default 15.
-
notFoundThreshold (integer): 404 responses in the window before id probing scores. Default 40.
-
fanOutThreshold (integer): Distinct endpoints in the window before fan-out scores. Default 40.
-
velocityThreshold (integer): Requests in the window before volume scores. Default 600.
-
sensitiveVelocityThreshold (integer): Requests to sensitive paths before velocity scores. Default 30.
-
sensitivePaths (string[]): Path prefixes treated as sensitive, for example
/v1/admin. Up to 100. -
testCases (object[]): Example requests with the outcome you expect (
expect:blockorallow), up to 50, run by the simulator. Never sent to SDKs. Each hasid,name,expectandrequest(method,url,headers,body,ip). -
buckets (string[]): The buckets the policy applies to, or
["*"]. -
environments (string[]): The environments the policy applies to, or
["*"]. -
configVersion (number): Goes up with every change. Servers report the version they run, so you can see when a change has reached the fleet.
-
warnings (object[]): Present when older SDK versions enforce part of the policy differently, for example IPv6 egress entries that SDK 0.1.0 and 0.1.1 do not enforce. The policy is saved as sent. A
criteria-rule-sent-disabledwarning names an enabled criteria rule, stored before the API checked such rules, that your SDK can’t evaluate as written (for example, it uses a field that isn’t known before your API responds, its query doesn’t parse, alikepattern isn’t a valid regular expression, or older SDKs read it differently): the rule is sent to your servers turned off, although it still reads as enabled. Acriteria-or-runs-as-andwarning names an enabled criteria rule saved withORbetween conditions before OR was refused: it runs as AND (every condition must match). A rule carries at most one of the two. Saving a policy refuses a new or changed criteria rule with either problem; an unchanged one (same query,negateandaction) is kept. Country lookups addcountry-older-sdks(lookups by client IP need@apitraffic/waf2.1.0 or later),country-ranges-too-large(the policy’s countries’ IP ranges are more than a policy can carry, so none are sent) andcountry-ranges-unavailable(the country data is not available yet, and the policy names no country header). -
countryLookup (object): Whether servers can look countries up by client IP (
available), and, when they can, whose data the IP ranges are:provider,providerVersion,licenseandattribution(textandurl, the credit to show with it: “IP Geolocation by DB-IP”,https://db-ip.com). -
createdAt (string, required): When the policy was created (ISO 8601).
List WAF policies
GET https://api.apitraffic.io/v1/accounts/{accountSid}/waf-policies
Returns every WAF policy in the account.
Path parameters
- accountSid (path, string, required): The account identifier.
Response
-
hasMore (boolean, required): Always
false. -
records (object[], required): The policies, as WAF policy objects.
curl https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/waf-policies \
-H "Authorization: Bearer $APITRAFFIC_TOKEN"
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const response = await fetch(`https://api.apitraffic.io/v1/accounts/${accountSid}/waf-policies`, {
headers: { Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}` },
});
const { records } = await response.json();
{
"hasMore": false,
"records": [
{
"sid": "waf_2qR5sT7uV9wX1yZ3aB6cD8eF4gH",
"accountSid": "acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa",
"description": "Acme Store WAF",
"enabled": true,
"mode": "block",
"paranoiaLevel": 1,
"anomalyThreshold": 5,
"failMode": "open",
"defaultBlock": { "status": 403, "body": "Blocked by ApiTraffic WAF" },
"managedRuleset": { "id": "apitraffic-core", "version": "latest", "enabled": true },
"managedRulesetStatus": { "id": "apitraffic-core", "version": "latest", "resolvedVersion": "1.1.2", "name": "ApiTraffic Core Rule Set", "enabled": true, "activeRuleCount": 212 },
"disabledRules": [],
"ruleExclusions": [],
"rules": [],
"lists": [
{ "id": "known-attackers", "kind": "ip", "effect": "deny", "values": ["203.0.113.66"], "message": "Known credential-stuffing source" }
],
"rateLimits": [
{ "id": "login-per-ip", "by": "ip", "pathPattern": "^/v1/auth/login$", "limit": 10, "windowSeconds": 60, "mode": "local", "status": 429, "scope": "instance", "message": "Too many sign-in attempts from this address" }
],
"limits": {},
"geo": {},
"egress": { "mode": "off" },
"behavior": { "enabled": false },
"testCases": [],
"buckets": ["*"],
"environments": ["*"],
"configVersion": 7,
"createdAt": "2026-03-02T16:20:00.000Z"
}
]
}
Get WAF policy
GET https://api.apitraffic.io/v1/accounts/{accountSid}/waf-policies/{wafSid}
Returns one WAF policy.
Path parameters
-
accountSid (path, string, required): The account identifier.
-
wafSid (path, string, required): The policy’s identifier (
waf_…).
Response
curl https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/waf-policies/waf_2qR5sT7uV9wX1yZ3aB6cD8eF4gH \
-H "Authorization: Bearer $APITRAFFIC_TOKEN"
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const wafSid = 'waf_2qR5sT7uV9wX1yZ3aB6cD8eF4gH';
const response = await fetch(`https://api.apitraffic.io/v1/accounts/${accountSid}/waf-policies/${wafSid}`, {
headers: { Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}` },
});
const policy = await response.json();
{
"sid": "waf_2qR5sT7uV9wX1yZ3aB6cD8eF4gH",
"accountSid": "acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa",
"description": "Acme Store WAF",
"enabled": true,
"mode": "block",
"paranoiaLevel": 1,
"anomalyThreshold": 5,
"failMode": "open",
"managedRuleset": { "id": "apitraffic-core", "version": "latest", "enabled": true },
"buckets": ["*"],
"environments": ["*"],
"configVersion": 7,
"createdAt": "2026-03-02T16:20:00.000Z"
}
The example shortens the policy; see List WAF policies for a full one.
Create WAF policy
POST https://api.apitraffic.io/v1/accounts/{accountSid}/waf-policies
Creates a WAF policy. Every field of the WAF policy object except sid, accountSid, managedRulesetStatus, configVersion, warnings, countryLookup and createdAt can be sent; anything you leave out takes its default. Start with mode: "detect", check what it would block in Get firewall overview, then switch to block.
Permissions: owner or admin, or an administrator API token. Limit: past the plan’s policy limit, the API answers 409 with code 2213.
Path parameters
- accountSid (path, string, required): The account identifier.
Body parameters
-
mode (body, string, default detect):
off,detectorblock. -
buckets (body, string[]): Bucket sids, or
["*"]. -
environments (body, string[]): Environment sids, or
["*"]. -
description (body, string | null): A short description.
-
… (body, various): Any other policy field, as described in The WAF policy object. Custom patterns (in rules, lists and exclusions) are checked when you save: they must compile and must not be able to backtrack catastrophically.
Response
The new WAF policy object.
curl -X POST https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/waf-policies \
-H "Authorization: Bearer $APITRAFFIC_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"description": "Acme Store WAF",
"mode": "detect",
"paranoiaLevel": 1,
"anomalyThreshold": 5,
"rateLimits": [
{ "id": "login-per-ip", "by": "ip", "pathPattern": "^/v1/auth/login$", "limit": 10, "windowSeconds": 60, "status": 429 }
],
"buckets": ["k3m9p2x"],
"environments": ["prd7k2"]
}'
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const response = await fetch(`https://api.apitraffic.io/v1/accounts/${accountSid}/waf-policies`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
description: 'Acme Store WAF',
mode: 'detect',
rateLimits: [
{ id: 'login-per-ip', by: 'ip', pathPattern: '^/v1/auth/login$', limit: 10, windowSeconds: 60, status: 429 },
],
buckets: ['k3m9p2x'],
environments: ['prd7k2'],
}),
});
const policy = await response.json();
{
"sid": "waf_2rS6tU8vW1xY3zA5bC7dE9fG2hJ",
"accountSid": "acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa",
"description": "Acme Store WAF",
"enabled": true,
"mode": "detect",
"paranoiaLevel": 1,
"anomalyThreshold": 5,
"failMode": "open",
"defaultBlock": { "status": 403, "body": "Forbidden" },
"managedRuleset": { "id": "apitraffic-core", "version": "latest", "enabled": true },
"rateLimits": [
{ "id": "login-per-ip", "by": "ip", "pathPattern": "^/v1/auth/login$", "limit": 10, "windowSeconds": 60, "mode": "distributed", "status": 429, "scope": "instance" }
],
"buckets": ["k3m9p2x"],
"environments": ["prd7k2"],
"configVersion": 1,
"createdAt": "2026-10-20T12:00:00.000Z"
}
The example shortens the response.
Update WAF policy
PUT https://api.apitraffic.io/v1/accounts/{accountSid}/waf-policies/{wafSid}
Replaces a WAF policy. Send the whole policy, as for Create WAF policy: fields you leave out return to their defaults, except geo and testCases, which keep their values when omitted. The new version reaches connected SDKs without a restart, and configVersion goes up.
Permissions: owner or admin, or an administrator API token.
Path parameters
-
accountSid (path, string, required): The account identifier.
-
wafSid (path, string, required): The policy’s identifier.
Body parameters
The same as Create WAF policy.
Response
The updated WAF policy object.
curl -X PUT https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/waf-policies/waf_2rS6tU8vW1xY3zA5bC7dE9fG2hJ \
-H "Authorization: Bearer $APITRAFFIC_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"description": "Acme Store WAF",
"mode": "block",
"rateLimits": [
{ "id": "login-per-ip", "by": "ip", "pathPattern": "^/v1/auth/login$", "limit": 10, "windowSeconds": 60, "status": 429 }
],
"buckets": ["k3m9p2x"],
"environments": ["prd7k2"]
}'
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const wafSid = 'waf_2rS6tU8vW1xY3zA5bC7dE9fG2hJ';
const current = await (await fetch(`https://api.apitraffic.io/v1/accounts/${accountSid}/waf-policies/${wafSid}`, {
headers: { Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}` },
})).json();
// Send back every field you can set, changing only the mode.
const { sid, accountSid: _account, managedRulesetStatus, configVersion, warnings, countryLookup, createdAt, ...policy } = current;
const response = await fetch(`https://api.apitraffic.io/v1/accounts/${accountSid}/waf-policies/${wafSid}`, {
method: 'PUT',
headers: {
Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ ...policy, mode: 'block' }),
});
const updated = await response.json();
{
"sid": "waf_2rS6tU8vW1xY3zA5bC7dE9fG2hJ",
"accountSid": "acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa",
"description": "Acme Store WAF",
"enabled": true,
"mode": "block",
"buckets": ["k3m9p2x"],
"environments": ["prd7k2"],
"configVersion": 2,
"createdAt": "2026-10-20T12:00:00.000Z"
}
The example shortens the response.
Delete WAF policy
DELETE https://api.apitraffic.io/v1/accounts/{accountSid}/waf-policies/{wafSid}
Deletes a WAF policy. Servers stop enforcing it when they next receive their configuration.
Permissions: owner or admin, or an administrator API token.
Path parameters
-
accountSid (path, string, required): The account identifier.
-
wafSid (path, string, required): The policy’s identifier.
Response
-
sid (string, required): The deleted policy’s identifier.
-
deleted (boolean, required):
true.
curl -X DELETE https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/waf-policies/waf_2rS6tU8vW1xY3zA5bC7dE9fG2hJ \
-H "Authorization: Bearer $APITRAFFIC_TOKEN"
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const wafSid = 'waf_2rS6tU8vW1xY3zA5bC7dE9fG2hJ';
await fetch(`https://api.apitraffic.io/v1/accounts/${accountSid}/waf-policies/${wafSid}`, {
method: 'DELETE',
headers: { Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}` },
});
{
"sid": "waf_2rS6tU8vW1xY3zA5bC7dE9fG2hJ",
"deleted": true
}
Simulate WAF policy
POST https://api.apitraffic.io/v1/accounts/{accountSid}/waf-policies/simulate
Evaluates a policy, saved or not, against up to 250 requests and says what it would do with each. Nothing is saved or sent to servers. Pass baselineWafSid to compare against a saved policy and see which requests a change would newly block or let through. Rate limits are not counted in a simulation.
Plan: needs the Web application firewall.
Path parameters
- accountSid (path, string, required): The account identifier.
Body parameters
-
policy (body, object, required): The policy to evaluate, with the same fields as Create WAF policy.
-
requests (body, object[], required): 1 to 250 requests:
method(defaultGET),url(path and query string, for example/v1/search?q=1),headers,body(raw; JSON is parsed when the content type says so),ip(for IP lists) andid(echoed back). -
baselineWafSid (body, string): A saved policy to compare with.
Response
-
summary (object, required):
evaluated,notEvaluated,blocked,matched(rules matched, below the threshold),allowed,newlyBlockedandnoLongerBlocked(with a baseline, otherwisenull), andtruncated(truewhen the time limit stopped the run early). -
results (object[], required): One entry per evaluated request:
id,method,path,verdict(block,matchorallow),score,threshold,ruleIdandreason(what decided it),status(the block response status),matches(each rule that matched, withruleId,category,message,severity,score,target,data,sourceandaction), and with a baseline,baseline(verdict,score,ruleId) andchanged.
curl -X POST https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/waf-policies/simulate \
-H "Authorization: Bearer $APITRAFFIC_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"policy": { "mode": "block", "paranoiaLevel": 2 },
"baselineWafSid": "waf_2qR5sT7uV9wX1yZ3aB6cD8eF4gH",
"requests": [
{ "id": "traversal", "url": "/v1/products/..%2F..%2F..%2F..%2Fetc%2Fpasswd" },
{ "id": "normal", "url": "/v1/search?q=running+shoes" }
]
}'
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const response = await fetch(`https://api.apitraffic.io/v1/accounts/${accountSid}/waf-policies/simulate`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
policy: { mode: 'block', paranoiaLevel: 2 },
baselineWafSid: 'waf_2qR5sT7uV9wX1yZ3aB6cD8eF4gH',
requests: [
{ id: 'traversal', url: '/v1/products/..%2F..%2F..%2F..%2Fetc%2Fpasswd' },
{ id: 'normal', url: '/v1/search?q=running+shoes' },
],
}),
});
const { summary, results } = await response.json();
{
"summary": {
"evaluated": 2,
"notEvaluated": 0,
"blocked": 1,
"matched": 0,
"allowed": 1,
"newlyBlocked": 0,
"noLongerBlocked": 0,
"truncated": false
},
"results": [
{
"id": "traversal",
"method": "GET",
"path": "/v1/products/..%2F..%2F..%2F..%2Fetc%2Fpasswd",
"verdict": "block",
"score": 10,
"threshold": 5,
"ruleId": "AT-LFI-001",
"reason": "anomaly-threshold",
"status": 403,
"matches": [
{ "ruleId": "AT-LFI-001", "category": "lfi", "message": "Path traversal detected in the request path", "severity": 2, "score": 5, "target": "path", "data": "../", "source": "managed", "action": "score" }
],
"baseline": { "verdict": "block", "score": 10, "ruleId": "AT-LFI-001" },
"changed": false
},
{
"id": "normal",
"method": "GET",
"path": "/v1/search",
"verdict": "allow",
"score": 0,
"threshold": 5,
"ruleId": null,
"reason": null,
"status": null,
"matches": [],
"baseline": { "verdict": "allow", "score": 0, "ruleId": null },
"changed": false
}
]
}
List managed rulesets
GET https://api.apitraffic.io/v1/accounts/{accountSid}/waf-rulesets
Returns the managed rulesets a policy can use.
Plan: needs the Web application firewall.
Path parameters
- accountSid (path, string, required): The account identifier.
Response
-
hasMore (boolean, required): Always
false. -
records (object[], required): One entry per ruleset:
id,version,name,description,released,total(rules),byCategoryandbyParanoia(rule counts).
curl https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/waf-rulesets \
-H "Authorization: Bearer $APITRAFFIC_TOKEN"
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const response = await fetch(`https://api.apitraffic.io/v1/accounts/${accountSid}/waf-rulesets`, {
headers: { Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}` },
});
const { records } = await response.json();
{
"hasMore": false,
"records": [
{
"id": "apitraffic-core",
"version": "1.1.2",
"name": "ApiTraffic Core Rule Set",
"description": "Managed protection for the OWASP Top 10 injection families: SQLi, XSS, OS command execution, path traversal/LFI/RFI, SSRF, template injection, NoSQL injection, XXE, plus scanner, probe and protocol hygiene rules.",
"released": "2026-10-07",
"total": 240,
"byCategory": { "sqli": 48, "xss": 41, "lfi": 22 },
"byParanoia": { "1": 140, "2": 62, "3": 28, "4": 10 }
}
]
}
Get managed ruleset
GET https://api.apitraffic.io/v1/accounts/{accountSid}/waf-rulesets/{rulesetId}
Returns a managed ruleset with every rule in it, so you can choose rules to turn off or exclude.
Plan: needs the Web application firewall.
Path parameters
-
accountSid (path, string, required): The account identifier.
-
rulesetId (path, string, required): The ruleset id, for example
apitraffic-core.
Query parameters
- version (query, string): The version. Defaults to the newest.
Response
The ruleset’s fields, as in List managed rulesets, plus rules: each with id, category, message, severity, score, paranoia, action, targets and tags.
curl https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/waf-rulesets/apitraffic-core \
-H "Authorization: Bearer $APITRAFFIC_TOKEN"
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const response = await fetch(`https://api.apitraffic.io/v1/accounts/${accountSid}/waf-rulesets/apitraffic-core`, {
headers: { Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}` },
});
const ruleset = await response.json();
{
"id": "apitraffic-core",
"version": "1.1.2",
"name": "ApiTraffic Core Rule Set",
"description": "Managed protection for the OWASP Top 10 injection families: SQLi, XSS, OS command execution, path traversal/LFI/RFI, SSRF, template injection, NoSQL injection, XXE, plus scanner, probe and protocol hygiene rules.",
"released": "2026-10-07",
"total": 240,
"byCategory": { "sqli": 48, "xss": 41, "lfi": 22 },
"byParanoia": { "1": 140, "2": 62, "3": 28, "4": 10 },
"rules": [
{ "id": "AT-LFI-001", "category": "lfi", "message": "Path traversal detected in the request path", "severity": 2, "score": 5, "paranoia": 1, "action": "score", "targets": ["path", "args"], "tags": [] }
]
}
The examples are shortened, and the rule counts are illustrative.
Firewall analytics filters
The four analytics operations below take the same filters:
-
startTime (query, string, required): Start of the range (ISO 8601, UTC, ending in
Z). -
endTime (query, string, required): End of the range.
-
environmentSid (query, string): Only this environment.
-
bucketSid (query, string): Only this bucket.
-
policySid (query, string): Only detections made by this policy (the policy that actually ran).
-
ruleId (query, string): Only detections this rule contributed to.
-
category (query, string): Only detections whose deciding rule is in this category.
-
action (query, string):
tag(matched below the threshold),would-block(would have been blocked indetectmode) orblock. -
endpoint (query, string): Only this endpoint. Also narrows the inspected traffic, so
matchRatestays a share of that endpoint’s own requests.
Get firewall overview
GET https://api.apitraffic.io/v1/accounts/{accountSid}/waf-analytics/overview
Returns firewall totals, a time series, the score distribution and the busiest rules.
Path parameters
- accountSid (path, string, required): The account identifier.
Query parameters
The analytics filters, plus:
- ruleLimit (query, integer, default 10):
Rules in
rules, 1 to 1000.
Response
-
summary (object, required):
matchedRequests,blockedRequests(actually refused),wouldBlockRequests(would have been, if enforcing),taggedRequests(matched below the threshold),ruleMatches,distinctRules,distinctCategories,distinctEndpoints,averageScore,maxScore,p95Score,firstSeen,lastSeen,inspectedRequests(all inbound requests in the range),matchRate(percent of inspected requests that matched) andenforcementRate(percent of matches that blocked or would have). -
timeseries (object[], required): Per period:
bucket, the request counts above,ruleMatches,averageScoreandmaxScore. -
scores (object[], required): The score distribution:
score,matchedRequests,blockedRequestsandwouldBlockRequests. -
rules (object[], required): The busiest rules, as in List firewall rule hits.
-
degraded (boolean): Present and
truewhen part of the answer could not be read;failednames the parts (timeseries,scores,rules).
curl -G https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/waf-analytics/overview \
-H "Authorization: Bearer $APITRAFFIC_TOKEN" \
--data-urlencode 'startTime=2026-10-19T00:00:00.000Z' \
--data-urlencode 'endTime=2026-10-20T00:00:00.000Z'
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const params = new URLSearchParams({ startTime: '2026-10-19T00:00:00.000Z', endTime: '2026-10-20T00:00:00.000Z' });
const response = await fetch(`https://api.apitraffic.io/v1/accounts/${accountSid}/waf-analytics/overview?${params}`, {
headers: { Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}` },
});
const { summary } = await response.json();
{
"summary": {
"matchedRequests": 318,
"blockedRequests": 296,
"wouldBlockRequests": 0,
"taggedRequests": 22,
"ruleMatches": 604,
"distinctRules": 19,
"distinctCategories": 5,
"distinctEndpoints": 7,
"averageScore": 9.4,
"maxScore": 35,
"p95Score": 20,
"firstSeen": "2026-10-19T00:12:40.000Z",
"lastSeen": "2026-10-19T23:51:07.000Z",
"inspectedRequests": 10412,
"matchRate": 3.05,
"enforcementRate": 93.08
},
"timeseries": [],
"scores": [],
"rules": []
}
List firewall rule hits
GET https://api.apitraffic.io/v1/accounts/{accountSid}/waf-analytics/rules
Ranks rules by the requests they fired on, with how often each one decided the outcome. Use it to find rules worth tuning.
Path parameters
- accountSid (path, string, required): The account identifier.
Query parameters
The analytics filters, plus limit (1 to 1000, default 50).
Response
- points (object[], required):
One entry per rule:
ruleId,category,message,severity,paranoia,ruleScore(score per hit),ruleAction,matchedRequests,blockedRequests,wouldBlockRequests,taggedRequests,decidedRequests(requests where this rule decided the outcome),averageScore,maxScore,distinctEndpoints,firstSeenandlastSeen.
curl -G https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/waf-analytics/rules \
-H "Authorization: Bearer $APITRAFFIC_TOKEN" \
--data-urlencode 'startTime=2026-10-19T00:00:00.000Z' \
--data-urlencode 'endTime=2026-10-20T00:00:00.000Z' \
--data-urlencode 'limit=10'
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const params = new URLSearchParams({ startTime: '2026-10-19T00:00:00.000Z', endTime: '2026-10-20T00:00:00.000Z', limit: '10' });
const response = await fetch(`https://api.apitraffic.io/v1/accounts/${accountSid}/waf-analytics/rules?${params}`, {
headers: { Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}` },
});
const { points } = await response.json();
{
"points": [
{
"ruleId": "AT-LFI-001",
"category": "lfi",
"message": "Path traversal detected in the request path",
"severity": 2,
"paranoia": 1,
"ruleScore": 5,
"ruleAction": "score",
"matchedRequests": 74,
"blockedRequests": 74,
"wouldBlockRequests": 0,
"taggedRequests": 0,
"decidedRequests": 61,
"averageScore": 12.3,
"maxScore": 20,
"distinctEndpoints": 2,
"firstSeen": "2026-10-19T00:31:02.000Z",
"lastSeen": "2026-10-19T23:40:19.000Z"
}
]
}
Get firewall breakdown
GET https://api.apitraffic.io/v1/accounts/{accountSid}/waf-analytics/breakdown
Groups firewall matches by one dimension, such as category, endpoint or policy.
Path parameters
- accountSid (path, string, required): The account identifier.
Query parameters
The analytics filters, plus:
-
dimension (query, string, required):
category,action,reason,severity,mode,policy,endpoint,path,method,statusCode,bucketSidorenvironmentSid. -
limit (query, integer, default 25): Groups to return, 1 to 1000.
Response
-
dimension (string, required): The dimension used.
-
points (object[], required): One entry per group:
key,matchedRequests,blockedRequests,wouldBlockRequests,taggedRequests,distinctRulesandlastSeen.
curl -G https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/waf-analytics/breakdown \
-H "Authorization: Bearer $APITRAFFIC_TOKEN" \
--data-urlencode 'dimension=reason' \
--data-urlencode 'startTime=2026-10-19T00:00:00.000Z' \
--data-urlencode 'endTime=2026-10-20T00:00:00.000Z'
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const params = new URLSearchParams({
dimension: 'reason',
startTime: '2026-10-19T00:00:00.000Z',
endTime: '2026-10-20T00:00:00.000Z',
});
const response = await fetch(`https://api.apitraffic.io/v1/accounts/${accountSid}/waf-analytics/breakdown?${params}`, {
headers: { Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}` },
});
const { points } = await response.json();
{
"dimension": "reason",
"points": [
{ "key": "anomaly-threshold", "matchedRequests": 211, "blockedRequests": 189, "wouldBlockRequests": 0, "taggedRequests": 22, "distinctRules": 17, "lastSeen": "2026-10-19T23:51:07.000Z" },
{ "key": "rate-limit", "matchedRequests": 84, "blockedRequests": 84, "wouldBlockRequests": 0, "taggedRequests": 0, "distinctRules": 1, "lastSeen": "2026-10-19T03:24:55.000Z" },
{ "key": "ip-deny", "matchedRequests": 23, "blockedRequests": 23, "wouldBlockRequests": 0, "taggedRequests": 0, "distinctRules": 1, "lastSeen": "2026-10-19T03:22:10.000Z" }
]
}
List firewall matches
GET https://api.apitraffic.io/v1/accounts/{accountSid}/waf-analytics/matches
Returns the matched requests themselves, newest first, so you can open them with Get request.
Path parameters
- accountSid (path, string, required): The account identifier.
Query parameters
The analytics filters, plus limit (1 to 1000, default 50).
Response
- points (object[], required):
One entry per request:
dt,sid(the request),method,path,endpoint,statusCode,durationMs,score,action,reason,ruleId(the deciding rule),category,ruleIds(every rule that contributed),bucketSidandenvironmentSid.
curl -G https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/waf-analytics/matches \
-H "Authorization: Bearer $APITRAFFIC_TOKEN" \
--data-urlencode 'action=block' \
--data-urlencode 'startTime=2026-10-19T03:00:00.000Z' \
--data-urlencode 'endTime=2026-10-19T04:00:00.000Z'
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const params = new URLSearchParams({
action: 'block',
startTime: '2026-10-19T03:00:00.000Z',
endTime: '2026-10-19T04:00:00.000Z',
});
const response = await fetch(`https://api.apitraffic.io/v1/accounts/${accountSid}/waf-analytics/matches?${params}`, {
headers: { Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}` },
});
const { points } = await response.json();
{
"points": [
{
"dt": "2026-10-19T03:21:44.000Z",
"sid": "req_2lM7nP9qR2sT4uV6wX8yZ1aB3cD",
"method": "POST",
"path": "/v1/auth/login",
"endpoint": "/v1/auth/login",
"statusCode": 429,
"durationMs": 1,
"score": 0,
"action": "block",
"reason": "rate-limit",
"ruleId": "login-per-ip",
"category": "rate-limit",
"ruleIds": ["login-per-ip"],
"bucketSid": "k3m9p2x",
"environmentSid": "prd7k2"
}
]
}
Block responses
A block response is what a request blocked by a policy receives, shared by the policies that name it with blockResponseSid. Changing one updates and pushes every policy that uses it.
GET https://api.apitraffic.io/v1/accounts/{accountSid}/waf-block-responses
POST https://api.apitraffic.io/v1/accounts/{accountSid}/waf-block-responses
GET https://api.apitraffic.io/v1/accounts/{accountSid}/waf-block-responses/{blockResponseSid}
PUT https://api.apitraffic.io/v1/accounts/{accountSid}/waf-block-responses/{blockResponseSid}
DELETE https://api.apitraffic.io/v1/accounts/{accountSid}/waf-block-responses/{blockResponseSid}
Permissions: reading needs any account role; creating, changing and deleting need owner or admin, or an administrator API token. Creating and changing need a plan that includes the firewall; reading and deleting do not.
The block response object
-
sid (string, required): The block response’s identifier (
wbr_…). -
name (string, required): A name to pick it by, up to 100 characters.
-
description (string): What it is for, up to 500 characters.
-
status (integer, required): The HTTP status, 200 to 599 except
204,205and304(default403). -
contentType (string, required):
auto(JSON when the body is a JSON object or array, else plain text),json,problem+json(RFC 9457) ortext. A JSON type needs a body of that shape. -
body (string, required): 1 to 4,096 bytes, without
<. Each{context_id}is replaced, as a block is sent, with the blocked request’s context id (ctx_…, 31 characters, counted towards the limit), which finds the request withcontext_sid == ctx_…. -
headers (object, required): Only
Retry-After,Cache-ControlandRateLimit-*, each once, with a value. -
usedBy (object[], required): The policies that use it:
sidanddescription. -
createdAt (string, required): When it was created.
-
updatedAt (string | null, required): When it last changed. Send it back as
expectedUpdatedAton an update to have the update refused if someone changed the response since.
A create takes name, description, status, contentType, body and headers. An update (PUT) changes only the fields it sends, plus the optional expectedUpdatedAt. A delete is refused while a policy uses the response, and answers sid and deleted.
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
1000 |
A field is missing or invalid, for example an unknown mode, a custom pattern that is unsafe, or more than 250 requests to simulate. The message names the field. |
403 |
2010 |
Creating, changing or deleting a policy without owner, admin or administrator access. |
403 |
2221 |
The plan does not include the firewall. |
403 |
2509 |
Changing a policy for the read-only sample bucket. |
404 |
4000 |
No policy or ruleset with that id. |
409 |
2213 |
The plan’s WAF policy limit is reached. |
400 |
2684 |
A policy names a block response that does not exist (or was deleted meanwhile). Pick another one. |
409 |
2680 |
A block response in use cannot be deleted; data.usedBy lists the policies. Also returned when policies are moving to and from it at that moment: try again. |
409 |
2682 |
The account keeps the most block responses it can (100). |
409 |
2683 |
A block response update was refused: it changed since expectedUpdatedAt (data.updatedAt is the stored version), or other saves kept changing it. Load it again and retry. |
503 |
2681 |
A block response was saved, but not every policy using it reached your servers. Save it again (with data.updatedAt as expectedUpdatedAt) to retry. |
503, 504 |
5101 to 5104 |
Analytics could not be read right now. Retry after Retry-After seconds. |
Related
-
Firewall policies: Build and tune a policy in the app.
-
Set up the firewall: Turn on enforcement in your SDK.