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.
Authentication
Section titled “Authentication”| Path | Accepts | Without credentials |
|---|---|---|
/api/* | Session cookie, or Authorization: Bearer pat_… | 401 |
/ui/* and / | Session cookie only | Redirect to /login |
| Everything else | Public | — |
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.
Public endpoints
Section titled “Public endpoints”| Route | Method | Purpose |
|---|---|---|
/login | GET, POST | Sign-in page |
/logout | POST | Ends the session |
/setup?token=… | GET, POST | Create the first owner from a setup link |
/invite?token=… | GET, POST | Accept a member invite |
/reset?token=… | GET, POST | Set a new password from a reset link |
/forgot-password | GET, POST | Ask for a reset email (needs email delivery) |
/enroll | POST | Agent 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.crt | GET | The NATS CA certificate. Only when PFCENTRAL_NATS_TLS_ENABLED=true. |
/metrics | GET | Prometheus metrics from device telemetry. See Monitor your fleet. |
/docs | GET | Interactive API reference |
/openapi.json, /openapi.yaml | GET | OpenAPI 3.1 description of the API |
The REST API
Section titled “The REST 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.
| Group | Paths | Examples |
|---|---|---|
| Devices | /api/devices, /api/devices/{deviceId}, /pending, /bootstrap-token, /{deviceId}/approve | List 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.
Active configuration export
Section titled “Active configuration export”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/xmlContent-Disposition: attachment; filename="config-active.xml"Cache-Control: no-storeThe 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.
umask 077curl --fail-with-body --show-error \ -H "Authorization: Bearer $TOKEN" \ "$PFC/api/devices/$DEVICE/system/config/export" \ --output config-active.xmlUse 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.
Conventions
Section titled “Conventions”- 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
applyoperation 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"}.
Example
Section titled “Example”export PFC=https://pf.example.comexport 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/applyMore examples are in API tokens and automation.
Traceroute and DNS Lookup
Section titled “Traceroute and DNS Lookup”Both require an admin or owner API token or session. These are live, uncached operations; neither changes the firewall configuration.
| Method and path | JSON 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.
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"Web UI routes
Section titled “Web UI routes”/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.