The Router Backup API Synology Didn’t Document

My router has a backup feature. It lives in the control panel under Backup & Restore — click Export, get a .dss file containing your network settings, firewall rules, DHCP reservations, DNS zones, WiFi configuration, and mesh topology. Everything you’d need to rebuild from scratch.

If you’re here for the working code, skip to the Implementation Reference at the end.

The problem: it’s manual-only. No scheduler. No “run this at 3am every morning.” The device managing my entire home network had no automated config backup, and it had been on my to-do list long enough that I’d stopped noticing it.

What followed was a two-session investigation into an API that exists but isn’t documented, an authentication mechanism that the documentation actively misrepresents, and a use case where AI collaboration was the thing that made the whole effort practical.


The API Exists

Synology’s SRM — the operating system running on the RT6600ax — shares its DNA with DSM, their NAS OS. DSM exposes a REST API that powers its own web UI, and SRM inherits a version of it. I knew this API existed. The question was whether the backup functionality was reachable through it.

Querying SYNO.API.Info — an unauthenticated endpoint that returns every available API on the device — confirmed it was:

SYNO.Backup.Config.Backup  →  entry.cgi  (versions 1–1)

The endpoint existed. So far so good.


The Documentation Stops There

Here’s where things get interesting. Synology publishes a developer guide for DSM — 223 pages covering authentication, session management, API structure, and dozens of specific namespaces. For SRM, there’s no equivalent. The router OS running in millions of homes has no public developer documentation.[1]

The official authentication guide describes a login flow: submit your username and password to SYNO.API.Auth, get a session ID back, use it for subsequent calls. Straightforward. That is the documentation.[2]

It is also wrong — or at least, incomplete to the point of being useless on SRM.


The Auth Wall

We started testing with curl. The backup service account had admin group membership in SRM. The request was well-formed. The response was error 402: permission denied.

Tried the built-in admin account. Error 400: bad credentials — except the web UI accepted the same credentials without complaint.

Over the next stretch of the session, Claude and I systematically eliminated every reasonable variable: session name (Core, SRM, RouterManagement, SurveillanceStation), HTTP vs HTTPS, port 8000 vs 8001, API version 1 vs 3, GET vs POST with URL encoding. Every combination returned 400 for the admin account while the browser accepted the same credentials without issue.

The SRM security log offered one useful clue: every successful UI login appeared as admin:. Every API attempt appeared as SYSTEM:. The router was rejecting API auth at a system level, before credential validation even happened.

We checked AutoBlock (added source IPs to the allow list — no effect). We looked at the “Allow external access to SRM” setting (WAN-only, completely irrelevant to LAN API calls). Both were dead ends.


Finding the Real Problem

With every obvious explanation exhausted, Claude queried the API info endpoint for anything encryption or token related. One name in the results stood out:

SYNO.API.Encryption

That single API name explained everything.

SRM’s web UI encrypts passwords client-side using RSA before submitting them. The server only accepts encrypted credentials. The official documentation describing plaintext authentication is DSM documentation — and while DSM supports plaintext credentials over HTTPS, SRM does not. There is no published note about this divergence anywhere.[3]

Fetching the encryption endpoint returned a 4096-bit RSA public key, a cipher field name, and a server timestamp. The browser fetches this on every login, encrypts your password with PKCS1v15, and sends ciphertext instead of plaintext. We’d been sending plaintext the whole time.


The RSA Rabbit Hole

We implemented the RSA flow manually using Python’s cryptography library: fetch the public key, encrypt the password, submit the ciphertext in the documented field. Still error 400.

Something was still missing. Claude pivoted to the synology-api Python library — a community project wrapping Synology’s APIs — and traced through its authentication module. What it revealed were two parameters appearing nowhere in any documentation: dsm_version=3 and session=webui.[4]

DSM uses dsm_version=7. SRM uses dsm_version=3. The session name webui — not Core, not SRM, not any of the names we’d tried — is what SRM expects. Neither is documented for SRM anywhere. The values were found in library source code, not in any published reference.

With those two parameters, authentication succeeded.


The Backup Flow

Once past the auth wall, SYNO.Backup.Config.Backup worked as advertised — a clean three-step async flow:

  1. Start — call with method=start. The router begins generating the archive and returns a task_id.
  2. Poll — call with method=status and the task_id until completion is confirmed.
  3. Download — call with method=download. The router returns the .dss archive.

The output is a file named SynologyRouter_YYYYMMDD.dss, roughly 91KB, containing the complete router configuration — network settings, firewall rules, DHCP reservations, DNS zones, WiFi credentials, and mesh AP config.


What This Would Have Looked Like Without Claude

This is the honest part of the post.

The technical problem here isn’t exotic. API investigation is a standard skill. The individual steps — probe the info endpoint, test auth variations, find the encryption layer, locate a community library — are each things a developer could work through independently.

But the scope would have made it impractical as a spare-evening project. Systematically testing a dozen authentication variations, recognizing SYNO.API.Encryption in a list of 100+ API names as the diagnostic key, implementing RSA in Python on the fly, debugging why the manual RSA implementation still failed, and tracing through library source code to identify two undocumented parameters — each step depends on full context from all the previous steps. Losing that thread between evenings, or spending an hour reconstructing it each time, is exactly what turns “automatable” into “perpetually on the list.”

Claude held the full investigation context across the session, made the lateral connection to SYNO.API.Encryption, knew to reach for the synology-api library when the manual implementation stalled, and identified the undocumented dsm_version and session parameters by reading the library source. No single step was magic. The value was continuity — a collaborator that didn’t lose the thread.

The router config backup now runs at 3:00 AM every night. Each .dss file lands in /mnt/user/backups/router/, where it’s swept into the existing nightly rsync to a secondary NAS. Thirty days of retention at ~91KB per file. If the router ever needs to be rebuilt, the config is there.

The gap that existed because the documentation didn’t exist is closed.


Footnotes

  1. Synology’s DSM Developer Guide (version 7, 2022) runs to 223 pages covering authentication, session management, API versioning, and namespace documentation. No equivalent document exists for SRM. Synology’s public knowledge base contains no developer-facing API reference for SRM 1.x.
  2. Synology’s DSM Login Web API Guide describes authentication as: submit account and passwd to SYNO.API.Auth via HTTP/HTTPS, receive a session ID. Client-side password encryption is not mentioned. HTTPS is referenced as the security mechanism; there is no mention of application-layer encryption of credentials.
  3. The existence of SYNO.API.Encryption is discoverable only by querying the SYNO.API.Info endpoint on the device itself — it does not appear in any Synology developer documentation, knowledge base article, or official community resource. The divergence between DSM (plaintext over HTTPS) and SRM (RSA-encrypted credentials) is nowhere documented.
  4. The synology-api Python library implements ~300 Synology APIs. Its README and supported API list do not mention SRM support, the dsm_version=3 parameter required for SRM, or session=webui as the correct session name. SYNO.Backup.Config.Backup is not among the library’s listed APIs. The working parameter combination was determined through library source code, not documentation.

Appendix: Implementation Reference

Enough detail to replicate this setup without AI assistance.

Dependencies

pip install synology-api cryptography requests

On Unraid, install to a persistent path (the root filesystem is tmpfs and doesn’t survive reboots):

pip3 install --target=/mnt/user/scripts/router-backup/lib \
  synology-api cryptography requests

Authentication

import sys, os
sys.path.insert(0, '/mnt/user/scripts/router-backup/lib')

from synology_api import base_api

session = base_api.BaseApi(
    ip_address='192.168.42.1',
    port=8000,
    username='backup',           # must be in admin group in SRM
    password=os.environ['ROUTER_BACKUP'],
    secure=False,
    cert_verify=False,
    dsm_version=3,               # SRM requires 3, not 6 or 7
    debug=False,
    otp_code=None
)
session.login('webui')           # 'webui' is required, other names return 402

Key parameters undocumented for SRM:

ParameterSRM valueDSM valueEffect of wrong value
dsm_version36 or 7Error 400
session name'webui''Core'Error 402

The service account must be a member of the admin group in SRM Control Panel → User.

Backup API Flow

import time, datetime, requests

WEBAPI = 'http://192.168.42.1:8000/webapi/entry.cgi'
sid = session.session_id

# Step 1: Start
resp = requests.post(WEBAPI, data={
    'api': 'SYNO.Backup.Config.Backup',
    'version': '1',
    'method': 'start',
    '_sid': sid,
})
task_id = resp.json()['data']['task_id']

# Step 2: Poll
while True:
    status = requests.post(WEBAPI, data={
        'api': 'SYNO.Backup.Config.Backup',
        'version': '1',
        'method': 'status',
        'task_id': task_id,
        '_sid': sid,
    }).json()
    if status['data']['state'] == 'finish':
        break
    time.sleep(2)

# Step 3: Download
response = requests.post(WEBAPI, data={
    'api': 'SYNO.Backup.Config.Backup',
    'version': '1',
    'method': 'download',
    'task_id': task_id,
    '_sid': sid,
}, stream=True)

filename = f"SynologyRouter_{datetime.date.today().strftime('%Y%m%d')}.dss"
with open(f'/mnt/user/backups/router/{filename}', 'wb') as f:
    for chunk in response.iter_content(chunk_size=8192):
        f.write(chunk)

Output and Restoration

The .dss file is a binary archive (~91KB for a moderately configured RT6600ax). Restore via SRM → Control Panel → Backup & Restore → Restore. Contents: network interfaces, DHCP reservations, firewall rules, DNS zones, WiFi SSIDs and credentials, mesh AP configuration.

Deployment

ItemValue
Script location/mnt/user/scripts/router-backup/
Dependencies/mnt/user/scripts/router-backup/lib/
Credentials.env in script dir, chmod 600
Schedule0 3 * * * via Unraid User Scripts
Output path/mnt/user/backups/router/
Retention30 most recent files (~2.7MB total)
Backup chainSwept into Tier 4 rsync to NAS2 at 5:30 AM

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *