# DSM 7 Web API reference (for the `synology` skill)

Consolidated from primary sources — kwent's device-extracted DSM 7.x `_full.json`
(method/version dump from a real device = authoritative for **names + versions**),
N4S4/`synology-api` source (param detail), and Synology's official *DSM Login Web API*
and *FileStation API* guides. Confidence per row: **[docs]** authoritative · **[comm]**
community/library · **[inf]** inferred. **[W]** = mutating → gate behind `--confirm`.
**[PKG]** = the API only appears in the device's `SYNO.API.Info` map if that package is
installed.

> **Golden rules (why the client is shaped the way it is)**
> 1. **Discover, don't hardcode.** Call `SYNO.API.Info query=all` after login and route
>    every call to the returned `path` (DSM 7 funnels most `SYNO.Core.*` through
>    `entry.cgi`; DSM 6 used per-API `.cgi`) at the advertised `maxVersion` (FileStation
>    `Upload` is the exception — use `minVersion`).
> 2. **CSRF is mandatory for writes.** Login with `enable_syno_token=yes`, then send the
>    `synotoken` as both the `X-SYNO-TOKEN` header and a `SynoToken` param on every call.
>    Without it, DSM 7 silently fails mutating calls. (The client does this.)
> 3. **Setters are read-modify-write.** Many `set` methods take a JSON blob (`conf`,
>    `profile`, `rules`, `shareinfo`, firewall/DSM `**kwargs`). Always `get` first, mutate,
>    push back — constructing from scratch wipes sibling settings.
> 4. **Param schemas are the weak spot.** Method names + versions below are
>    device-authoritative; many *setter param keys* are community/inferred. Before a
>    production write, capture the matching `get` and round-trip it, or run the call on-box
>    via `synowebapi --exec api=<API> method=<m> version=<v> k=v` (the on-box binary
>    validates params, sidestepping the gaps).
> 5. `format=sid` over `cookie` on DSM 7 (avoids cookie-jar/HTTPS issues).

## Auth & discovery — always present
| API | method | key params | ver | what | conf |
|---|---|---|---|---|---|
| SYNO.API.Info | query | `query=all` or CSV of API names | 1 | Discover APIs → `path`/`minVersion`/`maxVersion`. **Call first.** | docs |
| SYNO.API.Auth | login | `account`,`passwd`,`session`,`format=sid`,`enable_syno_token=yes`,`otp_code`,`enable_device_token=yes`,`device_id`,`device_name` | 7 (6/3 older) | Returns `data.sid` + `data.synotoken`. `device_id`/`enable_device_token` = trusted-device skip-OTP. | docs |
| SYNO.API.Auth | logout | `session` | 7 | End session. | docs |

**Auth error codes** (in `error.code` on login): 400 bad account/password · 401 disabled/guest ·
402 permission denied · **403 OTP code required** · **404 OTP code wrong** · 406 enforce-2FA (must
enable) · **407 IP auto-blocked** · 408 password expired (uncchangeable) · 409 password expired ·
410 password must change. Branch on 403/404/407 — the rest are hard failures.
**Generic codes** (any API): 100 unknown · 101 bad param · 102 API n/a · 103 method n/a · 104 version
unsupported · 105 insufficient permission · 106 session timeout · 107 dup-login interrupt · **119
invalid/missing SID or synotoken** → re-login. (Client re-logs in once on 106/119.)

## System / power / utilization — `SYNO.Core.System*`
| API | method | params | ver | what | conf |
|---|---|---|---|---|---|
| SYNO.Core.System | info | (none) | 1–2 | model, serial, firmware_ver/version_string, ram_size(MB), cpu_*, temperature, up_time, time/time_zone, ntp_server, sata_dev/usb_dev | comm |
| SYNO.Core.System | reboot | optional `force` | 1 | **[W]** reboot. *See 103 note.* | comm |
| SYNO.Core.System | shutdown | optional `force` | 1 | **[W]** power off. *See 103 note.* | comm |
| SYNO.Core.System.Utilization | get | — | 1 | live `cpu{*_load,1/5/15min}`, `memory{total_real,avail_real,real_usage,cached,...}`, `disk{total{read/write_byte},disk[]}`, `network[{device,tx,rx}]` | comm |
| SYNO.Core.System.Status | get | — | 1 | `is_system_crashed`,`is_disk_wcache_crashed`,`upgrade_ready` | comm |
| SYNO.Core.System.SystemHealth | get | — | 1 | aggregate health summary | comm |
| SYNO.Core.CurrentConnection | get *(some builds `list`)* | `offset`,`limit` | 1 | active user/file-service sessions | comm |
| SYNO.Core.System.Process / ProcessGroup | list | — | 1 | Resource Monitor processes | comm |

> **reboot/shutdown 103 regression:** confirmed API is `SYNO.Core.System` `reboot`/`shutdown`
> (NOT `Hardware.PowerSchedule` = *scheduled* on/off, NOT `Core.Upgrade`). Some DSM 7.2.x builds
> return **103 "method does not exist"** via the Web API. Mitigation: route via `entry.cgi` at
> `maxVersion` with the synotoken; on persistent 103 fall back to **SSH `synoshutdown -r|-s`**
> (the `syno-ssh.sh reboot|shutdown --confirm` recipes).

## Storage / shares / file services
| API | method | params | ver | what | conf |
|---|---|---|---|---|---|
| SYNO.Storage.CGI.Storage | load_info | — | 1 | one snapshot: `volumes[]`(id,fs_type,size.total/used,raid_type,vol_path), `disks[]`, `storagePools[]`, `ssdCaches[]`, `env`, `ha_info`. Parse defensively (sizes are byte-strings). | docs/comm |
| SYNO.Core.Share | list | `shareType`(all/dec/enc), `additional:=[…]`, `offset`,`limit` | 1 | shares + flags | docs |
| SYNO.Core.Share | get | `name`, `additional:=[…]` | 1 | one share | docs |
| SYNO.Core.Share | create / set | `name`, `shareinfo:={…}`(name,vol_path,desc,enable_recycle_bin,encryption,hidden,enable_share_quota,share_quota) | 1 | **[W]** create/modify share | comm |
| SYNO.Core.Share | delete | `name:=["s1",…]` | 1 | **[W]** delete | docs |
| SYNO.Core.Share.Permission | list | `name`,`action`(enum/find),`user_group_type`,`offset`,`limit` | 1 | per-user/group RW/RO/NA on a share | comm |
| SYNO.Core.Share.Permission | set / set_by_user_group | `name`,`user_group_type`,`permissions:=[…]` | 1 | **[W]** set share perms | comm |
| SYNO.Core.Share.Permission | list_by_group | `name`,`user_group_type`,`share_type` | 1 | shares a group can reach | comm |
| SYNO.Core.Share.Crypto | encrypt/decrypt/mount/unmount | `name`,`password` | 1 | **[W]** encryption (API partly gated → prefer CLI) | inf |
| SYNO.Core.FileServ.SMB / AFP / NFS / FTP | get / set | service config | 1–3 | **[W on set]** file-service protocol config | docs |

`Share.list` `additional` values (where perm/ACL/encryption/quota state is read):
`hidden, encryption, is_aclmode, is_support_acl, unite_permission, recyclebin, share_quota,
share_quota_used, is_sync_share, is_force_readonly, support_snapshot`. **Windows-ACL mode** is read
via `is_aclmode`+`is_support_acl`+`unite_permission` — but this only says a share *is* in ACL mode;
**the per-file ACE list is NOT in the Web API** (filesystem-only → use `synoacltool` over SSH).

## Users / groups / security / directory / certificates
| API | method | params | ver | what | conf |
|---|---|---|---|---|---|
| SYNO.Core.User | list / get | `additional:=["email","description","expired","cannot_chg_passwd","passwd_never_expire"]` | 1 | local users | docs |
| SYNO.Core.User | create / set / delete | `name`,`password`,`description`,`email`,`expired`,`cannot_chg_passwd` | 1 | **[W]** user CRUD (password set via `set`, no separate method) | docs |
| SYNO.Core.User.Group | join | `name`,`join_group:=[…]`,`leave_group:=[…]` | 1 | **[W]** add/remove user↔groups (async → `join_status task_id`) | docs |
| SYNO.Core.User.PasswordPolicy / PasswordExpiry | get / set | policy blob | 1 | **[W]** password strength/aging | docs |
| SYNO.Core.Group | list | `offset`,`limit`,`type` | 1 | groups | docs |
| SYNO.Core.Group | create / set / delete | `name`,`new_name`,`description` | 1 | **[W]** group CRUD | docs |
| SYNO.Core.Group.Member | list | `group`,`ingroup`(bool) | 1 | members | docs |
| SYNO.Core.Group.Member | change | `group`,`add_member:=[…]`,`remove_member:=[…]` | 1 | **[W]** add+remove in one call (**list-replace semantics — read first**) | docs |
| SYNO.Core.Quota | get / set | `name`,`group_quota` | 1 | **[W]** per-share group quota | docs |
| SYNO.Core.Security.Firewall | get / set | settings blob | max | **[W]** global firewall | docs |
| SYNO.Core.Security.Firewall.Rules | list / get / set / delete | `rules`(full JSON list) | max | **[W]** firewall rules (read-modify-write the whole set) | docs |
| SYNO.Core.Security.AutoBlock.Rules | get / list / set / delete | `rules` | max | **[W]** brute-force auto-block list + thresholds | docs |
| SYNO.Core.Security.DoS / Security.DSM | get / set | **kwargs | max | **[W]** DoS protect; DSM hardening (account-protection, HSTS, banner) | docs |
| SYNO.Core.OTP.EnforcePolicy / SmartBlock(.Trusted/.Untrusted/.User/.Device) | get/set | **kwargs | — | **[W]** force-2FA / Adaptive-MFA (account protection) | comm |
| SYNO.Core.Directory.Domain.Conf | get / set | `conf`(JSON: server,DNS,admin creds,NetBIOS) | max | **[W]** AD join (creds inside `conf` — capture a joined NAS's `get` first) | comm |
| SYNO.Core.Directory.LDAP.* | get / set | profile/base_dn | max | **[W]** LDAP join | comm |
| SYNO.Core.Certificate.CRT | list / set / delete | `cert_id`,`as_default`,`ids` | 1 | **[W on set/del]** list/select/delete certs | docs |
| SYNO.Core.Certificate | import / export | `serv_key`,`ser_cert`,`ca_cert`,`id`,`as_default` (multipart) | min | **[W]** upload/export cert | docs |
| SYNO.Core.Certificate.Service | set | `cert_id`,`service`,`old_id` | min | **[W]** bind cert to a service | docs |

## Packages — `SYNO.Core.Package*` (core)
| API | method | params | ver | what | conf |
|---|---|---|---|---|---|
| SYNO.Core.Package | list | `additional:=["status","installed_info"]` | 2 | installed packages + run status | comm |
| SYNO.Core.Package.Control | start / stop | `id=<pkgid>` (POST) | 1 | **[W]** start/stop a package. `id` = the package-ID **string** (`SynologyDrive`,`HyperBackup`,`LogCenter`,…) | docs |
| SYNO.Core.Package.Server | list | `blforcereload` | 2 | available catalog packages | comm |
| SYNO.Core.Package.Installation / Uninstallation | install / upload / uninstall | `url`/`name`/`id` | 1 | **[W]** install/remove | comm |

CLI fallback (most reliable): `synopkg list --name` · `synopkg status <pkgid>` · `synopkg
start|stop <pkgid>` (sudo).

## Log Center — syslog collector project [PKG: Log Center]
The **NAS-as-server** (receive pfSense/UniFi logs) is `SYNO.LogCenter.*` (needs the package). The
**NAS-forwarding-its-own-logs** is `SYNO.Core.SyslogClient.*` (core). For the on-site collector you
want the server side.
| API | method | params | ver | what | conf |
|---|---|---|---|---|---|
| SYNO.LogCenter.RecvRule | list | — | max | log-receiving rules (name, UDP/TCP/TLS, port 514, BSD/IETF format) = the "syslog server" objects | comm |
| SYNO.LogCenter.RecvRule | create / set / delete | `name`,`port`,`protocol`,`format`,`enable` | max | **[W]** create/enable a receive rule = turn the NAS into a syslog server. *Method names inferred — verify live via `apis SYNO.LogCenter.RecvRule`.* | inf |
| SYNO.LogCenter.Log | list | filter/paging | max | read received remote logs | comm |
| SYNO.LogCenter.Log | get_remotearch_subfolder | — | max | list remote-log archive subfolders | comm |
| SYNO.LogCenter.Setting.Storage | get / set | volume,retention,archive | max | **[W on set]** where received logs are stored + rotation | comm |
| SYNO.Core.SyslogClient.Status | cnt_get / eps_get | — | 1 | received-count / events-per-sec metrics | comm |
| SYNO.Core.SyslogClient.Setting(.Profile) | get / set | remote host,port,protocol,TLS,severity | 1 | **[W on set]** forward the NAS's OWN logs out | inf |

Log Center wraps **syslog-ng**; custom listeners can also be dropped in
`/etc/syslog-ng/patterndb.d/`. Prefer the RecvRule API/GUI for standup.

## Synology Drive — team-folder migration project [PKG: Synology Drive Server]
All read-only `list` (confirmed). `SYNO.C2FS.*` is C2 **cloud**, not on-prem — don't use it here.
| API | method | ver | what | conf |
|---|---|---|---|---|
| SYNO.SynologyDrive.Tasks | list | max | server-side sync tasks | comm |
| SYNO.SynologyDrive.TeamFolders | list | max | **Team Folders** (migration target) | comm |
| SYNO.SynologyDrive.Connection | list | max | connected Drive clients/devices | comm |
| SYNO.SynologyDrive.Profiles | list | max | per-user sync profiles (`user`,`start`,`limit`) | comm |
| SYNO.SynologyDrive.Settings | list | max | Drive Admin Console settings | comm |
| SYNO.SynologyDriveShareSync.Connection | list | max | server-to-server ShareSync connections | comm |

## Backup
| API | method | params | ver | what | conf |
|---|---|---|---|---|---|
| SYNO.Backup.Task | list / get / status | `task_id` | min | **[PKG: Hyper Backup]** backup tasks + run state | comm |
| SYNO.Backup.Task | backup | `task_id` | min | **[W]** run a backup task now | comm |
| SYNO.Backup.Repository | list / get | `task_id` | min | destinations/repositories (B2/Wasabi appear as target fields) | comm |
| SYNO.ActiveBackup.Task | list | `load_status`,`load_result`,`load_devices` | 1 | **[PKG: ABB — needs Btrfs]** task inventory | comm |
| SYNO.ActiveBackup.Task | backup / cancel / remove | `task_ids` | 1 | **[W]** run/cancel/delete | comm |
| SYNO.ActiveBackup.Log | list_log / list_result | `filter`,`offset`,`limit` | 1 | task logs + run history | comm |

> **ABB on ext4 — CORRECTED by live test 2026-06-24.** Active Backup for Business **is installed and
> running** on the Cascades DS718+ (ext4), and all 40 `SYNO.ActiveBackup.*` APIs are present. The
> Btrfs requirement applies only to certain ABB *features* (block-level dedup on the backup
> destination, file self-healing), NOT to installing/running the package. Don't assume absence —
> check `apis activebackup` on the device. Offsite copy still = **Hyper Backup → B2** (phase4 §6.4).

## Network / external access / services
| API | method | params | ver | what | conf |
|---|---|---|---|---|---|
| SYNO.Core.Network | get / set | hostname, dns(primary/secondary), gateway | 1 | **[W on set]** global net: hostname/DNS/default-gateway (single object — NOT per-NIC) | docs |
| SYNO.Core.Network.Interface | list | — | 1 | enumerate NICs + addrs (read-only) | docs |
| SYNO.Core.Network.Ethernet | get / set | `ifname`,`ip`,`mask`,`gateway`,`use_dhcp`,`mtu` | 1–2 | **[W on set]** per-NIC IPv4 (static/DHCP/jumbo) | docs |
| SYNO.Core.Network.Bond | list/get/create/set/delete | members, mode | 1–2 | **[W]** link-aggregation | docs |
| SYNO.Core.Network.Router.Static.Route | get / tablesget / set | route list[] | 1 | **[W on set]** static routes (there is NO `SYNO.Core.Network.Route`) | docs |
| SYNO.Core.Terminal | get / set | `enable_ssh`,`enable_telnet`,`ssh_port` | 3 | **[W on set]** enable SSH/Telnet, SSH port | docs |
| SYNO.Core.SNMP | get / set | `enable_snmp`,`community`,`snmp_port`,v3 user/auth/priv,trap | 1 | **[W on set]** SNMP | docs |
| SYNO.Core.QuickConnect | get / status / set | `enabled`,`quickconnect_id` | 1–3 | **[W on set]** QuickConnect | docs |
| SYNO.Core.DDNS.Record | list / create / set / delete / update_ip_address | provider,hostname,user,passwd | 1 | **[W on writes]** DDNS records | docs |
| SYNO.Core.PortForwarding.Rules | load / save | rules[] | 1 | **[W on save]** UPnP router port-forwards | docs |
| SYNO.Core.Notification.Mail.Conf / .Mail.Auth | get / set | mailserver,smtp_port,mailaddr,user,passwd,ssl | 1 | **[W on set]** SMTP for alerts | docs |
| SYNO.Core.Notification.Mail | send_test | — | 1 | **[W]** (actually emails) test mail | docs |
| SYNO.Core.Notification.Push.Conf | get / set / status | `enable_mobile`,`enable_mail`,`enable_webhook` | 1 | **[W on set]** push toggles | docs |
| SYNO.Core.Notification.Advance.FilterSettings | get / set / list | per-event rules | 1 | **[W on set]** alert rules (NOT `Notification.Rule`) | docs |
| SYNO.Core.TaskScheduler | list / get / run / set_enable / create / set / delete | id, real_owner, task object | 1–2 | **[W on writes]** scheduled tasks | docs |
| SYNO.Core.TaskScheduler.Root | create / set | `task_name`,`owner=root`,`script`,`run_days`,`start_time_h/m`,`same_day_repeat_*` | 1 | **[W]** root user-script task — **the way to schedule a syslog snapshotter as root** | comm |
| SYNO.Core.BandwidthControl | list/get/set | rules[] | 1–2 | **[W on set]** bandwidth rules | docs |
| SYNO.Backup.Service.NetworkBackup *(or SYNO.Core.FileServ.Rsync)* | get / set | `enable_network_backup` | 1 | **[W on set]** rsync server enable (probe which the device returns) | comm |

## SSH `syno*` CLI surface (what the Web API can't fully express)
- **`synoacltool -get <path>`** — per-file NTFS ACE list (the data Web API lacks). ACE format:
  `[idx] <type>:<name>:<allow|deny>:<perm-bits rwxpdDaARWcCo>:<inherit fdin> (level: N)`; level 0 =
  set on this path, ≥1 = inherited. `-add`/`-del <idx>`/`-enforce-inherit` to write.
- **`synoshare --enum ALL|ENC|DEC`** (list, incl. encrypted/plain split) · `--get <share>` ·
  `--list_acl <share>` · `--enc_mount/--enc_unmount <share> <pw>` (the dependable encryption path).
- **`synouser`** / **`synogroup`** — local account CLI (line-oriented `key=value`, not JSON);
  `--add` is **list-replace** for group members; run `--rebuild all` after manual edits.
- **`synopkg start|stop|status <pkgid>`** — package control fallback.
- **`synonet --get_all` / `--hostname` / `--gateway` / `--dns` / `--restart <iface>`** — network.
- **`synoservice --enable|--disable|--restart sshd|snmpd|rsyncd|crond`** — service toggles.
- **`synoschedtask --get | --run --id=<N>`** — scheduled tasks.
- **`synogetkeyvalue / synosetkeyvalue /etc/synoinfo.conf <key> [val]`** — raw config.
- **`synowebapi --exec api=<API> method=<m> version=<v> k=v`** — call ANY Web API on-box; the
  binary validates params, so this is the most robust scripted path for setters whose param schema
  isn't publicly documented. **Use this for production writes when unsure of param keys.**
- **`synoshutdown -r` (reboot) / `-s` (shutdown)** — power fallback when the Web API 103s.

## Sources
- kwent/syno `definitions/7.x/_full.json` (device-extracted method+version dump — authority for names/versions)
- N4S4/synology-api source + https://n4s4.github.io/synology-api/docs/apis (param detail)
- Synology *DSM Login Web API Guide* + *File Station API Guide* (PDFs)
- Synology KB: Log Center server, Terminal & SNMP, Task Scheduler, Key Manager (DSM 7 help tree)
- zub2/synoacl (synoacltool ACE format); ordinoscope.net (synoshare/synoacltool CLI)
