Skip to content

Filter API

The filter’s API is on port 8091, reached on the LAN or over Tailscale (see Away from home). Every route except /api/health, /ca.pem and /ca.mobileconfig needs Authorization: Bearer <token>: either state/api_token (Family Filter for Mac’s) or a parent phone’s own token.

Phone tokens are kept as hashes in state/tokens.json, and made with filter/scripts/filter-token.sh add "<whose iPhone>". That puts the new token on the clipboard rather than printing it. list and remove <id> revoke one without touching the others.

Route
GET /api/health {ok, version, rules, uptime, paused, you: {ip, listed, off, filtered}}. No token needed; you is the asking address
GET /ca.pem, GET /ca.mobileconfig The certificate, plain or as a profile
Route
GET /api/rules, POST /api/rules {pattern, note}, DELETE /api/rules/<id> Block rules. A URL or a domain; 400 if it isn’t one
GET /api/allow, POST /api/allow {pattern, note}, DELETE /api/allow/<id> Never blocked: same shape and matching
GET /api/check?url= {blocked, allowed, beaten, passthrough}: what blocks this URL, or what lets it through and the block it beat
GET /api/passthrough {builtin, intercepted, manual, auto}
POST /api/passthrough {host, note}, DELETE /api/passthrough/<host> Manual pass-through. Deleting an automatic one retries decrypting it
Route
GET /api/log?since=&kind=&limit= Log entries. kind is visit, blocked, allowed, passthrough or a focus-… kind, comma-separated. Default the last day, at most 2000
GET /api/alerts {cert_lost: [{client, device, since, hosts}], paused}
GET /api/tunnel?since=&until= {devices, covered, active}: when each tunnel carried traffic. Default the last 8 days, at most 32

A log entry is {time, kind, client, device, url, rule?, status?, page?}. Visits also have ua (the user agent, which says which app), dest and user (a person clicked or typed, rather than a tab reloading by itself). Visits are top-level page loads only, not every image and script.

Route
GET /api/pause, POST /api/pause {minutes}, DELETE /api/pause The whole filter. minutes null means until switched back on. by records which token switched it off
GET /api/devices, POST /api/devices/<ip>/off {minutes}, DELETE /api/devices/<ip>/off One device
GET /api/clients The devices from clients.txt
Route
GET /api/focus {sets, windows, pauses, ondemand}
PUT /api/focus/sets Replace the block sets: [{id?, name, patterns, note, allowance?}]
POST /api/focus/sets/<id>/patterns {pattern, by?} Add one site or page to one set, safely alongside other clients
PUT /api/focus/windows Replace the scheduled windows: [{id?, name, rules: [{set, mode, minutes}], days, start, end, all_day, from?, to?, button, enabled}]. mode is block, limit or open
PUT /api/focus/pauses Replace the days off: [{id?, from, to?, note, sets}]
GET /api/focus/now What’s blocking now, the limits and allowances on now, exceptions, windows started on demand, and scheduled windows ended early
GET /api/focus/sessions, POST /api/focus/sessions Start a window now (its rules), or block sets, for minutes or until a time (at most a day)
POST /api/focus/sessions/<id>/extend, DELETE /api/focus/sessions/<id> Extend one started on demand, or end any window’s run now
GET/POST /api/focus/exceptions, DELETE /api/focus/exceptions/<id> Temporary allow or block, at most a day
GET /api/focus/limits, POST /api/focus/limits/<id>/grant Time limits on now; more time
GET /api/focus/allowances, POST /api/focus/allowances/<set>/add, POST/DELETE /api/focus/allowances/<set>/open Allowances today; more time today; open now
GET /api/focus/done?since= “I’m done” presses
POST /api/focus/done/ack {ids, by} Acknowledge presses
POST /api/focus/done/<id>/end Let the child off: end the window the press came from

The full request and response shapes are in the source.

Source: filter/README.md