OLT API
The router drives its OLTs through the licensed vendor driver that ships with it. This page covers:
- Operations —
POST /olt/{op}: one operation on one OLT (read its state, add an ONT, tag a VLAN, …). - The registry —
/olts: the OLTs this router knows by name, with their credentials sealed on the router. - Plan sync —
/olt/sync: the router’s plans pushed to every registered OLT as profiles and rate tables. - Backups —
/olt/backup,/olt/backups,/olt/diff: the OLTs’ configuration history.
Base URL, authentication (X-API-Key), error format and the action API are described in the
API overview.
Choosing the OLT
Section titled “Choosing the OLT”Every operation needs an OLT. The router picks it in this order:
oltin the body (or headerX-OLT-Name) — a registered OLT by name. An unknown name is404.host,user,passin the body (or headersX-OLT-Host,X-OLT-User,X-OLT-Pass), with optionalprotocol(telnetorssh, defaulttelnet; headerX-OLT-Protocol) andport(the OLT’s TCP port; headerX-OLT-Port).passwordis accepted as an alias ofpass.- Nothing — when exactly one OLT is registered, that one.
Otherwise the answer is 400 (“name a registered OLT (olt) or send host, user and pass on this call”).
A numeric port in the body is the OLT’s TCP port; a PON port such as "0/1/3" in port is an
argument of the operation. pon is accepted as an alias for the PON port.
Common options and the answer
Section titled “Common options and the answer”| Name | In | Type | Notes |
|---|---|---|---|
olt |
body | string | A registered OLT’s name (see above). |
host, user, pass |
body | string | One-off credentials instead of olt. |
protocol |
body | string | telnet (default) or ssh. |
vendor |
body | string | Driver vendor for one-off credentials; default huawei. A registered OLT uses its own. |
timeout |
body | integer | Seconds, 10–300. Default 40; 180 for config, plan-sync and init. |
raw |
body | boolean | Also return every command sent and the OLT’s reply (raw). Always included on failure. |
dry_run |
body | boolean | Write operations that support it only plan the change and return what they would do. |
force |
body | boolean | Override a guard where the operation allows it (see Shared OLTs). |
Every operation answers the same envelope:
{ "ok": true, "code": 200, "op": "info", "olt": { "host": "XXX.XXX.XXX.10", "name": "olt-1" }, "took_ms": 1790, "result": { }, "error": null}codeis200on success,401when the OLT refused the login,502for any other failure (the OLT’s error is inerror, andrawholds the transcript). An unknown operation is404with the list of operations inops.- After a successful write (other than
saveand dry runs) the answer carries"note": "not saved to the OLT's flash yet — run op \"save\" when done".
Write operations need the licence’s write feature, run one at a time per OLT, and are never retried on another transport once sent. Each call is logged on the router (operation, OLT, outcome — never credentials).
Shared OLTs
Section titled “Shared OLTs”A registered OLT can carry this router’s S-VLAN (svlan) and be marked as another company’s
(operator). On such an OLT the router only touches what is its own: VLAN operations on another
S-VLAN are refused unless force=true; ONT operations refuse an ONT whose service-ports are on
another S-VLAN (force never overrides that); exec needs force=true. Objects the router creates
are named with its prefix (default dtvsol, or s<svlan> when an S-VLAN is set).
Operations: reading
Section titled “Operations: reading”GET /olt
Section titled “GET /olt”Lists the operations, which of them change the OLT, and how to call them. No OLT is contacted.
curl -H "X-API-Key: YOUR_API_KEY" "http://ROUTER-IP:8880/olt"{ "ops": ["info", "autofind", "onus", "vlans", "serviceports", "profiles", "config", "run", "audit", "boards", "pon-ports", "port-optical", "counters", "exec", "vlan-add", "vlan-del", "port-vlan", "profile-add", "profile-del", "ont-add", "ont-del", "ont-reboot", "ont-desc", "ont-optical", "ntp", "sysname", "save", "plan-sync", "init", "ont-activate", "ont-deactivate", "ont-replan"], "write_ops": ["exec", "vlan-add", "vlan-del", "port-vlan", "profile-add", "profile-del", "ont-add", "ont-del", "ont-reboot", "ont-desc", "ntp", "sysname", "save", "plan-sync", "init", "ont-activate", "ont-deactivate", "ont-replan"], "usage": "POST /olt/{op} with JSON {host, user, pass, [port], [protocol: telnet|ssh], ...op args}; ..."}POST /olt/info
Section titled “POST /olt/info”The OLT’s product, software version and patch, uptime, system name, boards, clock and NTP state.
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"olt": "olt-1"}' "http://ROUTER-IP:8880/olt/info"{ "ok": true, "code": 200, "op": "info", "olt": { "host": "XXX.XXX.XXX.10", "name": "olt-1" }, "took_ms": 1790, "result": { "product": "MA5608T", "version": "V800R018C10", "uptime": "35 day(s), 4 hour(s)", "sysname": "olt-1", "boards": [ { "slot": 0, "board": "GPFD", "status": "Normal" } ], "time": "2026-09-28 12:00:00+00:00", "ntp": "synchronized" }, "error": null}With one-off credentials instead of a registered name:
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"host": "XXX.XXX.XXX.10", "user": "admin", "pass": "OLT_PASSWORD", "protocol": "ssh"}' \ "http://ROUTER-IP:8880/olt/info"POST /olt/autofind
Section titled “POST /olt/autofind”The ONTs the OLT sees but that are not registered yet (PON port, serial, vendor, when seen).
Result: {"count": N, "onts": [...]}.
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"olt": "olt-1"}' "http://ROUTER-IP:8880/olt/autofind"POST /olt/onus
Section titled “POST /olt/onus”The registered ONTs: id, serial, run/config/match state. Result: {"count": N, "onts": [...]}.
| Name | In | Type | Notes |
|---|---|---|---|
port |
body | string | Optional PON port frame/slot/port, e.g. 0/1/3. Without it, every PON board. |
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"olt": "olt-1", "port": "0/1/3"}' "http://ROUTER-IP:8880/olt/onus"POST /olt/vlans
Section titled “POST /olt/vlans”The OLT’s VLAN table and the VLANs tagged on every uplink port.
Result: {"count": N, "vlans": [...], "uplink_ports": {"0/3/0": {"vlans": [100, 101], "native": null}}}.
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"olt": "olt-1"}' "http://ROUTER-IP:8880/olt/vlans"POST /olt/serviceports
Section titled “POST /olt/serviceports”The service-ports (subscriber flows). Result: {"count": N, "service_ports": [...]}.
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"olt": "olt-1"}' "http://ROUTER-IP:8880/olt/serviceports"POST /olt/profiles
Section titled “POST /olt/profiles”The DBA, line and service profiles. Result: {"dba": [...], "line": [...], "service": [...]}.
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"olt": "olt-1"}' "http://ROUTER-IP:8880/olt/profiles"POST /olt/config
Section titled “POST /olt/config”The OLT’s full running configuration as text. Result: {"lines": N, "config": "..."}.
Default timeout 180 s.
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"olt": "olt-1"}' "http://ROUTER-IP:8880/olt/config"POST /olt/run
Section titled “POST /olt/run”Runs one read-only display … command and returns its raw output. Anything that is not a
display command is refused (use exec for configuration).
Result: {"command": "...", "output": "..."}.
| Name | In | Type | Notes |
|---|---|---|---|
command |
body | string | Required. Must start with display. |
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"olt": "olt-1", "command": "display ont info 0 1 3 all"}' "http://ROUTER-IP:8880/olt/run"POST /olt/audit
Section titled “POST /olt/audit”Read only: what the OLT holds for this router on its S-VLAN — whether the VLAN exists and its type, the uplinks it is tagged on, its service-ports with their rate limits, the rate tables, whether DHCP option 82 is enabled, and the ONTs of the given PON ports. Used by the router’s doctor.
| Name | In | Type | Notes |
|---|---|---|---|
svlan |
body | integer | Required. The S-VLAN to audit. |
ports |
body | array | Optional PON ports whose ONTs to list, e.g. ["0/1/3"]. |
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"olt": "olt-1", "svlan": 400, "ports": ["0/1/3"]}' "http://ROUTER-IP:8880/olt/audit"{ "ok": true, "code": 200, "op": "audit", "result": { "svlan": 400, "exists": true, "type": "smart", "attribute": "stacking", "uplinks": [ { "port": "0/3/0", "native_vlan": 1, "state": "up" } ], "service_ports": [ { "index": 12, "state": "up", "pon": "0/1/3", "ont_id": 0, "gem": 1, "flow_type": "vlan", "user_vlan": 100, "inner_vlan": 100, "car": { "in": 11, "out": 10 } } ], "option82": true, "rates": { "10": { "cir": 112640, "pir": 112640 } }, "onts": [], "plan_prefix": "dtvsol" }}POST /olt/boards
Section titled “POST /olt/boards”The OLT’s boards (slot, type, state).
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"olt": "olt-1"}' "http://ROUTER-IP:8880/olt/boards"POST /olt/pon-ports
Section titled “POST /olt/pon-ports”The PON ports and their state.
| Name | In | Type | Notes |
|---|---|---|---|
port |
body | string | Optional: one PON port frame/slot/port; default every port. |
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"olt": "olt-1"}' "http://ROUTER-IP:8880/olt/pon-ports"POST /olt/port-optical
Section titled “POST /olt/port-optical”The optical readings of the PON ports’ own transceivers.
| Name | In | Type | Notes |
|---|---|---|---|
port |
body | string | Optional: one PON port; default every port. |
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"olt": "olt-1", "port": "0/1/3"}' "http://ROUTER-IP:8880/olt/port-optical"POST /olt/counters
Section titled “POST /olt/counters”Traffic counters of the ports, or of the ONTs.
| Name | In | Type | Notes |
|---|---|---|---|
port |
body | string | Optional: one PON port. |
ont_id |
body | integer | Optional: one ONT’s counters (needs port). |
uplinks |
body | boolean | Include the uplink ports. |
onts |
body | boolean | Per-ONT counters for every ONT (of port if given) instead of the ports’ own. |
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"olt": "olt-1", "uplinks": true}' "http://ROUTER-IP:8880/olt/counters"POST /olt/ont-optical
Section titled “POST /olt/ont-optical”An ONT’s optical readings: receive/transmit power, temperature, voltage.
Result: {"port": "0/1/3", "onts": [...]}.
| Name | In | Type | Notes |
|---|---|---|---|
port |
body | string | Required. PON port frame/slot/port. |
ont_id |
body | integer or "all" |
Optional; default every ONT on the port. |
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"olt": "olt-1", "port": "0/1/3", "ont_id": 0}' "http://ROUTER-IP:8880/olt/ont-optical"Operations: changing the OLT
Section titled “Operations: changing the OLT”Nothing below is saved to the OLT’s flash until POST /olt/save. VLAN ids are 1–4094.
POST /olt/ont-add
Section titled “POST /olt/ont-add”Normally you provision a subscriber with POST /services (see the
Services API), which calls this for you and also sets up the
router side.
| Name | In | Type | Notes |
|---|---|---|---|
port |
body | string | Required. PON port, e.g. 0/1/3 (pon also accepted). |
sn |
body | string | Required. The ONT serial as 16 hex digits. |
vlan |
body | integer | Required. The PON port’s VLAN. |
user_vlan |
body | integer | The VLAN the ONT sends; default vlan. |
description |
body | string | ONT description. |
plan |
body | string | A plan on this router: its profiles and rate limits are used (the rates include the router’s OLT headroom, default ×1.10). 404 if the plan does not exist. |
down_kbps, up_kbps |
body | integer | Explicit rate limits instead of plan. |
line_profile, srv_profile, profile_id |
body | integer | Explicit profile ids (profile_id sets both; default the vlan). |
svlan |
body | integer | Outer VLAN; a registered OLT’s svlan is used when omitted. |
iptv |
body | boolean | With a registered OLT: add the OLT’s IPTV VLAN (iptv_vlan) for this subscriber. |
iptv_vlan |
body | integer | The IPTV VLAN explicitly. |
eth_ports |
body | integer | Default 1. |
gemport |
body | integer | Default 1. |
dry_run |
body | boolean | Plan only. |
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"olt": "olt-1", "port": "0/1/3", "sn": "ABCD123456789012", "vlan": 100, "plan": "plan_100_50", "description": "router-1 sub 42"}' \ "http://ROUTER-IP:8880/olt/ont-add"{ "ok": true, "code": 200, "op": "ont-add", "olt": { "host": "XXX.XXX.XXX.10", "name": "olt-1" }, "result": { "port": "0/1/3", "sn": "ABCD123456789012", "ont_id": 0, "vlan": 100, "cvlan": 100, "user_vlan": 100 }, "error": null, "note": "not saved to the OLT's flash yet — run op \"save\" when done"}POST /olt/ont-del
Section titled “POST /olt/ont-del”| Name | In | Type | Notes |
|---|---|---|---|
port |
body | string | Required. PON port. |
ont_id |
body | integer | Required. |
force |
body | boolean | Also act on an ONT that has no service-port at all. |
expect_svlan |
body | integer | One more S-VLAN counted as this router’s for this ONT. |
dry_run |
body | boolean | Plan only. |
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"olt": "olt-1", "port": "0/1/3", "ont_id": 0}' "http://ROUTER-IP:8880/olt/ont-del"Result: {"port": "0/1/3", "ont_id": 0, "deleted": true, "service_ports_removed": [...]}.
POST /olt/ont-reboot
Section titled “POST /olt/ont-reboot”Same parameters as ont-del (port, ont_id, force, expect_svlan).
Result: {"port": "0/1/3", "ont_id": 0, "rebooted": true}.
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"olt": "olt-1", "port": "0/1/3", "ont_id": 0}' "http://ROUTER-IP:8880/olt/ont-reboot"POST /olt/ont-activate
Section titled “POST /olt/ont-activate”Parameters: port, ont_id, force, expect_svlan. Result: {"port": "0/1/3", "ont_id": 0, "active": true}.
Resuming a service (POST /services/{id}/resume) calls this for you.
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"olt": "olt-1", "port": "0/1/3", "ont_id": 0}' "http://ROUTER-IP:8880/olt/ont-activate"POST /olt/ont-deactivate
Section titled “POST /olt/ont-deactivate”Parameters: port, ont_id, force, expect_svlan. Result: {"port": "0/1/3", "ont_id": 0, "active": false}.
Suspending a service (POST /services/{id}/suspend) calls this for you.
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"olt": "olt-1", "port": "0/1/3", "ont_id": 0}' "http://ROUTER-IP:8880/olt/ont-deactivate"POST /olt/ont-desc
Section titled “POST /olt/ont-desc”| Name | In | Type | Notes |
|---|---|---|---|
port |
body | string | Required. |
ont_id |
body | integer | Required. |
description |
body | string | The new description (empty clears it). |
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"olt": "olt-1", "port": "0/1/3", "ont_id": 0, "description": "sub 42"}' \ "http://ROUTER-IP:8880/olt/ont-desc"POST /olt/ont-replan
Section titled “POST /olt/ont-replan”Changing a service’s plan (POST /services/{id} with plan) calls this for you.
| Name | In | Type | Notes |
|---|---|---|---|
port |
body | string | Required. |
ont_id |
body | integer | Required. |
plan |
body | string | Required. The plan name. |
down_kbps, up_kbps |
body | integer | Required. The new rate limits. |
vlan |
body | integer | Required. The inner VLAN. |
user_vlan |
body | integer | Default vlan. |
svlan |
body | integer | Must be this router’s S-VLAN on a shared OLT. |
expect_svlan |
body | integer | As for ont-del. |
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"olt": "olt-1", "port": "0/1/3", "ont_id": 0, "plan": "plan_300_150", "down_kbps": 337920, "up_kbps": 168960, "vlan": 100, "svlan": 400}' \ "http://ROUTER-IP:8880/olt/ont-replan"POST /olt/vlan-add
Section titled “POST /olt/vlan-add”| Name | In | Type | Notes |
|---|---|---|---|
vlan |
body | integer | Required. |
type |
body | string | smart (default), standard, mux or super. |
attribute |
body | string | common, stacking or qinq. |
description |
body | string | Optional. |
uplinks |
body | array or string | Uplink ports to tag it on, e.g. ["0/3/0"] or "0/3/0,0/3/1". |
force |
body | boolean | Needed for a VLAN other than this router’s S-VLAN on a shared OLT. |
dry_run |
body | boolean | Plan only. |
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"olt": "olt-1", "vlan": 100, "uplinks": "0/3/0", "description": "PON 0/1/3"}' \ "http://ROUTER-IP:8880/olt/vlan-add"POST /olt/vlan-del
Section titled “POST /olt/vlan-del”| Name | In | Type | Notes |
|---|---|---|---|
vlan |
body | integer | Required. |
uplinks |
body | array or string | Uplink ports to untag it from first. |
force |
body | boolean | As for vlan-add. |
dry_run |
body | boolean | Plan only. |
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"olt": "olt-1", "vlan": 100, "uplinks": ["0/3/0"]}' "http://ROUTER-IP:8880/olt/vlan-del"POST /olt/port-vlan
Section titled “POST /olt/port-vlan”| Name | In | Type | Notes |
|---|---|---|---|
vlan |
body | integer | Required. |
port |
body | string | Required. The port, e.g. 0/3/0. |
remove |
body | boolean | Untag instead of tag. |
force |
body | boolean | As for vlan-add. |
dry_run |
body | boolean | Plan only. |
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"olt": "olt-1", "vlan": 100, "port": "0/3/0"}' "http://ROUTER-IP:8880/olt/port-vlan"Result: {"vlan": 100, "port": "0/3/0", "tagged": true}.
POST /olt/profile-add
Section titled “POST /olt/profile-add”| Name | In | Type | Notes |
|---|---|---|---|
vlan |
body | integer | Required. |
dba |
body | integer | DBA profile id; default 5. |
eth_ports |
body | integer | Default 1. |
profile_id |
body | integer | Default the vlan. |
force |
body | boolean | Override the ownership guard on a shared OLT. |
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"olt": "olt-1", "vlan": 100, "dba": 5, "eth_ports": 1}' "http://ROUTER-IP:8880/olt/profile-add"POST /olt/profile-del
Section titled “POST /olt/profile-del”| Name | In | Type | Notes |
|---|---|---|---|
profile_id |
body | integer | Required. |
force |
body | boolean | Override the ownership guard on a shared OLT. |
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"olt": "olt-1", "profile_id": 100}' "http://ROUTER-IP:8880/olt/profile-del"POST /olt/exec
Section titled “POST /olt/exec”On a shared OLT it needs force=true. A command containing ? is refused.
Result: {"executed": N, "steps": [{"cmd": "...", "output": "..."}]}.
| Name | In | Type | Notes |
|---|---|---|---|
commands |
body | array or string | Required. A list, or one text with commands separated by newlines or commas. |
force |
body | boolean | Required on a shared OLT. |
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"olt": "olt-1", "commands": ["vlan desc 100 description PON-0-1-3"]}' \ "http://ROUTER-IP:8880/olt/exec"POST /olt/ntp
Section titled “POST /olt/ntp”| Name | In | Type | Notes |
|---|---|---|---|
server |
body | string | Required. NTP server address. |
remove |
body | boolean | Remove instead of set. |
timezone |
body | string | Optional time zone to set with it. |
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"olt": "olt-1", "server": "10.0.0.1"}' "http://ROUTER-IP:8880/olt/ntp"Result: {"server": "10.0.0.1", "set": true}.
POST /olt/sysname
Section titled “POST /olt/sysname”| Name | In | Type | Notes |
|---|---|---|---|
name |
body | string | Required. Letters, digits, ., _, -. |
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"olt": "olt-1", "name": "olt-1"}' "http://ROUTER-IP:8880/olt/sysname"POST /olt/save
Section titled “POST /olt/save”Result: {"saved": true, "output": "..."}.
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"olt": "olt-1"}' "http://ROUTER-IP:8880/olt/save"POST /olt/plan-sync
Section titled “POST /olt/plan-sync”When plans is not given, the router’s own plans and OLT headroom are sent (and a registered OLT’s
iptv_vlan). To sync every registered OLT at once use POST /olt/sync.
| Name | In | Type | Notes |
|---|---|---|---|
dry_run |
body | boolean | Only list the commands it would run. |
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"olt": "olt-1", "dry_run": true}' "http://ROUTER-IP:8880/olt/plan-sync"POST /olt/init
Section titled “POST /olt/init”With a registered OLT, missing values come from the registry (svlan, iptv_vlan, operator), the
system name defaults to the OLT’s registered name, NTP to the router’s own address towards the OLT,
and the plans to the router’s plans. On an OLT registered as another company’s (operator) the
OLT-wide settings are left alone.
| Name | In | Type | Notes |
|---|---|---|---|
apply |
body | boolean | Make the changes; default false (dry run). |
svlan |
body | integer | This router’s S-VLAN. |
iptv_vlan |
body | integer | IPTV VLAN. |
uplinks |
body | array or string | Uplink ports to use. |
sysname |
body | string | System name. |
ntp |
body | string | NTP server. |
timezone |
body | string | Time zone. |
operator |
body | boolean | Treat the OLT as another company’s. |
router_parent |
body | string | The router interface the OLT’s S-VLANs ride on. |
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"olt": "olt-1", "svlan": 400, "uplinks": "0/3/0"}' "http://ROUTER-IP:8880/olt/init"Result (dry run, trimmed): {"dry_run": true, "svlan": 400, "pon_ports": ["0/1/0", "0/1/1"], "uplinks": ["0/3/0"], "steps": [{"cmd": "..."}]}.
Plan sync
Section titled “Plan sync”POST /olt/sync
Section titled “POST /olt/sync”One OLT at a time. The same work runs by itself in the background after every plan change and on a
timer. Also answers GET with the parameters in the query string.
| Name | In | Type | Notes |
|---|---|---|---|
olt |
body or query | string | Only this registered OLT. |
dry_run |
body or query | boolean | Only list the commands per OLT. |
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"olt": "olt-1", "dry_run": true}' "http://ROUTER-IP:8880/olt/sync"{ "ok": true, "code": 200, "dry_run": true, "plans": [ { "name": "plan_100_50", "down_mbps": 100, "up_mbps": 50 } ], "headroom": 1.1, "olts": { "olt-1": { "ok": true, "error": null, "in_sync": false, "executed": 0, "dry_run": true, "commands": ["..."], "changes": { }, "notes": [] } }}502 when any OLT failed; 404 when no OLT is registered or the named one is unknown.
GET /olt/sync
Section titled “GET /olt/sync”Same as POST /olt/sync, with olt and dry_run in the query string.
curl -H "X-API-Key: YOUR_API_KEY" "http://ROUTER-IP:8880/olt/sync?dry_run=1"Configuration backups
Section titled “Configuration backups”The OLTs’ configurations are kept on the router as a history (one entry per change). The router backs
them up by itself after changes; these calls read the history or take a backup now. olt may be
omitted when exactly one OLT is registered (404 otherwise). Each also accepts POST with the
parameters in the body.
POST /olt/backup
Section titled “POST /olt/backup”Refused with 409 for an OLT registered as another company’s (operator).
| Name | In | Type | Notes |
|---|---|---|---|
olt |
body or query | string | The registered OLT. |
curl -X POST -H "X-API-Key: YOUR_API_KEY" "http://ROUTER-IP:8880/olt/backup?olt=olt-1"{ "code": 200, "olt": "olt-1", "ok": true, "changed": true, "commit": "3f2a9c1", "lines": 2140}502 when the configuration could not be read (or looked incomplete — then nothing is stored).
GET /olt/backups
Section titled “GET /olt/backups”The backup history of an OLT, newest first, and its maintenance state.
| Name | In | Type | Notes |
|---|---|---|---|
olt |
query | string | The registered OLT. |
n |
query | integer | How many entries; default 30, 1–500. |
curl -H "X-API-Key: YOUR_API_KEY" "http://ROUTER-IP:8880/olt/backups?olt=olt-1&n=10"{ "ok": true, "code": 200, "olt": "olt-1", "state": { }, "backups": [ { "commit": "3f2a9c1", "at": "2026-09-28 12:00:00", "what": "olt-1: on request — 1 file changed, 3 insertions(+), 1 deletion(-)" } ]}GET /olt/diff
Section titled “GET /olt/diff”What changed in an OLT’s configuration: at one backup (default the latest), or between two.
| Name | In | Type | Notes |
|---|---|---|---|
olt |
query | string | The registered OLT. |
rev |
query | string | A backup’s commit id (4–40 hex digits); default the latest. |
to |
query | string | A second commit id: the diff between rev and to. |
curl -H "X-API-Key: YOUR_API_KEY" "http://ROUTER-IP:8880/olt/diff?olt=olt-1&rev=3f2a9c1"{ "ok": true, "code": 200, "olt": "olt-1", "diff": "3f2a9c1 2026-09-28 12:00:00\n...\n" }With no backup yet: "diff": "" and "note": "no backup yet". 400 for a malformed commit id,
404 for an unknown one.
The OLT registry
Section titled “The OLT registry”GET /olts
Section titled “GET /olts”The registered OLTs. Passwords are never returned (has_pass is always true); for SNMP access
only its version is shown. Each entry carries the result of its last plan sync.
curl -H "X-API-Key: YOUR_API_KEY" "http://ROUTER-IP:8880/olts"{ "count": 1, "olts": [ { "name": "olt-1", "vendor": "huawei", "protocol": "telnet", "host": "XXX.XXX.XXX.10", "port": null, "user": "admin", "svlan": 400, "iptv_vlan": 200, "comment": "", "product": "MA5608T", "added": "2026-09-01 10:00:00", "updated": "2026-09-20 09:00:00", "has_pass": true, "snmp": "v3", "last_sync": { "at": "2026-09-28 11:45:00", "ok": true, "in_sync": true } } ], "headroom": 1.1, "plans": 2}POST /olts
Section titled “POST /olts”Unless force=true, the login is tested first (and new SNMP access gets its own test read); a failed
test is 502 and nothing is stored. For an OLT this router owns (not operator), the router then
also sets the OLT’s clock source.
| Name | In | Type | Notes |
|---|---|---|---|
name |
body | string | Required. Lower-case letters, digits, ., _, -, max 32; starts with a letter or digit. |
host |
body | string | Required. IP address or hostname. |
user |
body | string | Required. |
pass |
body | string | Required. |
protocol |
body | string | telnet (default) or ssh. |
port |
body | integer | TCP port, if not the protocol’s default. |
svlan |
body | integer | This router’s S-VLAN on the OLT (1–4094, or null/"none"). |
iptv_vlan |
body | integer | IPTV VLAN (1–4094, or null/"none"). |
operator |
body | boolean | The OLT belongs to another company; the router only manages its own S-VLAN and profiles there. |
parent |
body | string | The router interface the OLT’s S-VLANs ride on (must exist). |
comment |
body | string | Free text. |
snmp |
body | object | Optional SNMP access for the licensed driver (version v3 with user and auth/priv settings, or v2c/v1 with a community). null removes it. |
force |
body | boolean | Store without the login test. |
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"name": "olt-1", "host": "XXX.XXX.XXX.10", "user": "admin", "pass": "OLT_PASSWORD", "protocol": "ssh", "svlan": 400, "iptv_vlan": 200}' \ "http://ROUTER-IP:8880/olts"{ "ok": true, "code": 201, "message": "OLT registered: olt-1", "olt": { "name": "olt-1", "host": "XXX.XXX.XXX.10", "protocol": "ssh", "svlan": 400, "has_pass": true, "snmp": null }, "probe": { "product": "MA5608T" }, "ntp": { }, "next": "dtvsol olt sync --olt olt-1 (pushes the router's plans to it)"}Errors: 400 for invalid fields, 409 when the name already exists, 502 when the login test fails
(“send force=true to store anyway”).
POST /olts/{name}
Section titled “POST /olts/{name}”Same fields as POST /olts (except name, which comes from the path). The login is tested again
unless force=true. Answers 200 with "message": "OLT updated: olt-1"; 404 for an unknown name.
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"svlan": 500, "comment": "rack 2"}' "http://ROUTER-IP:8880/olts/olt-1"DELETE /olts/{name}
Section titled “DELETE /olts/{name}”The name may also be given in the body as name (with DELETE /olts).
curl -X DELETE -H "X-API-Key: YOUR_API_KEY" "http://ROUTER-IP:8880/olts/olt-1"{ "ok": true, "code": 200, "message": "OLT removed: olt-1", "note": "its profiles on the OLT itself are left as they are"}Action API equivalents
Section titled “Action API equivalents”The action API takes its parameters in the query string. Because that puts credentials in a URL,
prefer the REST routes above; with a registered OLT, only olt=<name> is needed.
| Action | Query parameters | Same as |
|---|---|---|
action=olt-<op> (e.g. action=olt-info) |
olt, or host/user/pass; plus the operation’s arguments |
POST /olt/{op} |
action=olt-sync |
olt, dry_run |
POST /olt/sync |
action=olt-backup |
olt |
POST /olt/backup |
action=olt-backups |
olt, n |
GET /olt/backups |
action=olt-diff |
olt, rev, to |
GET /olt/diff |
action=olts-list |
— | GET /olts |
action=olts-add |
name, host, user, pass, protocol, port, svlan, iptv_vlan, … |
POST /olts |
action=olts-set |
name, fields to change |
POST /olts/{name} |
action=olts-del |
name |
DELETE /olts/{name} |