What the API is for
The PortWall API lets a script do what you do in the console: check that protection is working, list your sites, switch a site between watching and blocking, block or allowlist addresses, export attacks and back up the configuration. It is the same API the console itself uses. Every endpoint, with the role it needs and every field it takes and returns, is in the API reference.
Every example on this page was run against a PortWall server before it was published. The examples use PowerShell, which every Windows server has; curl works too.
Where the API answers
- On Windows, the API answers only on the server itself, at
http://127.0.0.1:5000/api/. Run your scripts on the PortWall server, for example from Task Scheduler. Nothing outside the server can reach it. - On the Linux appliance, scripts on the appliance use the same address. From another machine, use the console's
address,
https://<appliance address>:9443/api/. The console's certificate is self-signed, so the script must trust it: compare its fingerprint with the one the appliance shows on its screen.
Getting a key
- In the console, open API Keys.
- Give the key a name that says what it is for (for example
nightly-backup), and pick a role:- Viewer reads everything and changes nothing. Use it for monitoring and reports.
- Admin can also change settings, block addresses and export the configuration.
- Click Create key and copy the key straight away. It is shown once only.
Use one key per script, so you can revoke one without breaking the others. See API keys for scripts and integrations for revoking and rotating keys.
Starting a script
Every example below starts from these two lines. Replace the key with yours:
$api = 'http://127.0.0.1:5000/api'
$headers = @{ 'X-Api-Key' = 'pwk_your_key_here' }
Run the examples in Windows PowerShell or PowerShell 7. Keep the key out of shared scripts: read it from a file only
Administrators can open, for example $headers = @{ 'X-Api-Key' = (Get-Content C:\scripts\portwall-key.txt -Raw).Trim() }.
In Windows PowerShell, put a list the API returns into a variable before you pipe it, as the examples do. Piping
Invoke-RestMethod straight into Select-Object passes the whole list as one item.
Check that protection is working
Needs a Viewer key.
$status = Invoke-RestMethod "$api/protection-status" -Headers $headers
$status.allHealthy
$status.summary
$status.problems
$status.features | Select-Object name, enabled, enforcing, severity
allHealthy is True when every protection that is turned on is enforcing. summary says the same in a sentence,
for example 11 protections enabled and enforcing. problems lists anything that is on but not working, with the fix.
These are the same checks as the Overview's Protection status cards, so a monitoring system can alert on
allHealthy being False.
Check the licence
Needs a Viewer key.
$licence = Invoke-RestMethod "$api/license" -Headers $headers
$licence | Select-Object state, tier, enforcementActive
state is Licensed, Grace or Unlicensed; enforcementActive is False when blocking is disarmed. See
Licences, editions and licence states.
List your sites and their modes
Needs a Viewer key.
$sites = Invoke-RestMethod "$api/sites/all" -Headers $headers
$sites | Select-Object siteName, mode
mode is one of:
| Value | In the console |
|---|---|
Off |
Off |
DetectionOnly |
Detection only: attacks are logged, nothing is blocked |
On |
Blocking |
Switch a site to blocking
Needs an Admin key. This puts one or more sites in a mode and waits until the change is in force:
$body = @{ mode = 'On'; sites = @('shop.example.com') } | ConvertTo-Json
$result = Invoke-RestMethod "$api/sites/mode" -Method Post -Headers $headers -ContentType 'application/json' -Body $body
if ($result.applying) {
do {
Start-Sleep -Seconds 2
$apply = Invoke-RestMethod "$api/config-apply/status" -Headers $headers
} while ($apply.completed -lt $result.applySeq)
$apply.outcome
}
Use the site names from the list above. The reply says how many sites changed; when nothing needed changing it does
not apply anything, and the script skips the wait. The change is applied in the background, usually within seconds,
and outcome is applied when it is in force. Use DetectionOnly to go back to watching. The change is recorded in
the audit log, as a change made in the console would be.
Block an address
Needs an Admin key. To block an address for a while, give the minutes:
$body = @{ ip = '203.0.113.50'; ttlMinutes = 60; reason = 'scraping the price list' } | ConvertTo-Json
Invoke-RestMethod "$api/blocks" -Method Post -Headers $headers -ContentType 'application/json' -Body $body
To block it until you remove the block yourself:
$body = @{ ip = '203.0.113.51'; permanent = $true; reason = 'repeat offender' } | ConvertTo-Json
Invoke-RestMethod "$api/blocks" -Method Post -Headers $headers -ContentType 'application/json' -Body $body
To see what is blocked, and to remove a block:
$blocks = Invoke-RestMethod "$api/blocks" -Headers $headers
$blocks | Select-Object ip, reason, expiresAt, country
Invoke-RestMethod "$api/blocks/203.0.113.50" -Method Delete -Headers $headers
A block takes effect within moments. An address on the allowlist cannot be blocked: the API answers 409. A
permanent block shows an empty expiresAt.
Allowlist an address or range
Needs an Admin key.
$body = @{ ipOrCidr = '198.51.100.0/24'; scope = 'Both'; reason = 'head office' } | ConvertTo-Json
Invoke-RestMethod "$api/allowlist" -Method Post -Headers $headers -ContentType 'application/json' -Body $body
scope is Waf (never blocked by the WAF or by blocks), Feeds (never blocked by threat-intelligence feeds) or
Both. To list the allowlist, and to remove an entry:
$allowed = Invoke-RestMethod "$api/allowlist" -Headers $headers
$allowed | Select-Object ipOrCidr, scope, reason
Invoke-RestMethod "$api/allowlist/198.51.100.0/24" -Method Delete -Headers $headers
See Allowlist and outbound (egress) control.
Export recent attacks to a spreadsheet
Needs a Viewer key. The attack feed comes 200 entries at a time, newest first. This saves the latest 1,000 to a CSV file:
$all = @(); $skip = 0
do {
$page = Invoke-RestMethod "$api/events?skip=$skip&take=200" -Headers $headers
$all += $page.items
$skip += 200
} while ($page.hasMore -and $skip -lt 1000)
$all | Select-Object timestamp, source, country, site, kind, anomalyScore, ruleIds |
Export-Csv C:\scripts\portwall-attacks.csv -NoTypeInformation
total in each page is the number of attacks recorded. To find the attacks from one address or on one site, add
q: "$api/events?q=203.0.113.50&take=200". It matches the address, the site name, the kind of attack and the rule
ID.
Fetch only the attacks since a time
Needs a Viewer key. Add since, a time in UTC, to get only the attacks at or after it. This fetches the last 24
hours:
$since = (Get-Date).ToUniversalTime().AddHours(-24).ToString('yyyy-MM-ddTHH:mm:ssZ')
$recent = Invoke-RestMethod "$api/events?since=$since&take=200" -Headers $headers
$recent.total
$recent.items | Select-Object timestamp, source, site, kind
total counts only the attacks since that time, and skip pages through them as above. A script that runs every hour
can save the newest timestamp it saw and pass it as since next time. Because since includes attacks at exactly
that time, the last attack of the previous run comes back once more: skip the attacks whose timestamp is the one you
saved.
Back up the configuration every night
Needs an Admin key. This saves the whole configuration, the same file as Download backup on the Backup & Restore page:
New-Item -ItemType Directory -Force C:\portwall-backups | Out-Null
Invoke-WebRequest "$api/config/export" -Headers $headers -UseBasicParsing `
-OutFile "C:\portwall-backups\portwall-config-$(Get-Date -Format yyyy-MM-dd).json"
Save it as a script and run it daily from Task Scheduler. Keep the backups somewhere only Administrators can read, and copy them off the server too. See Back up, restore and move PortWall for restoring.
With curl
curl works too (it is built into Windows Server 2019 and later). These are for Command Prompt, or a Linux shell on the appliance; in PowerShell, use the examples above
(PowerShell's own curl is a different command, and it passes quotes inside JSON to curl.exe differently):
curl -H "X-Api-Key: pwk_your_key_here" http://127.0.0.1:5000/api/protection-status
curl -X POST -H "X-Api-Key: pwk_your_key_here" -H "Content-Type: application/json" -d "{\"ip\":\"203.0.113.50\",\"ttlMinutes\":60}" http://127.0.0.1:5000/api/blocks
Errors you may see
| Answer | Meaning |
|---|---|
401 |
The key is missing, wrong or revoked. |
403 |
The key's role cannot do this (a Viewer key trying a change), or the request came from an address the console does not accept. |
400 |
Something in the request is not valid, for example an address that is not an IP address. The reply says what. |
404 |
The site, entry or address is not there. |
409 |
The change conflicts with something, for example blocking an allowlisted address. |
With a 400 or 409, the reply is JSON with one field, error, that says what went wrong and what to change, for
example {"error": "Invalid IP address."}. A 404 often has no text at all. In PowerShell, read the reply like this:
try {
$body = @{ ip = 'not-an-address'; ttlMinutes = 60 } | ConvertTo-Json
Invoke-RestMethod "$api/blocks" -Method Post -Headers $headers -ContentType 'application/json' -Body $body
} catch {
$reply = $_.ErrorDetails.Message
if (-not $reply) {
# Windows PowerShell 5.1 leaves the reply in the response itself
$reply = (New-Object System.IO.StreamReader($_.Exception.Response.GetResponseStream())).ReadToEnd()
}
[int]$_.Exception.Response.StatusCode
($reply | ConvertFrom-Json).error
}
This prints 400 and Invalid IP address. It works in Windows PowerShell 5.1 and in PowerShell 7.
The full API reference
The API reference lists every endpoint scripts can use: what it does, the role it needs, and every field it takes and returns. It also covers the tenant API, for hosting providers whose customers manage their own sites. For something the API cannot do yet, write to [email protected] with what you want to automate.