Skip to content

HTTP endpoints

pfCentral serves everything on one HTTP port (:2808 by default): the web UI, the REST API, enrollment and the API documentation. Put HTTPS in front with a proxy; see HTTPS for the web UI.

PathAcceptsWithout credentials
/api/*Session cookie, or Authorization: Bearer pat_…401
/ui/* and /Session cookie onlyRedirect to /login
Everything elsePublic—

An API token acts as the member who made it, with that member’s role. See API tokens and automation.

Every request counts against one server-wide rate limit (PFCENTRAL_RATELIMIT_GLOBAL_RPM, default 600 per minute). Over the limit, the answer is 429.

RouteMethodPurpose
/loginGET, POSTSign-in page
/logoutPOSTEnds the session
/setup?token=…GET, POSTCreate the first owner from a setup link
/invite?token=…GET, POSTAccept a member invite
/reset?token=…GET, POSTSet a new password from a reset link
/forgot-passwordGET, POSTAsk for a reset email (needs email delivery)
/enrollPOSTAgent enrollment. Body {"bootstrap_token","smbios_uuid","hostname"}. 400 for a missing field or a bad UUID, 401 for a used or expired token.
/nats-ca.crtGETThe NATS CA certificate. Only when PFCENTRAL_NATS_TLS_ENABLED=true.
/metricsGETPrometheus metrics from device telemetry. See Monitor your fleet.
/docsGETInteractive API reference
/openapi.json, /openapi.yamlGETOpenAPI 3.1 description of the API

The API has about 280 operations. The full, current list with request and response schemas is at https://<your server>/docs, and in the UI under avatar menu → API Explorer, where you can try requests with your own session.

GroupPathsExamples
Devices/api/devices, /api/devices/{deviceId}, /pending, /bootstrap-token, /{deviceId}/approveList the fleet, make an enrollment token, delete a device
Firewall/api/devices/{deviceId}/firewall/…Rules (with apply, enable, disable), aliases (and alias members), NAT, schedules, states, traffic shaper, virtual IPs
Interfaces/api/devices/{deviceId}/interfaces/…Assignments, VLANs, bridges, GRE, groups, LAGG
Services/api/devices/{deviceId}/services/…Service control, DHCP server (with apply and backend), NTP, SSH, cron
DNS/api/devices/{deviceId}/dns-resolver/…, /dns-forwarder/…Settings, host and domain overrides, access lists, apply
VPN/api/devices/{deviceId}/vpn/…IPsec phase 1 and 2, OpenVPN servers, clients and overrides, WireGuard tunnels, peers and settings, each with apply
System/api/devices/{deviceId}/system/…Info, status, settings, hostname, DNS, timezone, console, tunables, packages, certificates, active config/export, agent upgrade
Users/api/devices/{deviceId}/users/…Local users, groups, auth servers on the firewall
Status/api/devices/{deviceId}/status/…Interfaces, gateways, CARP, DHCP leases, logs, OpenVPN
Diagnostics/api/devices/{deviceId}/diagnostics/…Ping, traceroute, DNS lookup, command, ARP, pf tables, reboot, halt, packet capture (packet-capture), and pfSense’s config backups (config-history)
Members/api/members/…, /api/policies/…Invites, password resets, enable or disable a member, set roles

Each device operation maps to one NATS request to that firewall’s agent. See NATS subjects.

GET /api/devices/{deviceId}/system/config/export (export-active-config) requires admin or owner and a device in the caller’s tenant. API tokens use their creator’s role. There is no request body or timestamp parameter.

On success, the response is the raw, complete saved active XML, not JSON:

Content-Type: application/xml
Content-Disposition: attachment; filename="config-active.xml"
Cache-Control: no-store

The server requests system.config.export without using the shared response cache. The agent reads its configured active file (/cf/conf/config.xml by default) without parsing, redacting, applying or modifying it. This is not diagnostics.config-history.get and does not create a backup on the device. Settings saved but not applied are included; this is not a snapshot of running service state.

Terminal window
umask 077
curl --fail-with-body --show-error \
-H "Authorization: Bearer $TOKEN" \
"$PFC/api/devices/$DEVICE/system/config/export" \
--output config-active.xml

Use a protected directory and encrypt the file yourself. It can include passwords, VPN secrets and private keys. Check the HTTP status before treating the output as a backup; curl can write an error body to the output file.

Normal authentication, tenant and role checks apply (401, 404 and 403). An unreadable active file returns an agent error with code 503 and message active configuration unavailable; a reply without data returns 502. Transport failures and older agents without this endpoint use the normal API error response. No partial XML attachment is returned on these failures. The JSON-encoded NATS response must fit the broker’s maximum payload; export is one request/reply, not a stream.

The browser route is GET /ui/devices/{deviceId}/system/config/export. It has the same role and tenant checks, returns the same attachment on success, and returns a generic 502 on agent or transport failure. Both download handlers use Cache-Control: no-store, including handler errors. Successful exports are not audited; authorization denials are. There is no restore endpoint.

  • IDs. {deviceId} is the firewall’s UUID. Item {id} values are what the list operation returns (often the position in pfSense’s configuration, so they can change when items are added or removed). List again before you update or delete.
  • Save and Apply. Create, update and delete change the configuration. Call the matching apply operation to reload the service, as in the UI.
  • Timeouts. Most device requests wait 5 seconds. Traceroute and DNS Lookup wait up to 45 seconds; their agent execution deadline is 35 seconds, with up to 5 seconds for process termination. An offline device fails at once.
  • Errors. Errors use the RFC 9457 problem format: {"title","status","detail"}.
Terminal window
export PFC=https://pf.example.com
export TOKEN=pat_…
curl -s -H "Authorization: Bearer $TOKEN" $PFC/api/devices | jq '.[] | {id, name}'
curl -s -H "Authorization: Bearer $TOKEN" -X POST \
$PFC/api/devices/$DEVICE/firewall/rules/apply

More examples are in API tokens and automation.

Both require an admin or owner API token or session. These are live, uncached operations; neither changes the firewall configuration.

Method and pathJSON body
POST /api/devices/{deviceId}/diagnostics/traceroute{"host":"192.0.2.10","max_hops":10}
POST /api/devices/{deviceId}/diagnostics/dns-lookup{"host":"example.com","record_type":"A","server":"192.0.2.53"}

host is required. Other fields are optional. The defaults, accepted types, command arguments and limits are specified in Diagnostics NATS contracts. Both return 200 with {"host":"…","output":"…","result_code":0} even when the tool exits nonzero; check result_code and the raw output. Invalid input returns 422 before it reaches the device. An insufficient role returns 403.

Terminal window
curl -s -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"host":"example.com","record_type":"MX"}' \
"$PFC/api/devices/$DEVICE/diagnostics/dns-lookup"

/ui/* routes serve the web UI and its live updates. They are not a stable API; use /api/* for scripts. The live streams (/ui/devices/stream, /ui/devices/{id}/stream, /ui/devices/{id}/metrics/stream) are server-sent events, which your proxy must not buffer.