Skip to content

Clients API

A client is the legacy, MAC-based subscriber record: one MAC address pinned to one fixed IPv4 address (and optionally an IPv6 address) on one of the router’s networks, served by DHCP, with an optional speed plan, a suspension flag and an optional end date. New integrations that provision ONTs use the Services API instead; clients remain for billings and networks keyed on the CPE’s MAC.

Base URL, authentication (X-API-Key), error format and the action API are described in the API overview.

MAC addresses are accepted in any common form (aa-bb-cc-dd-ee-ff, aabbccddeeff, AA:BB:CC:DD:EE:FF) and are stored upper-case with colons.

Lists the registered clients, each with the IPv6 addresses it is actually seen using and any prefix delegated to it.

Name In Type Notes
network query CIDR Only clients whose IPv4 address is in this network, e.g. 10.110.0.0/21.
q query string Case-insensitive search in MAC, IP, IPv6, hostname, comment, interface and network.
Terminal window
curl -H "X-API-Key: YOUR_API_KEY" "http://ROUTER-IP:8880/clients?network=10.110.0.0/21"
{
"count": 1,
"clients": [
{
"mac": "AA:BB:CC:DD:EE:FF",
"ip": "10.110.0.2",
"ipv6": null,
"plan": "plan_100_50",
"suspended": false,
"auto_suspended": false,
"expires": null,
"hostname": "client-aabbccddeeff",
"network": "10.110.0.0/21",
"network6": null,
"iface": "vlan100",
"gateway": "10.110.0.1",
"comment": "",
"created": "2026-09-01 10:00:00",
"ipv6_actual": "XXXX:XXXX:100::25",
"ipv6_all": ["XXXX:XXXX:100::25"],
"ipv6_state": "REACHABLE",
"prefix6_delegated": []
}
]
}

One client. {mac} may also be the client’s IPv4 address, its IPv6 address or its hostname (case-insensitive).

Name In Type Notes
mac path string MAC, IPv4, IPv6 or hostname.
Terminal window
curl -H "X-API-Key: YOUR_API_KEY" http://ROUTER-IP:8880/clients/AA:BB:CC:DD:EE:FF
{
"client": {
"mac": "AA:BB:CC:DD:EE:FF",
"ip": "10.110.0.2",
"hostname": "client-aabbccddeeff",
"network": "10.110.0.0/21",
"iface": "vlan100",
"gateway": "10.110.0.1",
"plan": "plan_100_50",
"suspended": false,
"expires": null
}
}

404 {"error": "Client not found"} when nothing matches.

Who is online now: every client and every service with its neighbour (ARP/NDP) state, plus the dynamic DHCP leases that belong to neither. status is online (REACHABLE, DELAY, PROBE, PERMANENT), recent (STALE) or offline. mac_mismatch is true when the address is answered by a different MAC than the one registered.

Name In Type Notes
state query string Only online, recent or offline entries (the summary still counts everything).
Terminal window
curl -H "X-API-Key: YOUR_API_KEY" "http://ROUTER-IP:8880/clients/active?state=online"
{
"summary": {
"registered": 120,
"services": 340,
"online": 401,
"recent": 12,
"offline": 47,
"dynamic_active_leases": 3
},
"clients": [
{
"mac": "AA:BB:CC:DD:EE:FF",
"ip": "10.110.0.2",
"hostname": "client-aabbccddeeff",
"iface": "vlan100",
"status": "online",
"neigh_state": "REACHABLE",
"seen_mac": "AA:BB:CC:DD:EE:FF",
"mac_mismatch": false,
"ipv6_actual": null,
"ipv6_all": [],
"prefix6_delegated": []
},
{
"mac": "AA:BB:CC:00:11:22",
"ip": "100.64.16.9",
"hostname": "Customer name",
"iface": "vlan116",
"status": "online",
"neigh_state": "REACHABLE",
"service": "svc_1a2b3c4d",
"service_state": "active",
"seen_mac": "AA:BB:CC:00:11:22",
"mac_mismatch": false
}
],
"dynamic_leases": [
{
"ip": "10.120.0.50",
"state": "active",
"mac": "AA:BB:CC:33:44:55",
"hostname": "cpe",
"ends": "2026/09/28 12:00:00",
"neigh_state": "STALE",
"online": false
}
]
}

Every MAC seen on the wire over IPv6 — registered or not — with the IPv6 address it holds, its DHCPv6 lease, its pinned prefix and the prefix delegated to it. The router’s own MACs are left out; other routers (IPv6 router advertisements) are left out unless routers=1, or unless they are registered or hold a lease.

Name In Type Notes
iface query string Only this interface, e.g. vlan100.
routers query 1 Include neighbouring routers.
Terminal window
curl -H "X-API-Key: YOUR_API_KEY" "http://ROUTER-IP:8880/clients6?iface=vlan100"
{
"summary": { "total": 1, "online": 1, "registered": 1, "with_prefix": 1 },
"clients": [
{
"mac": "AA:BB:CC:DD:EE:FF",
"registered": true,
"hostname": "client-aabbccddeeff",
"ipv4": "10.110.0.2",
"iface": "vlan100",
"ipv6": "XXXX:XXXX:100::25",
"ipv6_all": ["XXXX:XXXX:100::25"],
"ipv6_reserved": null,
"link_local": "fe80::aabb:ccff:fedd:eeff",
"state": "REACHABLE",
"online": true,
"router": false,
"lease6": ["XXXX:XXXX:100::25"],
"prefix6_pinned": null,
"prefix6_delegated": ["XXXX:XXXX:b:500::/64"]
}
],
"orphan_delegated_prefixes": []
}

Address usage per network on the router: the registered addresses, unregistered addresses seen live on the wire, and the first ten free addresses.

Name In Type Notes
network query CIDR One network, e.g. 10.110.0.0/21. Omit for all.
Terminal window
curl -H "X-API-Key: YOUR_API_KEY" "http://ROUTER-IP:8880/ips?network=10.110.0.0/21"
{
"ok": true,
"code": 200,
"count": 1,
"networks": [
{
"network": "10.110.0.0/21",
"iface": "vlan100",
"gateway": "10.110.0.1",
"total_assignable": 2045,
"used_count": 1,
"free_count": 2044,
"next_free": ["10.110.0.3", "10.110.0.4"],
"used": [ { "ip": "10.110.0.2", "mac": "AA:BB:CC:DD:EE:FF", "hostname": "client-aabbccddeeff" } ],
"unregistered_seen": [ { "ip": "10.110.0.77", "mac": "AA:BB:CC:66:77:88", "neigh_state": "STALE" } ]
}
]
}

404 when the named network is not on this router.

Registers a client. The IPv4 address must lie in a network configured on one of the router’s interfaces (not its network, gateway or broadcast address); the network, interface and gateway are taken from there. An IPv6 address, when given, must be on the same interface.

Name In Type Notes
mac body string Required.
ip body IPv4 Required.
ipv6 body IPv6 Optional fixed IPv6 address.
hostname body string Letters, digits, dots, hyphens, max 63. Default client-<mac without colons>.
comment body string Free text.
plan body string An existing plan name, or none.
expires body date End date (YYYY-MM-DD or YYYY-MM-DD HH:MM), or never.
Terminal window
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"mac":"AA:BB:CC:DD:EE:FF","ip":"10.110.0.2","hostname":"cpe-1","plan":"plan_100_50"}' \
http://ROUTER-IP:8880/clients
{
"ok": true,
"code": 201,
"message": "Client added successfully",
"client": {
"mac": "AA:BB:CC:DD:EE:FF",
"ip": "10.110.0.2",
"ipv6": null,
"plan": "plan_100_50",
"suspended": false,
"auto_suspended": false,
"expires": null,
"hostname": "cpe-1",
"network": "10.110.0.0/21",
"network6": null,
"iface": "vlan100",
"gateway": "10.110.0.1",
"comment": "",
"created": "2026-09-28 10:00:00"
}
}

Errors: 400 invalid MAC/IP/IPv6/hostname or an address outside the router’s networks; 404 unknown plan; 409 MAC, IP, IPv6 or hostname already in use (for a duplicate MAC the answer carries existing); 500 when DHCP does not reload — the client is then rolled back.

Name In Type Notes
mac path string Required.
Terminal window
curl -X DELETE -H "X-API-Key: YOUR_API_KEY" http://ROUTER-IP:8880/clients/AA:BB:CC:DD:EE:FF
{
"ok": true,
"code": 200,
"message": "Client deleted",
"client": { "mac": "AA:BB:CC:DD:EE:FF", "ip": "10.110.0.2", "hostname": "cpe-1" },
"dhcp": { "ok": true, "message": "DHCP reloaded" }
}

404 when the client does not exist; 400 {"error": "Specify MAC"} without a MAC.

Name In Type Notes
mac body string Required.
plan body string A plan name; empty or none removes the limit.
Terminal window
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"mac":"AA:BB:CC:DD:EE:FF","plan":"plan_200_100"}' \
http://ROUTER-IP:8880/plan
{ "ok": true, "code": 200, "message": "Plan assigned", "mac": "AA:BB:CC:DD:EE:FF", "plan": "plan_200_100" }

404 for an unknown client or plan.

The MAC can be in the body or in the path (POST /suspend/{mac}). Suspending by hand clears the automatic (end-date) suspension flag.

Name In Type Notes
mac body or path string Required.
Terminal window
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"mac":"AA:BB:CC:DD:EE:FF"}' http://ROUTER-IP:8880/suspend
{ "ok": true, "code": 200, "message": "Client suspended", "mac": "AA:BB:CC:DD:EE:FF", "suspended": true }

Same parameters as POST /suspend (MAC in the body or the path).

Terminal window
curl -X POST -H "X-API-Key: YOUR_API_KEY" http://ROUTER-IP:8880/resume/AA:BB:CC:DD:EE:FF
{ "ok": true, "code": 200, "message": "Client resumed", "mac": "AA:BB:CC:DD:EE:FF", "suspended": false }

After the date is stored, the end dates of all clients are reconciled immediately (the router also does this every 5 minutes).

Name In Type Notes
mac body string Required.
expires body date YYYY-MM-DD or YYYY-MM-DD HH:MM; empty or never clears it.
Terminal window
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"mac":"AA:BB:CC:DD:EE:FF","expires":"2026-10-31"}' \
http://ROUTER-IP:8880/expires
{
"ok": true,
"code": 200,
"message": "End date set",
"mac": "AA:BB:CC:DD:EE:FF",
"expires": "2026-10-31 00:00:00",
"suspended": false
}

400 for a date it cannot read, 404 for an unknown client.

All are GET /api?action=… with the parameters in the query string (see the API overview).

Action Query parameters Same as
action=list [network] GET /clients
action=search q GET /clients?q= (answer adds query)
action=get mac GET /clients/{mac}
action=add mac, ip, [hostname], [comment], [ipv6] POST /clients (no plan or end date)
action=delete mac DELETE /clients/{mac}
action=active / action=connected [state] GET /clients/active
action=clients6 [iface], [routers=1] GET /clients6
action=ip-info [network] GET /ips
action=set-plan mac, plan POST /plan
action=suspend / action=resume mac POST /suspend / POST /resume
action=set-expires mac, expires POST /expires