REST API overview
SafeDNS Shield filtering configuration is managed through its REST API. The API address is deployment-specific. The default management API port is 8080, but the host, protocol, and port may be changed during deployment.
Use the following placeholder throughout this guide:
<base-url> = http[s]://<shield-management-host>:<management-api-port>
For example:
http://192.0.2.10:8080
Access is normally restricted to source addresses approved during deployment.
Using the API
Authentication
All client-facing API requests require an HTTP bearer token.
Authorization: Bearer <token>
Content-Type: application/json
Example:
curl \
--header "Authorization: Bearer <token>" \
--header "Content-Type: application/json" \
"<base-url>/profiles/"
Use the token and management API address assigned to the Shield deployment.
General behavior
- Successfully completed configuration changes take effect immediately. No service restart or separate activation request is required.
- Successful create operations normally return
201 Created. - Successful update and retrieval operations normally return
200 OK. - Successful deletion and initialization operations return
204 No Contentwith no response body. - Invalid request data may return
422 Unprocessable Entitywith validation details. - IPv4 addresses are supplied as dotted-decimal strings in request bodies, but some resource paths identify an IPv4 address by its unsigned integer representation.
- The examples use trailing slashes where they appear in the API specification.
Profiles and filtering
A filtering profile combines category rules, application rules, block-page behavior, and several profile-level options.
Profile fields
| Field | Type | Description |
|---|---|---|
id |
integer | Client-supplied profile identifier. Required when creating a profile. |
page_id |
integer or null |
Identifier of the block page assigned to the profile. |
white_list_only |
boolean | When enabled, only domains on the allowlist and explicitly allowed applications are permitted. Default: false. |
empty_dns_answer |
boolean | When enabled, Shield returns an empty DNS answer for a blocked domain instead of directing the client to a block page. Default: false. |
appaware_priority_over_bwlists |
boolean | When enabled, application rules take priority over explicit domain allow/deny-list rules. Default: false. |
A profile request also supports:
| Field | Type | Description |
|---|---|---|
cat_ids |
array of integers | Complete collection of filtering category IDs assigned to the profile. |
apps |
array of objects | Complete collection of application rules assigned to the profile. |
Each application rule contains:
{
"app_id": 12,
"status": "deny"
}
Supported statuses are:
-
deny— blocks domains associated with the application. -
allow— explicitly permits resolution of domains associated with the application, including when those domains would otherwise be blocked by the profile's category rules.
Application IDs can be obtained from:
GET /app_aware/application/
Category IDs are listed in the SafeDNS category reference.
Replacement semantics
When cat_ids or apps is included in:
PATCH /profiles/{profile_id}
it replaces the complete existing collection. It is not merged with the current configuration.
- Omitting
cat_idsleaves the category collection unchanged. - Sending
"cat_ids": []removes all category assignments. - Omitting
appsleaves the application-rule collection unchanged. - Sending
"apps": []removes all application rules.
Initial configuration example
The /init/ operation creates the initial Shield configuration in one request.
-
Method:
POST -
URL:
<base-url>/init/
{
"blockpages": [
{
"id": 1,
"type": 0
},
{
"id": 2,
"type": 1
}
],
"profiles": [
{
"profile": {
"id": 1,
"page_id": 1,
"white_list_only": false,
"empty_dns_answer": false,
"appaware_priority_over_bwlists": false
},
"cat_ids": [3, 4, 12],
"apps": [
{
"app_id": 1,
"status": "deny"
},
{
"app_id": 12,
"status": "deny"
},
{
"app_id": 93,
"status": "deny"
}
]
}
],
"bw_lists": [
{
"profile_id": 1,
"type": "deny",
"domains": [
"example1.com",
"example2.com",
"example3.com"
]
}
],
"napts": [],
"nets": [
{
"ip": "100.100.100.100",
"profile_id": 1,
"prefix_len": 32
},
{
"ip": "100.110.110.0",
"profile_id": 1,
"prefix_len": 24
},
{
"ip": "100.120.0.0",
"profile_id": 1,
"prefix_len": 16
}
],
"nets6": []
}
All six top-level arrays are required. Include an empty array when a resource type is not used:
-
blockpages -
profiles -
bw_lists -
napts -
nets -
nets6
The example creates:
- Two block-page records:
- Type
0: default block page. - Type
1: custom block page.
- Type
- Profile
1, assigned to block page1. - Three blocked content/security categories.
- Three denied applications.
- Three explicitly denied domains.
- One IPv4 host assignment and two IPv4 subnet assignments.
Custom block-page content is configured outside the Shield REST API. The API only creates and assigns the block-page record.
A successful initialization returns:
204 No Content
Profile configuration examples
Modifying filtering categories
To change categories assigned to a profile, send the complete desired category collection.
The following request replaces the existing categories of profile 1 with six categories:
-
Method:
PATCH -
URL:
<base-url>/profiles/1
{
"cat_ids": [3, 4, 12, 66, 70, 71]
}
Because apps and profile are omitted, only the category collection is changed.
To remove all category assignments:
{
"cat_ids": []
}
Replacing application rules for a profile
Application rules can be replaced together through the profile endpoint.
-
Method:
PATCH -
URL:
<base-url>/profiles/1
{
"apps": [
{
"app_id": 1,
"status": "deny"
},
{
"app_id": 12,
"status": "allow"
}
]
}
This request removes any other application rules previously assigned to profile 1. It denies application 1 and explicitly permits domains associated with application 12, even if category rules would otherwise block them.
To remove all application rules:
{
"apps": []
}
Managing individual application rules
Retrieve the application catalogue
-
Method:
GET -
URL:
<base-url>/app_aware/application/
Example response:
[
{
"id": 1,
"name": "Application name"
}
]
Retrieve all application rules for a profile
-
Method:
GET -
URL:
<base-url>/profile/1/app_aware
Example response:
[
{
"profile_id": 1,
"app_id": 12,
"status": "allow"
}
]
Add one application rule
-
Method:
POST -
URL:
<base-url>/profile/1/app_aware
{
"app_id": 12,
"status": "allow"
}
Add several application rules with the same status
-
Method:
POST -
URL:
<base-url>/profile/1/app_aware/batch
{
"app_ids": [1, 12, 93],
"status": "deny"
}
Delete an application rule
The final path value is the application ID. The current OpenAPI parameter name is app_aware_id, but it identifies the same value as app_id.
-
Method:
DELETE -
URL:
<base-url>/profile/1/app_aware/12
No request body is required.
Adding a domain to the allowlist
To exempt a domain from category-based blocking, add it to the allowlist of the relevant profile.
-
Method:
POST -
URL:
<base-url>/profile/1/bw_list
{
"type": "allow",
"domain": "example4.com"
}
Adding domains to the denylist
To block several domains directly, use the batch operation.
-
Method:
POST -
URL:
<base-url>/profile/1/bw_list/batch
{
"type": "deny",
"domains": [
"example5.com",
"example6.com",
"example7.com",
"example8.com",
"example9.com"
]
}
Allow/deny-list storage limitation
Shield stores allowlist and denylist domains as hashes. The API therefore cannot retrieve or enumerate the original domain names after they have been submitted.
API clients should retain their own copy of configured allowlists and denylists when later inspection, reconciliation, or synchronization is required.
Update and delete operations still accept the original domain name. Shield calculates its hash internally to locate the stored rule.
Creating a new filtering profile
-
Method:
POST -
URL:
<base-url>/profiles/
{
"profile": {
"id": 2,
"page_id": 1,
"white_list_only": false,
"empty_dns_answer": false,
"appaware_priority_over_bwlists": false
},
"cat_ids": [3, 4, 12, 13, 66, 70, 71],
"apps": [
{
"app_id": 1,
"status": "deny"
},
{
"app_id": 12,
"status": "deny"
},
{
"app_id": 93,
"status": "deny"
}
]
}
This creates profile 2, assigns block page 1, enables the listed filtering categories, and adds three denied application rules.
Subscriber assignments
Assigning an IPv4 address or subnet
To assign an IPv4 host to profile 1, create a /32 network record.
-
Method:
POST -
URL:
<base-url>/net/
{
"ip": "100.100.100.101",
"profile_id": 1,
"prefix_len": 32
}
To assign a /24 subnet:
{
"ip": "100.100.101.0",
"profile_id": 1,
"prefix_len": 24
}
Supported IPv4 prefix lengths are /10 through /32.
Retrieve all IPv4 assignments
-
Method:
GET -
URL:
<base-url>/net/
Example response:
[
{
"ip": "100.100.100.101",
"profile_id": 1,
"prefix_len": 32
},
{
"ip": "100.100.101.0",
"profile_id": 1,
"prefix_len": 24
}
]
IPv4 integer representation
Individual IPv4 resource paths identify an address by its unsigned 32-bit integer representation.
For example:
100.100.100.101 = 1684300901
100.100.101.0 = 1684301056
Python conversion example:
from ipaddress import IPv4Address
int_ip = int(IPv4Address("100.100.100.101"))
print(int_ip) # 1684300901
Reassign an IPv4 address or subnet
-
Method:
PATCH -
URL:
<base-url>/net/1684301056
{
"ip": "100.100.101.0",
"profile_id": 2,
"prefix_len": 24
}
Remove an IPv4 address or subnet
-
Method:
DELETE -
URL:
<base-url>/net/1684301056
No request body is required.
Assigning an IPv6 address or subnet
-
Method:
POST -
URL:
<base-url>/net6/
{
"ip": "2001:db8:100::",
"profile_id": 1,
"prefix_len": 64
}
Supported IPv6 prefix lengths are /16 through /128.
Retrieve all IPv6 assignments
-
Method:
GET -
URL:
<base-url>/net6/
Example response:
[
{
"ip": "2001:db8:100::",
"profile_id": 1,
"prefix_len": 64
}
]
Identifying subscribers through NAPT port ranges
NAPT records assign different source-port ranges on a shared IPv4 address to different filtering profiles.
Port boundaries are inclusive. A range from 10000 to 19999 includes both port 10000 and port 19999.
Create a NAPT assignment
-
Method:
POST -
URL:
<base-url>/napt/
{
"ip": "203.0.113.10",
"lower_port_bound": 10000,
"upper_port_bound": 19999,
"profile_id": 1
}
Port values must be between 0 and 65535. The complete port range is 0 through 65535.
Create several NAPT assignments
-
Method:
POST -
URL:
<base-url>/napt/batch
[
{
"ip": "203.0.113.10",
"lower_port_bound": 10000,
"upper_port_bound": 19999,
"profile_id": 1
},
{
"ip": "203.0.113.10",
"lower_port_bound": 20000,
"upper_port_bound": 29999,
"profile_id": 2
}
]
Retrieve all NAPT assignments for an IPv4 address
The path uses the integer representation of the IPv4 address.
-
Method:
GET -
URL:
<base-url>/napt/3405803786
3405803786 is the integer representation of 203.0.113.10.
Delete several NAPT assignments
Unlike individual NAPT resource paths, the batch-deletion body uses dotted-decimal IPv4 strings.
-
Method:
DELETE -
URL:
<base-url>/napt/batch
[
{
"ip": "203.0.113.10",
"lower_port_bound": 10000,
"upper_port_bound": 19999
},
{
"ip": "203.0.113.10",
"lower_port_bound": 20000,
"upper_port_bound": 29999
}
]
profile_id is not included in the batch-deletion request.
Profile schedules
A schedule temporarily switches one profile to another during specified periods.
The profile identified in the URL is the source profile. target_profile_id identifies the profile used during scheduled periods. A profile cannot target itself.
Supported day formats
Both weekday names and integers are accepted:
| Integer | Weekday |
|---|---|
0 |
Monday |
1 |
Tuesday |
2 |
Wednesday |
3 |
Thursday |
4 |
Friday |
5 |
Saturday |
6 |
Sunday |
Weekday names must be lowercase:
monday, tuesday, wednesday, thursday, friday, saturday, sunday
Examples in this guide use weekday names for readability.
Times use HH:MM format and must fall on 30-minute boundaries, such as 08:00, 12:30, or 18:00. 00:00 represents the start of a day and 24:00 represents the end of a day.
Create a schedule
-
Method:
POST -
URL:
<base-url>/profile/1/schedule
{
"target_profile_id": 2,
"periods": [
{
"start": {
"day": "monday",
"time": "18:00"
},
"end": {
"day": "tuesday",
"time": "08:00"
}
},
{
"start": {
"day": 5,
"time": "00:00"
},
"end": {
"day": 5,
"time": "24:00"
}
}
]
}
This example switches profile 1 to profile 2 from Monday evening through Tuesday morning and for the whole of Saturday.
Update a schedule
-
Method:
PATCH -
URL:
<base-url>/profile/1/schedule
To change the target profile for all existing periods:
{
"target_profile_id": 3
}
To replace all existing periods:
{
"periods": [
{
"start": {
"day": "friday",
"time": "18:00"
},
"end": {
"day": "monday",
"time": "08:00"
}
}
]
}
When periods is supplied, all previously configured periods are deleted and replaced by the submitted collection.
Delete a schedule
-
Method:
DELETE -
URL:
<base-url>/profile/1/schedule
No request body is required.
Block pages
Block-page records use two types:
| Type | Meaning |
|---|---|
0 |
Default block page |
1 |
Custom block page |
Example creation request:
-
Method:
POST -
URL:
<base-url>/blockpage/
{
"id": 2,
"type": 1
}
Custom block-page content is configured outside the Shield REST API.