Skip to content

Services API

A service is one subscriber on the fibre network: an ONT on a PON port of a registered OLT, a fixed IPv4 address (and optionally IPv6 with a delegated prefix) on that port’s network, a speed plan, an optional end date. This is the API a billing system uses. No CPE MAC address is needed: the router recognises the ONT by the port and ONT id the OLT stamps on its DHCP requests (Option 82).

Numbering is derived, never chosen: the subscribers of one PON port share that port’s C-VLAN, 100 + card × 16 + pon (port 0/1/0 → C-VLAN 116) inside the router’s S-VLAN, and its address block. The legacy MAC-based records are the Clients API.

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

  1. The technician installs the ONU; the billing asks GET /services/unregistered and shows the serials found.
  2. The technician picks one; the billing calls POST /services with its own ref, the sn, olt, pon, plan and name.
  3. The answer (201) carries service.id — store it — and technician.user_vlan: set the ONU’s WAN to that VLAN with DHCP.
  4. Afterwards, by id: change the plan or end date (POST /services/{id}), suspend/resume, delete, status, traffic graph.

provisioning (being created), active, suspended, error (the OLT side was done but the router side failed — fix the cause and send {"retry": true}).

Lists services (deleted ones are never listed), each with a live block unless fast=1. states counts every service by state.

Name In Type Notes
state query string active, suspended, error, provisioning.
olt query string Only services on this OLT.
q query string Case-insensitive text search in the whole record.
fast query 1 Skip the live probe (link, neighbour, lease).
Terminal window
curl -H "X-API-Key: YOUR_API_KEY" "http://ROUTER-IP:8880/services?state=active&olt=olt-1&fast=1"
{
"ok": true,
"code": 200,
"count": 1,
"states": { "active": 340, "suspended": 12 },
"services": [
{
"id": "svc_1a2b3c4d",
"ref": "billing-000812",
"name": "Customer name",
"olt": "olt-1",
"pon": "0/1/0",
"ont_id": 7,
"state": "active"
}
]
}

One service with its live state and its plan. {id} accepts the service id, the billing’s ref, the ONT serial, the contract, the IPv4 address or the interface name.

Name In Type Notes
id path string Id, ref, serial, contract, address or interface.
Terminal window
curl -H "X-API-Key: YOUR_API_KEY" http://ROUTER-IP:8880/services/svc_1a2b3c4d
{
"ok": true,
"code": 200,
"service": {
"id": "svc_1a2b3c4d",
"ref": "billing-000812",
"name": "Customer name",
"contract": "C-00812",
"comment": null,
"olt": "olt-1",
"pon": "0/1/0",
"ont_id": 7,
"sn": "0123456789ABCDEF",
"svlan": 500,
"cvlan": 116,
"user_vlan": 116,
"iface": "v500.116",
"ipv4": { "network": "100.64.16.0/24", "gateway": "100.64.16.1", "address": "100.64.16.9" },
"ipv6": { "link": "XXXX:XXXX:a:183::/64", "pd": "XXXX:XXXX:b:8300::/56" },
"plan": "plan_200_200",
"iptv": false,
"expires": "2026-10-31 00:00:00",
"state": "active",
"created": "2026-09-25 10:30:00",
"updated": "2026-09-25 10:30:00",
"olt_service_ports": { "internet": 1234, "iptv": null },
"live": {
"iface_exists": true,
"link": "up",
"online": true,
"mac": "AA:BB:CC:DD:EE:FF",
"neigh_state": "REACHABLE",
"lease": { "state": "active", "mac": "AA:BB:CC:DD:EE:FF", "ends": "2026/09/28 12:00:00", "hostname": null }
}
},
"plan": { "name": "plan_200_200", "down_mbps": 200, "up_mbps": 200 }
}

404 when no service matches. The traffic graph of a service is GET /services/{id}/graph — see the System API.

Asks every registered OLT (or one) for the ONTs it sees connected and not yet registered — the technician’s pick list. Each OLT is asked in turn. errors names the OLTs that could not be asked; cvlan is the C-VLAN the ONT’s port uses.

Name In Type Notes
olt query string Ask only this OLT.
Terminal window
curl -H "X-API-Key: YOUR_API_KEY" "http://ROUTER-IP:8880/services/unregistered?olt=olt-1"
{
"ok": true,
"code": 200,
"count": 1,
"onts": [
{
"olt": "olt-1",
"pon": "0/1/0",
"sn": "0123456789ABCDEF",
"vendor": "ABCD",
"model": "ONT-MODEL",
"software": null,
"seen_at": "2026-09-25 10:12:03+00:00",
"svlan": 500,
"cvlan": 116
}
],
"errors": {},
"next": "POST /services {ref, sn, olt, pon, plan|down_mbps+up_mbps, name, ...}"
}

Creates a service in one synchronous call; when it answers 201 the service is live. With ref, the call is idempotent: repeating it returns the existing service (200, "existing": true) instead of creating a second one.

Name In Type Notes
ref body string Recommended: the billing’s own id for this service.
sn body string ONT serial, 16 hex digits. Required with an OLT.
olt body string Registered OLT name; required when more than one OLT is registered.
pon body string PON port frame/slot/port. If omitted, the serial is looked up in the OLT’s autofind table.
plan body string Existing plan name…
down_mbps, up_mbps body integer …or the speeds: plan plan_<down>_<up> is created when missing.
name body string Required: the customer.
contract, comment body string Free text.
user_vlan body integer 1–4094: the VLAN the ONU sends, when it is not the port’s C-VLAN (the OLT translates it).
ipv6 body boolean Default true when the router has a service IPv6 pool configured.
iptv body boolean Put the ONT’s IPTV port on the OLT’s IPTV VLAN (when the OLT has one).
expires body date End date; the router suspends the service then, and resumes it when a later date is set. never clears it.
svlan, ont_id body integer Only with "olt": "none" (an ONT provisioned by hand): required together with pon.
Terminal window
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"ref":"billing-000812","sn":"0123456789ABCDEF","olt":"olt-1","pon":"0/1/0",
"down_mbps":200,"up_mbps":200,"name":"Customer name","contract":"C-00812",
"expires":"2026-10-31"}' \
http://ROUTER-IP:8880/services
{
"ok": true,
"code": 201,
"message": "Service created",
"service": {
"id": "svc_1a2b3c4d",
"ref": "billing-000812",
"pon": "0/1/0",
"ont_id": 7,
"svlan": 500,
"cvlan": 116,
"user_vlan": 116,
"iface": "v500.116",
"ipv4": { "network": "100.64.16.0/24", "gateway": "100.64.16.1", "address": "100.64.16.9" },
"plan": "plan_200_200",
"state": "active"
},
"technician": {
"user_vlan": 116,
"note": "set the ONU's WAN to VLAN 116, DHCP; it receives 100.64.16.9"
}
}
Code Meaning
200 A service with this ref already exists; it is returned with "existing": true.
400 A field is missing or invalid (error says which).
404 Unknown OLT or plan, or the serial is not in the OLT’s autofind table.
409 The serial (or that port’s ONT id) already belongs to a service — returned in service — or the port cannot be used (error says why).
502 The OLT refused; olt_raw has the last lines of its answer. Nothing was created on the router.
500 The OLT side succeeded, the router side failed: the service exists in state error; retry with POST /services/{id} {"retry": true}.
507 No address left for this ONT on its port.

Changes one or more fields. PUT is accepted the same way. The answer’s olt says whether the OLT side of a plan change succeeded.

Name In Type Notes
id path string Id, ref, serial, contract, address or interface.
plan or down_mbps + up_mbps body string / integer New plan (created from the speeds when missing).
name, contract, comment, ref body string New values.
expires body date New end date, or never.
retry body boolean Re-run the router side (after a 500 on create).
Terminal window
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"plan":"plan_300_300"}' http://ROUTER-IP:8880/services/svc_1a2b3c4d
{
"ok": true,
"code": 200,
"message": "Service updated",
"changed": ["plan"],
"olt": "ONT moved to the new plan on the OLT",
"service": { "id": "svc_1a2b3c4d", "plan": "plan_300_300", "state": "active" }
}

400 when nothing is given to change; 404 for an unknown service.

The OLT step can be turned off in the router’s configuration, leaving only the router-side block.

Name In Type Notes
id path string Id, ref, serial, contract, address or interface.
Terminal window
curl -X POST -H "X-API-Key: YOUR_API_KEY" http://ROUTER-IP:8880/services/svc_1a2b3c4d/suspend
{
"ok": true,
"code": 200,
"message": "Service suspended",
"olt": "ONT deactivated on the OLT",
"service": { "id": "svc_1a2b3c4d", "state": "suspended" }
}

502 when the router side was done but the OLT did not follow (the answer says so): retry.

Same parameters and answers as suspend ("message": "Service resumed", "olt": "ONT activated on the OLT").

Terminal window
curl -X POST -H "X-API-Key: YOUR_API_KEY" http://ROUTER-IP:8880/services/svc_1a2b3c4d/resume

The record is removed on the router even when the OLT step fails (the answer’s olt says so). The port’s interface stays for the other subscribers on it.

Name In Type Notes
id path string Id, ref, serial, contract, address or interface.
keep_ont query or body boolean Leave the ONT registered on the OLT.
Terminal window
curl -X DELETE -H "X-API-Key: YOUR_API_KEY" "http://ROUTER-IP:8880/services/svc_1a2b3c4d?keep_ont=1"
{
"ok": true,
"code": 200,
"message": "Service deleted",
"olt": null,
"service": { "id": "svc_1a2b3c4d", "name": "Customer name", "state": "active" }
}

Operator use when renumbering (not part of the billing flow): moves a service created under an older numbering onto its port’s C-VLAN and address, recreating the service-port on the OLT without touching the ONU. The id stays; the ONU takes the new address at its next DHCP. The old per-subscriber interface is removed once nothing else uses it.

Name In Type Notes
id path string Id, ref, serial, contract, address or interface.
Terminal window
curl -X POST -H "X-API-Key: YOUR_API_KEY" http://ROUTER-IP:8880/services/svc_1a2b3c4d/migrate
{
"ok": true,
"code": 200,
"message": "Service moved to its PON port",
"olt": "service-port recreated: user-vlan 116 -> S-VLAN 500 / C-VLAN 116",
"service": { "id": "svc_1a2b3c4d", "iface": "v500.116", "state": "active" },
"note": "the ONU keeps its settings; it takes the new address at its next DHCP (reboot the ONT to make that now)"
}

Answers "message": "Already on its port" when there is nothing to move; 409 when the port, address or C-VLAN is held by something else; 502 when the OLT refuses (router side unchanged).

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

Action Query parameters Same as
action=service-list [state], [olt], [q], [fast=1] GET /services
action=service-get id GET /services/{id}
action=service-unregistered [olt] GET /services/unregistered
action=service-add sn, olt, pon, plan or down_mbps+up_mbps, name, [ref], [contract], [ipv6], [iptv], [expires] POST /services
action=service-set id, [plan], [name], [expires], [retry=1] … POST /services/{id}
action=service-suspend / action=service-resume id POST /services/{id}/suspend / resume
action=service-del id, [keep_ont=1] DELETE /services/{id}
action=service-expiry — Runs the end-date check now (suspends what expired, resumes what was extended); answers {"changed": [...]}.