# DashCaddy API Surface > **Generated:** 2026-07-13 > **Total routes:** 285 > **Files scanned:** 47 > **Source of truth:** router.* registrations in `dashcaddy-api/routes/` + root paths in `src/app.js` ## Auth & Rate Limit Model **Auth classification:** - `public` = in `PUBLIC_ROUTES` allowlist (`src/utilities/middleware.js:310-364`), bypasses TOTP - `protected` = requires valid TOTP session cookie (`dashcaddy_session`) OR API key/JWT token **Rate limits** (from `RATE_LIMITS` in `src/utilities/constants.js:69`): - `GENERAL` = 1000 req / 15 min / IP — default for all `/api/v1/*` - `STRICT` = 20 req / 15 min / IP — auth key endpoints (`/auth/keys`, `/auth/jwt`, `/auth/gate`, `/auth/app-token`) - `TOTP` = 10 req / 15 min / IP — TOTP verify/setup **CSRF:** TOTP session uses double-submit cookie pattern. State-changing requests (POST/PUT/DELETE/PATCH) require `X-CSRF-Token` header matching the `csrf_token` cookie. --- ## Summary by Area | Area | Routes | Public | Protected | |---|---:|---:|---:| | App catalog | 28 | 0 | 28 | | Tailscale | 20 | 20 | 0 | | Backups | 19 | 0 | 19 | | DNS | 19 | 0 | 19 | | Monitoring | 19 | 3 | 16 | | Updates | 16 | 6 | 10 | | Authentication | 15 | 10 | 5 | | Logs | 15 | 0 | 15 | | Configuration | 13 | 9 | 4 | | Health | 12 | 2 | 10 | | Services | 12 | 4 | 8 | | Containers (lifecycle) | 10 | 0 | 10 | | Core / system | 9 | 6 | 3 | | Dependencies | 8 | 0 | 8 | | Notifications | 8 | 0 | 8 | | App recipes | 8 | 0 | 8 | | Docker resources | 7 | 0 | 7 | | Caddy / sites | 7 | 0 | 7 | | Updates / workflows | 6 | 0 | 6 | | Auto-restart | 5 | 0 | 5 | | Certificate authority | 5 | 5 | 0 | | OpenClaw integration | 5 | 0 | 5 | | Config drift | 4 | 0 | 4 | | Licensing | 4 | 2 | 2 | | File browser | 3 | 0 | 3 | | Theming | 3 | 1 | 2 | | Service credentials | 2 | 0 | 2 | | Events | 2 | 0 | 2 | | Internal helpers | 1 | 0 | 1 | | **TOTAL** | **285** | **68** | **217** | ## App catalog _28 routes_ | Method | Full path | Auth | Rate limit | Defined in | |---|---|---|---|---| | DELETE | `/api/v1/:appId` | protected | GENERAL (1000/15m) | `routes/apps/removal.js:39` | | GET | `/api/v1/:appId/backup-points` | protected | GENERAL (1000/15m) | `routes/apps/restore.js:131` | | POST | `/api/v1/:appId/restore` | protected | GENERAL (1000/15m) | `routes/apps/restore.js:38` | | POST | `/api/v1/:appId/revert/:filename` | protected | GENERAL (1000/15m) | `routes/apps/restore.js:185` | | POST | `/api/v1/arr/auto-setup` | protected | GENERAL (1000/15m) | `routes/arr/config.js:282` | | POST | `/api/v1/arr/configure-overseerr` | protected | GENERAL (1000/15m) | `routes/arr/config.js:27` | | GET | `/api/v1/arr/credentials` | protected | GENERAL (1000/15m) | `routes/arr/credentials.js:109` | | POST | `/api/v1/arr/credentials` | protected | GENERAL (1000/15m) | `routes/arr/credentials.js:21` | | DELETE | `/api/v1/arr/credentials/:service` | protected | GENERAL (1000/15m) | `routes/arr/credentials.js:134` | | GET | `/api/v1/arr/detect` | protected | GENERAL (1000/15m) | `routes/arr/detect.js:20` | | GET | `/api/v1/arr/quality-profiles` | protected | GENERAL (1000/15m) | `routes/arr/config.js:497` | | POST | `/api/v1/arr/quality-profiles` | protected | GENERAL (1000/15m) | `routes/arr/config.js:566` | | POST | `/api/v1/arr/smart-connect` | protected | GENERAL (1000/15m) | `routes/arr/smart-connect.js:26` | | GET | `/api/v1/arr/smart-detect` | protected | GENERAL (1000/15m) | `routes/arr/detect.js:78` | | POST | `/api/v1/arr/test-connection` | protected | GENERAL (1000/15m) | `routes/arr/config.js:208` | | POST | `/api/v1/check-existing` | protected | GENERAL (1000/15m) | `routes/apps/deploy.js:241` | | DELETE | `/api/v1/compose-stack/:stackName` | protected | GENERAL (1000/15m) | `routes/apps/compose.js:308` | | POST | `/api/v1/deploy` | protected | GENERAL (1000/15m) | `routes/apps/deploy.js:254` | | POST | `/api/v1/deploy-compose` | protected | GENERAL (1000/15m) | `routes/apps/compose.js:170` | | POST | `/api/v1/import-compose` | protected | GENERAL (1000/15m) | `routes/apps/compose.js:159` | | GET | `/api/v1/plex/libraries` | protected | GENERAL (1000/15m) | `routes/arr/plex.js:26` | | GET | `/api/v1/ports/:basePort/suggest` | protected | GENERAL (1000/15m) | `routes/apps/templates.js:77` | | GET | `/api/v1/ports/:port/check` | protected | GENERAL (1000/15m) | `routes/apps/templates.js:65` | | POST | `/api/v1/restore-all` | protected | GENERAL (1000/15m) | `routes/apps/restore.js:58` | | GET | `/api/v1/restore-status` | protected | GENERAL (1000/15m) | `routes/apps/restore.js:97` | | GET | `/api/v1/templates` | protected | GENERAL (1000/15m) | `routes/apps/templates.js:45` | | GET | `/api/v1/templates/:appId` | protected | GENERAL (1000/15m) | `routes/apps/templates.js:54` | | POST | `/api/v1/update-subdomain` | protected | GENERAL (1000/15m) | `routes/apps/templates.js:91` | ## Tailscale _20 routes_ | Method | Full path | Auth | Rate limit | Defined in | |---|---|---|---|---| | GET | `/api/v1/tailscale/acl` | public | GENERAL (1000/15m) | `routes/tailscale.js:301` | | GET | `/api/v1/tailscale/admin/devices` | public | GENERAL (1000/15m) | `routes/tailscale-admin.js:154` | | DELETE | `/api/v1/tailscale/admin/devices/:id` | public | GENERAL (1000/15m) | `routes/tailscale-admin.js:170` | | GET | `/api/v1/tailscale/admin/keys` | public | GENERAL (1000/15m) | `routes/tailscale-admin.js:202` | | POST | `/api/v1/tailscale/admin/keys` | public | GENERAL (1000/15m) | `routes/tailscale-admin.js:211` | | DELETE | `/api/v1/tailscale/admin/keys/:id` | public | GENERAL (1000/15m) | `routes/tailscale-admin.js:237` | | GET | `/api/v1/tailscale/admin/users` | public | GENERAL (1000/15m) | `routes/tailscale-admin.js:191` | | GET | `/api/v1/tailscale/api-devices` | public | GENERAL (1000/15m) | `routes/tailscale.js:274` | | GET | `/api/v1/tailscale/check-connection` | public | GENERAL (1000/15m) | `routes/tailscale.js:96` | | POST | `/api/v1/tailscale/config` | public | GENERAL (1000/15m) | `routes/tailscale.js:80` | | GET | `/api/v1/tailscale/devices` | public | GENERAL (1000/15m) | `routes/tailscale.js:113` | | DELETE | `/api/v1/tailscale/oauth-config` | public | GENERAL (1000/15m) | `routes/tailscale.js:259` | | POST | `/api/v1/tailscale/oauth-config` | public | GENERAL (1000/15m) | `routes/tailscale.js:201` | | POST | `/api/v1/tailscale/protect-service` | public | GENERAL (1000/15m) | `routes/tailscale.js:147` | | DELETE | `/api/v1/tailscale/settings` | public | GENERAL (1000/15m) | `routes/tailscale-admin.js:124` | | GET | `/api/v1/tailscale/settings` | public | GENERAL (1000/15m) | `routes/tailscale-admin.js:60` | | PUT | `/api/v1/tailscale/settings` | public | GENERAL (1000/15m) | `routes/tailscale-admin.js:76` | | POST | `/api/v1/tailscale/settings/test` | public | GENERAL (1000/15m) | `routes/tailscale-admin.js:131` | | GET | `/api/v1/tailscale/status` | public | GENERAL (1000/15m) | `routes/tailscale.js:36` | | POST | `/api/v1/tailscale/sync` | public | GENERAL (1000/15m) | `routes/tailscale.js:287` | ## Backups _19 routes_ | Method | Full path | Auth | Rate limit | Defined in | |---|---|---|---|---| | POST | `/api/v1/backups/backup/:appId` | protected | GENERAL (1000/15m) | `routes/backups.js:161` | | POST | `/api/v1/backups/compare/:filename` | protected | GENERAL (1000/15m) | `routes/backups.js:373` | | GET | `/api/v1/backups/config` | protected | GENERAL (1000/15m) | `routes/backups.js:480` | | POST | `/api/v1/backups/config` | protected | GENERAL (1000/15m) | `routes/backups.js:486` | | DELETE | `/api/v1/backups/credentials/:provider` | protected | GENERAL (1000/15m) | `routes/backups.js:631` | | GET | `/api/v1/backups/credentials/:provider` | protected | GENERAL (1000/15m) | `routes/backups.js:558` | | POST | `/api/v1/backups/credentials/:provider` | protected | GENERAL (1000/15m) | `routes/backups.js:590` | | POST | `/api/v1/backups/execute` | protected | GENERAL (1000/15m) | `routes/backups.js:492` | | GET | `/api/v1/backups/files` | protected | GENERAL (1000/15m) | `routes/backups.js:118` | | GET | `/api/v1/backups/files/:appId` | protected | GENERAL (1000/15m) | `routes/backups.js:189` | | GET | `/api/v1/backups/history` | protected | GENERAL (1000/15m) | `routes/backups.js:498` | | POST | `/api/v1/backups/restore-file/:filename` | protected | GENERAL (1000/15m) | `routes/backups.js:237` | | POST | `/api/v1/backups/restore/:backupId` | protected | GENERAL (1000/15m) | `routes/backups.js:538` | | GET | `/api/v1/backups/schedule` | protected | GENERAL (1000/15m) | `routes/backups.js:29` | | POST | `/api/v1/backups/schedule` | protected | GENERAL (1000/15m) | `routes/backups.js:59` | | POST | `/api/v1/backups/schedule` | protected | GENERAL (1000/15m) | `routes/backups.js:511` | | DELETE | `/api/v1/backups/schedule/:appId` | protected | GENERAL (1000/15m) | `routes/backups.js:102` | | GET | `/api/v1/backups/storage-info` | protected | GENERAL (1000/15m) | `routes/backups.js:505` | | POST | `/api/v1/backups/test-destination` | protected | GENERAL (1000/15m) | `routes/backups.js:546` | ## DNS _19 routes_ | Method | Full path | Auth | Rate limit | Defined in | |---|---|---|---|---| | GET | `/api/v1/dns/check-update` | protected | GENERAL (1000/15m) | `routes/dns.js:669` | | DELETE | `/api/v1/dns/credentials` | protected | GENERAL (1000/15m) | `routes/dns.js:597` | | POST | `/api/v1/dns/credentials` | protected | GENERAL (1000/15m) | `routes/dns.js:490` | | GET | `/api/v1/dns/logs` | protected | GENERAL (1000/15m) | `routes/dns.js:337` | | GET | `/api/v1/dns/propagation` | protected | GENERAL (1000/15m) | `routes/dns.js:802` | | GET | `/api/v1/dns/propagation/:domain` | protected | GENERAL (1000/15m) | `routes/dns.js:847` | | POST | `/api/v1/dns/propagation/verify` | protected | GENERAL (1000/15m) | `routes/dns.js:815` | | GET | `/api/v1/dns/provider/status` | protected | GENERAL (1000/15m) | `routes/dns.js:55` | | GET | `/api/v1/dns/providers` | protected | GENERAL (1000/15m) | `routes/dns.js:48` | | DELETE | `/api/v1/dns/record` | protected | GENERAL (1000/15m) | `routes/dns.js:176` | | POST | `/api/v1/dns/record` | protected | GENERAL (1000/15m) | `routes/dns.js:225` | | POST | `/api/v1/dns/refresh-token` | protected | GENERAL (1000/15m) | `routes/dns.js:655` | | GET | `/api/v1/dns/resolve` | protected | GENERAL (1000/15m) | `routes/dns.js:292` | | POST | `/api/v1/dns/restart/:dnsId` | protected | GENERAL (1000/15m) | `routes/dns.js:621` | | GET | `/api/v1/dns/token-status` | protected | GENERAL (1000/15m) | `routes/dns.js:474` | | DELETE | `/api/v1/dns/universal/record` | protected | GENERAL (1000/15m) | `routes/dns.js:119` | | POST | `/api/v1/dns/universal/record` | protected | GENERAL (1000/15m) | `routes/dns.js:71` | | GET | `/api/v1/dns/universal/resolve` | protected | GENERAL (1000/15m) | `routes/dns.js:145` | | POST | `/api/v1/dns/update` | protected | GENERAL (1000/15m) | `routes/dns.js:732` | ## Monitoring _19 routes_ | Method | Full path | Auth | Rate limit | Defined in | |---|---|---|---|---| | GET | `/api/v1/certificates` | protected | GENERAL (1000/15m) | `routes/ssl-monitor.js:26` | | GET | `/api/v1/certificates/:serviceId` | protected | GENERAL (1000/15m) | `routes/ssl-monitor.js:35` | | POST | `/api/v1/check` | protected | GENERAL (1000/15m) | `routes/ssl-monitor.js:50` | | POST | `/api/v1/check/:serviceId` | protected | GENERAL (1000/15m) | `routes/ssl-monitor.js:59` | | GET | `/api/v1/config` | public | GENERAL (1000/15m) | `routes/ssl-monitor.js:80` | | POST | `/api/v1/config` | public | GENERAL (1000/15m) | `routes/ssl-monitor.js:90` | | GET | `/api/v1/monitoring/aggregated/:containerId` | protected | GENERAL (1000/15m) | `routes/monitoring.js:72` | | GET | `/api/v1/monitoring/alerts` | protected | GENERAL (1000/15m) | `routes/monitoring.js:104` | | DELETE | `/api/v1/monitoring/alerts/:containerId` | protected | GENERAL (1000/15m) | `routes/monitoring.js:174` | | GET | `/api/v1/monitoring/alerts/:containerId` | protected | GENERAL (1000/15m) | `routes/monitoring.js:168` | | POST | `/api/v1/monitoring/alerts/:containerId` | protected | GENERAL (1000/15m) | `routes/monitoring.js:162` | | POST | `/api/v1/monitoring/alerts/:containerId/test` | protected | GENERAL (1000/15m) | `routes/monitoring.js:111` | | GET | `/api/v1/monitoring/alerts/config` | protected | GENERAL (1000/15m) | `routes/monitoring.js:85` | | POST | `/api/v1/monitoring/alerts/config` | protected | GENERAL (1000/15m) | `routes/monitoring.js:91` | | GET | `/api/v1/monitoring/history/:containerId` | protected | GENERAL (1000/15m) | `routes/monitoring.js:49` | | GET | `/api/v1/monitoring/stats` | public | GENERAL (1000/15m) | `routes/monitoring.js:20` | | GET | `/api/v1/monitoring/stats/:containerId` | protected | GENERAL (1000/15m) | `routes/monitoring.js:38` | | GET | `/api/v1/stats/container/:id` | protected | GENERAL (1000/15m) | `routes/monitoring.js:240` | | GET | `/api/v1/stats/containers` | protected | GENERAL (1000/15m) | `routes/monitoring.js:182` | ## Updates _16 routes_ | Method | Full path | Auth | Rate limit | Defined in | |---|---|---|---|---| | POST | `/api/v1/system/rollback` | protected | GENERAL (1000/15m) | `routes/updates.js:164` | | GET | `/api/v1/system/rollback-versions` | protected | GENERAL (1000/15m) | `routes/updates.js:158` | | POST | `/api/v1/system/update-apply` | protected | GENERAL (1000/15m) | `routes/updates.js:95` | | GET | `/api/v1/system/update-check` | public | GENERAL (1000/15m) | `routes/updates.js:89` | | GET | `/api/v1/system/update-history` | public | GENERAL (1000/15m) | `routes/updates.js:152` | | POST | `/api/v1/system/update-notify` | public | GENERAL (1000/15m) | `routes/updates.js:126` | | GET | `/api/v1/system/update-status` | public | GENERAL (1000/15m) | `routes/updates.js:143` | | GET | `/api/v1/system/version` | public | GENERAL (1000/15m) | `routes/updates.js:83` | | GET | `/api/v1/updates/auto-update` | protected | GENERAL (1000/15m) | `routes/updates.js:65` | | POST | `/api/v1/updates/auto-update/:containerId` | protected | GENERAL (1000/15m) | `routes/updates.js:59` | | GET | `/api/v1/updates/available` | public | GENERAL (1000/15m) | `routes/updates.js:29` | | POST | `/api/v1/updates/check` | protected | GENERAL (1000/15m) | `routes/updates.js:22` | | GET | `/api/v1/updates/history` | protected | GENERAL (1000/15m) | `routes/updates.js:49` | | POST | `/api/v1/updates/rollback/:containerId` | protected | GENERAL (1000/15m) | `routes/updates.js:43` | | POST | `/api/v1/updates/schedule/:containerId` | protected | GENERAL (1000/15m) | `routes/updates.js:71` | | POST | `/api/v1/updates/update/:containerId` | protected | GENERAL (1000/15m) | `routes/updates.js:37` | ## Authentication _15 routes_ | Method | Full path | Auth | Rate limit | Defined in | |---|---|---|---|---| | GET | `/api/v1/auth/app-token/:serviceId` | public | STRICT (20/15m) | `routes/auth/sso-gate.js:104` | | GET | `/api/v1/auth/gate/:serviceId` | public | STRICT (20/15m) | `routes/auth/sso-gate.js:26` | | POST | `/api/v1/auth/jwt` | protected | STRICT (20/15m) | `routes/auth/keys.js:103` | | GET | `/api/v1/auth/keys` | protected | STRICT (20/15m) | `routes/auth/keys.js:36` | | POST | `/api/v1/auth/keys` | protected | STRICT (20/15m) | `routes/auth/keys.js:47` | | DELETE | `/api/v1/auth/keys/:keyId` | protected | STRICT (20/15m) | `routes/auth/keys.js:81` | | GET | `/api/v1/auth/login-page` | public | GENERAL (1000/15m) | `routes/auth/sso-gate.js:206` | | GET | `/api/v1/totp/check-session` | public | TOTP (10/15m) | `routes/auth/totp.js:228` | | GET | `/api/v1/totp/config` | public | TOTP (10/15m) | `routes/auth/totp.js:30` | | POST | `/api/v1/totp/config` | public | TOTP (10/15m) | `routes/auth/totp.js:286` | | POST | `/api/v1/totp/disable` | protected | TOTP (10/15m) | `routes/auth/totp.js:253` | | GET | `/api/v1/totp/recovery-info` | public | TOTP (10/15m) | `routes/auth/totp.js:56` | | POST | `/api/v1/totp/setup` | public | TOTP (10/15m) | `routes/auth/totp.js:116` | | POST | `/api/v1/totp/verify` | public | TOTP (10/15m) | `routes/auth/totp.js:194` | | POST | `/api/v1/totp/verify-setup` | public | TOTP (10/15m) | `routes/auth/totp.js:157` | ## Logs _15 routes_ | Method | Full path | Auth | Rate limit | Defined in | |---|---|---|---|---| | DELETE | `/api/v1/audit-logs` | protected | GENERAL (1000/15m) | `routes/errorlogs.js:71` | | GET | `/api/v1/audit-logs` | protected | GENERAL (1000/15m) | `routes/errorlogs.js:55` | | DELETE | `/api/v1/error-logs` | protected | GENERAL (1000/15m) | `routes/errorlogs.js:47` | | GET | `/api/v1/error-logs` | protected | GENERAL (1000/15m) | `routes/errorlogs.js:20` | | GET | `/api/v1/logs/container/:id` | protected | GENERAL (1000/15m) | `routes/logs.js:39` | | GET | `/api/v1/logs/containers` | protected | GENERAL (1000/15m) | `routes/logs.js:23` | | GET | `/api/v1/logs/digest/:date` | protected | GENERAL (1000/15m) | `routes/logs.js:184` | | POST | `/api/v1/logs/digest/generate` | protected | GENERAL (1000/15m) | `routes/logs.js:176` | | GET | `/api/v1/logs/digest/history` | protected | GENERAL (1000/15m) | `routes/logs.js:169` | | GET | `/api/v1/logs/digest/latest` | protected | GENERAL (1000/15m) | `routes/logs.js:152` | | GET | `/api/v1/logs/digest/live` | protected | GENERAL (1000/15m) | `routes/logs.js:162` | | GET | `/api/v1/logs/docker-disk` | protected | GENERAL (1000/15m) | `routes/logs.js:203` | | POST | `/api/v1/logs/docker-maintenance` | protected | GENERAL (1000/15m) | `routes/logs.js:211` | | GET | `/api/v1/logs/file` | protected | GENERAL (1000/15m) | `routes/logs.js:218` | | GET | `/api/v1/logs/stream/:id` | protected | GENERAL (1000/15m) | `routes/logs.js:93` | ## Configuration _13 routes_ | Method | Full path | Auth | Rate limit | Defined in | |---|---|---|---|---| | POST | `/api/v1/assets/upload` | protected | GENERAL (1000/15m) | `routes/config/assets.js:33` | | GET | `/api/v1/backup/export` | protected | GENERAL (1000/15m) | `routes/config/backup.js:51` | | POST | `/api/v1/backup/preview` | protected | GENERAL (1000/15m) | `routes/config/backup.js:153` | | POST | `/api/v1/backup/restore` | protected | GENERAL (1000/15m) | `routes/config/backup.js:218` | | DELETE | `/api/v1/config` | public | GENERAL (1000/15m) | `routes/config/settings.js:78` | | GET | `/api/v1/config` | public | GENERAL (1000/15m) | `routes/config/settings.js:26` | | POST | `/api/v1/config` | public | GENERAL (1000/15m) | `routes/config/settings.js:35` | | DELETE | `/api/v1/favicon` | public | GENERAL (1000/15m) | `routes/config/assets.js:272` | | GET | `/api/v1/favicon` | public | GENERAL (1000/15m) | `routes/config/assets.js:203` | | POST | `/api/v1/favicon` | public | GENERAL (1000/15m) | `routes/config/assets.js:212` | | DELETE | `/api/v1/logo` | public | GENERAL (1000/15m) | `routes/config/assets.js:170` | | GET | `/api/v1/logo` | public | GENERAL (1000/15m) | `routes/config/assets.js:77` | | POST | `/api/v1/logo` | public | GENERAL (1000/15m) | `routes/config/assets.js:112` | ## Health _12 routes_ | Method | Full path | Auth | Rate limit | Defined in | |---|---|---|---|---| | DELETE | `/api/v1/health-checks/:serviceId/configure` | protected | GENERAL (1000/15m) | `routes/health.js:357` | | POST | `/api/v1/health-checks/:serviceId/configure` | protected | GENERAL (1000/15m) | `routes/health.js:351` | | GET | `/api/v1/health-checks/:serviceId/stats` | protected | GENERAL (1000/15m) | `routes/health.js:340` | | GET | `/api/v1/health-checks/incidents` | protected | GENERAL (1000/15m) | `routes/health.js:363` | | GET | `/api/v1/health-checks/incidents/history` | protected | GENERAL (1000/15m) | `routes/health.js:371` | | GET | `/api/v1/health-checks/status` | public | GENERAL (1000/15m) | `routes/health.js:319` | | GET | `/api/v1/health/ca` | public | GENERAL (1000/15m) | `routes/health.js:267` | | GET | `/api/v1/health/cached` | protected | GENERAL (1000/15m) | `routes/health.js:179` | | GET | `/api/v1/health/probe` | protected | GENERAL (1000/15m) | `routes/health.js:230` | | GET | `/api/v1/health/pylon` | protected | GENERAL (1000/15m) | `routes/health.js:245` | | GET | `/api/v1/health/service/:id` | protected | GENERAL (1000/15m) | `routes/health.js:188` | | GET | `/api/v1/health/services` | protected | GENERAL (1000/15m) | `routes/health.js:109` | ## Services _12 routes_ | Method | Full path | Auth | Rate limit | Defined in | |---|---|---|---|---| | DELETE | `/api/v1/seedhost-creds` | protected | GENERAL (1000/15m) | `routes/services.js:310` | | GET | `/api/v1/seedhost-creds` | protected | GENERAL (1000/15m) | `routes/services.js:289` | | POST | `/api/v1/seedhost-creds` | protected | GENERAL (1000/15m) | `routes/services.js:272` | | GET | `/api/v1/services` | public | GENERAL (1000/15m) | `routes/services.js:374` | | POST | `/api/v1/services` | public | GENERAL (1000/15m) | `routes/services.js:389` | | PUT | `/api/v1/services` | public | GENERAL (1000/15m) | `routes/services.js:434` | | DELETE | `/api/v1/services/:id` | protected | GENERAL (1000/15m) | `routes/services.js:462` | | DELETE | `/api/v1/services/:serviceId/credentials` | protected | GENERAL (1000/15m) | `routes/services.js:237` | | GET | `/api/v1/services/:serviceId/credentials` | protected | GENERAL (1000/15m) | `routes/services.js:252` | | POST | `/api/v1/services/:serviceId/credentials` | protected | GENERAL (1000/15m) | `routes/services.js:213` | | GET | `/api/v1/services/status` | public | GENERAL (1000/15m) | `routes/services.js:327` | | POST | `/api/v1/services/update` | protected | GENERAL (1000/15m) | `routes/services.js:486` | ## Containers (lifecycle) _10 routes_ | Method | Full path | Auth | Rate limit | Defined in | |---|---|---|---|---| | DELETE | `/api/v1/containers/:id` | protected | GENERAL (1000/15m) | `routes/containers.js:235` | | GET | `/api/v1/containers/:id/check-update` | protected | GENERAL (1000/15m) | `routes/containers.js:155` | | GET | `/api/v1/containers/:id/logs` | protected | GENERAL (1000/15m) | `routes/containers.js:193` | | GET | `/api/v1/containers/:id/resources` | protected | GENERAL (1000/15m) | `routes/containers.js:223` | | PUT | `/api/v1/containers/:id/resources` | protected | GENERAL (1000/15m) | `routes/containers.js:205` | | POST | `/api/v1/containers/:id/restart` | protected | GENERAL (1000/15m) | `routes/containers.js:48` | | POST | `/api/v1/containers/:id/start` | protected | GENERAL (1000/15m) | `routes/containers.js:34` | | POST | `/api/v1/containers/:id/stop` | protected | GENERAL (1000/15m) | `routes/containers.js:41` | | POST | `/api/v1/containers/:id/update` | protected | GENERAL (1000/15m) | `routes/containers.js:55` | | GET | `/api/v1/containers/discover` | protected | GENERAL (1000/15m) | `routes/containers.js:242` | ## Core / system _9 routes_ | Method | Full path | Auth | Rate limit | Defined in | |---|---|---|---|---| | GET | `/api/v1/docs` | protected | GENERAL (1000/15m) | `src/app.js:925` | | GET | `/api/v1/docs/spec` | protected | GENERAL (1000/15m) | `src/app.js:943` | | GET | `/api/v1/network/ips` | protected | GENERAL (1000/15m) | `src/app.js:899` | | GET | `/health` | public | GENERAL (1000/15m) | `src/app.js:777` | | GET | `/health/live` | public | GENERAL (1000/15m) | `src/app.js:778` | | GET | `/health/ready` | public | GENERAL (1000/15m) | `src/app.js:782` | | GET | `/healthz` | public | GENERAL (1000/15m) | `src/app.js:779` | | GET | `/probe/:id` | public | GENERAL (1000/15m) | `src/app.js:786` | | GET | `/readyz` | public | GENERAL (1000/15m) | `src/app.js:783` | ## Dependencies _8 routes_ | Method | Full path | Auth | Rate limit | Defined in | |---|---|---|---|---| | DELETE | `/api/v1/dependencies/:serviceId` | protected | GENERAL (1000/15m) | `routes/dependencies.js:166` | | GET | `/api/v1/dependencies/:serviceId` | protected | GENERAL (1000/15m) | `routes/dependencies.js:80` | | POST | `/api/v1/dependencies/:serviceId` | protected | GENERAL (1000/15m) | `routes/dependencies.js:123` | | GET | `/api/v1/dependencies/:serviceId/chain` | protected | GENERAL (1000/15m) | `routes/dependencies.js:105` | | POST | `/api/v1/dependencies/:serviceId/restart` | protected | GENERAL (1000/15m) | `routes/dependencies.js:198` | | GET | `/api/v1/dependencies/:serviceId/status` | protected | GENERAL (1000/15m) | `routes/dependencies.js:114` | | GET | `/api/v1/dependencies/graph` | protected | GENERAL (1000/15m) | `routes/dependencies.js:48` | | GET | `/api/v1/dependencies/validate` | protected | GENERAL (1000/15m) | `routes/dependencies.js:56` | ## Notifications _8 routes_ | Method | Full path | Auth | Rate limit | Defined in | |---|---|---|---|---| | GET | `/api/v1/notifications/config` | protected | GENERAL (1000/15m) | `routes/notifications.js:20` | | POST | `/api/v1/notifications/config` | protected | GENERAL (1000/15m) | `routes/notifications.js:53` | | POST | `/api/v1/notifications/health-check` | protected | GENERAL (1000/15m) | `routes/notifications.js:214` | | DELETE | `/api/v1/notifications/history` | protected | GENERAL (1000/15m) | `routes/notifications.js:208` | | GET | `/api/v1/notifications/history` | protected | GENERAL (1000/15m) | `routes/notifications.js:192` | | POST | `/api/v1/notifications/send` | protected | GENERAL (1000/15m) | `routes/notifications.js:246` | | GET | `/api/v1/notifications/status` | protected | GENERAL (1000/15m) | `routes/notifications.js:224` | | POST | `/api/v1/notifications/test` | protected | GENERAL (1000/15m) | `routes/notifications.js:159` | ## App recipes _8 routes_ | Method | Full path | Auth | Rate limit | Defined in | |---|---|---|---|---| | DELETE | `/api/v1/:recipeId` | protected | GENERAL (1000/15m) | `routes/recipes/manage.js:197` | | POST | `/api/v1/:recipeId/restart` | protected | GENERAL (1000/15m) | `routes/recipes/manage.js:171` | | POST | `/api/v1/:recipeId/start` | protected | GENERAL (1000/15m) | `routes/recipes/manage.js:108` | | POST | `/api/v1/:recipeId/stop` | protected | GENERAL (1000/15m) | `routes/recipes/manage.js:139` | | POST | `/api/v1/deploy` | protected | GENERAL (1000/15m) | `routes/recipes/deploy.js:29` | | GET | `/api/v1/deployed` | protected | GENERAL (1000/15m) | `routes/recipes/manage.js:24` | | GET | `/api/v1/templates` | protected | GENERAL (1000/15m) | `routes/recipes/index.js:34` | | GET | `/api/v1/templates/:recipeId` | protected | GENERAL (1000/15m) | `routes/recipes/index.js:63` | ## Docker resources _7 routes_ | Method | Full path | Auth | Rate limit | Defined in | |---|---|---|---|---| | GET | `/api/v1/docker/disk-usage` | protected | GENERAL (1000/15m) | `routes/docker-resources.js:84` | | GET | `/api/v1/docker/networks` | protected | GENERAL (1000/15m) | `routes/docker-resources.js:50` | | POST | `/api/v1/docker/networks` | protected | GENERAL (1000/15m) | `routes/docker-resources.js:64` | | DELETE | `/api/v1/docker/networks/:id` | protected | GENERAL (1000/15m) | `routes/docker-resources.js:76` | | GET | `/api/v1/docker/volumes` | protected | GENERAL (1000/15m) | `routes/docker-resources.js:17` | | POST | `/api/v1/docker/volumes` | protected | GENERAL (1000/15m) | `routes/docker-resources.js:30` | | DELETE | `/api/v1/docker/volumes/:name` | protected | GENERAL (1000/15m) | `routes/docker-resources.js:42` | ## Caddy / sites _7 routes_ | Method | Full path | Auth | Rate limit | Defined in | |---|---|---|---|---| | GET | `/api/v1/caddy/cas` | protected | GENERAL (1000/15m) | `routes/sites.js:57` | | GET | `/api/v1/caddy/config` | protected | GENERAL (1000/15m) | `routes/sites.js:31` | | POST | `/api/v1/caddy/reload` | protected | GENERAL (1000/15m) | `routes/sites.js:38` | | GET | `/api/v1/caddyfile` | protected | GENERAL (1000/15m) | `routes/sites.js:25` | | POST | `/api/v1/site` | protected | GENERAL (1000/15m) | `routes/sites.js:160` | | DELETE | `/api/v1/site/:domain` | protected | GENERAL (1000/15m) | `routes/sites.js:135` | | POST | `/api/v1/site/external` | protected | GENERAL (1000/15m) | `routes/sites.js:188` | ## Updates / workflows _6 routes_ | Method | Full path | Auth | Rate limit | Defined in | |---|---|---|---|---| | GET | `/api/v1/workflows/workflows` | protected | GENERAL (1000/15m) | `routes/workflows.js:22` | | POST | `/api/v1/workflows/workflows/:workflowId/disable` | protected | GENERAL (1000/15m) | `routes/workflows.js:35` | | POST | `/api/v1/workflows/workflows/:workflowId/enable` | protected | GENERAL (1000/15m) | `routes/workflows.js:28` | | GET | `/api/v1/workflows/workflows/:workflowId/history` | protected | GENERAL (1000/15m) | `routes/workflows.js:52` | | POST | `/api/v1/workflows/workflows/:workflowId/run` | protected | GENERAL (1000/15m) | `routes/workflows.js:42` | | GET | `/api/v1/workflows/workflows/history` | protected | GENERAL (1000/15m) | `routes/workflows.js:60` | ## Auto-restart _5 routes_ | Method | Full path | Auth | Rate limit | Defined in | |---|---|---|---|---| | GET | `/api/v1/policies` | protected | GENERAL (1000/15m) | `routes/auto-restart.js:30` | | DELETE | `/api/v1/policies/:serviceId` | protected | GENERAL (1000/15m) | `routes/auto-restart.js:103` | | GET | `/api/v1/policies/:serviceId` | protected | GENERAL (1000/15m) | `routes/auto-restart.js:39` | | POST | `/api/v1/policies/:serviceId` | protected | GENERAL (1000/15m) | `routes/auto-restart.js:60` | | POST | `/api/v1/policies/:serviceId/test` | protected | GENERAL (1000/15m) | `routes/auto-restart.js:123` | ## Certificate authority _5 routes_ | Method | Full path | Auth | Rate limit | Defined in | |---|---|---|---|---| | GET | `/api/v1/ca/cert/:domain` | public | GENERAL (1000/15m) | `routes/ca.js:127` | | GET | `/api/v1/ca/certs` | public | GENERAL (1000/15m) | `routes/ca.js:242` | | GET | `/api/v1/ca/info` | public | GENERAL (1000/15m) | `routes/ca.js:15` | | GET | `/api/v1/ca/install-script` | public | GENERAL (1000/15m) | `routes/ca.js:63` | | GET | `/api/v1/ca/root.crt` | public | GENERAL (1000/15m) | `routes/ca.js:45` | ## OpenClaw integration _5 routes_ | Method | Full path | Auth | Rate limit | Defined in | |---|---|---|---|---| | DELETE | `/api/v1/openclaw/` | protected | GENERAL (1000/15m) | `routes/openclaw.js:244` | | POST | `/api/v1/openclaw/deploy` | protected | GENERAL (1000/15m) | `routes/openclaw.js:150` | | GET | `/api/v1/openclaw/proxy/*` | protected | GENERAL (1000/15m) | `routes/openclaw.js:216` | | POST | `/api/v1/openclaw/proxy/*` | protected | GENERAL (1000/15m) | `routes/openclaw.js:230` | | GET | `/api/v1/openclaw/status` | protected | GENERAL (1000/15m) | `routes/openclaw.js:116` | ## Config drift _4 routes_ | Method | Full path | Auth | Rate limit | Defined in | |---|---|---|---|---| | POST | `/api/v1/fix` | protected | GENERAL (1000/15m) | `routes/config-drift.js:51` | | GET | `/api/v1/last` | protected | GENERAL (1000/15m) | `routes/config-drift.js:39` | | POST | `/api/v1/polling` | protected | GENERAL (1000/15m) | `routes/config-drift.js:66` | | GET | `/api/v1/report` | protected | GENERAL (1000/15m) | `routes/config-drift.js:30` | ## Licensing _4 routes_ | Method | Full path | Auth | Rate limit | Defined in | |---|---|---|---|---| | POST | `/api/v1/license/activate` | protected | GENERAL (1000/15m) | `routes/license.js:16` | | POST | `/api/v1/license/deactivate` | protected | GENERAL (1000/15m) | `routes/license.js:41` | | GET | `/api/v1/license/feature/:feature` | public | GENERAL (1000/15m) | `routes/license.js:52` | | GET | `/api/v1/license/status` | public | GENERAL (1000/15m) | `routes/license.js:35` | ## File browser _3 routes_ | Method | Full path | Auth | Rate limit | Defined in | |---|---|---|---|---| | GET | `/api/v1/browse/directories` | protected | GENERAL (1000/15m) | `routes/browse.js:52` | | GET | `/api/v1/browse/roots` | protected | GENERAL (1000/15m) | `routes/browse.js:34` | | GET | `/api/v1/media/detected-mounts` | protected | GENERAL (1000/15m) | `routes/browse.js:137` | ## Theming _3 routes_ | Method | Full path | Auth | Rate limit | Defined in | |---|---|---|---|---| | GET | `/api/v1/themes` | public | GENERAL (1000/15m) | `routes/themes.js:40` | | DELETE | `/api/v1/themes/:slug` | protected | GENERAL (1000/15m) | `routes/themes.js:65` | | POST | `/api/v1/themes/:slug` | protected | GENERAL (1000/15m) | `routes/themes.js:45` | ## Service credentials _2 routes_ | Method | Full path | Auth | Rate limit | Defined in | |---|---|---|---|---| | GET | `/api/v1/credentials/list` | protected | GENERAL (1000/15m) | `routes/credentials.js:15` | | POST | `/api/v1/credentials/rotate-key` | protected | GENERAL (1000/15m) | `routes/credentials.js:21` | ## Events _2 routes_ | Method | Full path | Auth | Rate limit | Defined in | |---|---|---|---|---| | GET | `/api/v1/events/clients` | protected | GENERAL (1000/15m) | `routes/events.js:154` | | GET | `/api/v1/events/stream` | protected | GENERAL (1000/15m) | `routes/events.js:126` | ## Internal helpers _1 routes_ | Method | Full path | Auth | Rate limit | Defined in | |---|---|---|---|---| | GET | `/api/v1/status` | protected | GENERAL (1000/15m) | `routes/context.js:8` | ## Mount Point Map How `src/app.js` wires route files to URL prefixes (via `apiRouter.use`): | Route file(s) | Mounted at | |---|---| | `routes/ca` | `/api/v1/ca` | | `routes/containers` | `/api/v1/containers` | | `routes/dependencies` | `/api/v1/dependencies` | | `routes/dns` | `/api/v1/dns` | | `routes/docker-resources` | `/api/v1/docker` | | `routes/events` | `/api/v1/events` | | `routes/license` | `/api/v1/license` | | `routes/notifications` | `/api/v1/notifications` | | `routes/openclaw` | `/api/v1/openclaw` | | `routes/recipes/` | `/api/v1/recipes` | | `routes/tailscale` | `/api/v1/tailscale` | | `routes/tailscale-admin` | `/api/v1/tailscale` | | `routes/workflows` | `/api/v1/workflows` | | `routes/auth/` | `/api/v1 (root)` | | `routes/config/` | `/api/v1 (root)` | | `routes/services` | `/api/v1 (root)` | | `routes/health` | `/api/v1 (root)` | | `routes/monitoring` | `/api/v1 (root)` | | `routes/updates` | `/api/v1 (root)` | | `routes/sites` | `/api/v1 (root)` | | `routes/credentials` | `/api/v1 (root)` | | `routes/arr/` | `/api/v1 (root)` | | `routes/apps/` | `/api/v1 (root)` | | `routes/logs` | `/api/v1 (root)` | | `routes/backups` | `/api/v1 (root)` | | `routes/browse` | `/api/v1 (root)` | | `routes/errorlogs` | `/api/v1 (root)` | | `routes/themes` | `/api/v1 (root)` | | `routes/auto-restart` | `/api/v1 (root)` | | `routes/config-drift` | `/api/v1 (root)` | | `routes/ssl-monitor` | `/api/v1 (root)` | ## PUBLIC_ROUTES Allowlist Source: `src/utilities/middleware.js:310-364` (42 entries) | Method | Path | Match | |---|---|---| | ANY | `/health` | exact | | ANY | `/health/live` | exact | | ANY | `/health/ready` | exact | | ANY | `/healthz` | exact | | ANY | `/readyz` | exact | | ANY | `/probe/` | prefix | | ANY | `/api/v1/tailscale/` | prefix | | ANY | `/api/v1/totp/config` | exact | | ANY | `/api/v1/totp/recovery-info` | exact | | ANY | `/api/v1/totp/verify` | exact | | ANY | `/api/v1/totp/setup` | exact | | ANY | `/api/v1/totp/verify-setup` | exact | | ANY | `/api/v1/totp/check-session` | exact | | ANY | `/api/v1/auth/gate/` | prefix | | ANY | `/api/v1/auth/app-token/` | prefix | | ANY | `/api/v1/auth/login-page` | exact | | ANY | `/api/v1/services` | exact | | ANY | `/api/v1/ca/info` | exact | | ANY | `/api/v1/ca/root.crt` | exact | | ANY | `/api/v1/ca/install-script` | exact | | ANY | `/api/v1/health/ca` | exact | | GET | `/api/v1/ca/cert/` | prefix | | ANY | `/api/v1/ca/certs` | exact | | ANY | `/api/v1/csrf-token` | exact | | ANY | `/api/v1/logo` | exact | | ANY | `/api/v1/favicon` | exact | | ANY | `/api/v1/themes` | exact | | ANY | `/api/v1/license/status` | exact | | GET | `/api/v1/license/feature/` | prefix | | ANY | `/api/v1/config` | exact | | ANY | `/api/v1/services/status` | exact | | ANY | `/api/v1/health-checks/status` | exact | | ANY | `/api/v1/monitoring/stats` | exact | | ANY | `/api/v1/system/version` | exact | | ANY | `/api/v1/system/update-status` | exact | | ANY | `/api/v1/system/update-history` | exact | | ANY | `/api/v1/system/update-check` | exact | | ANY | `/api/v1/updates/available` | exact | | ANY | `/api/v1/system/update-notify` | exact | | ANY | `/api/v1/monitoring/stats` | exact | | ANY | `/api/v1/health-checks/status` | exact | | ANY | `/api/v1/version` | exact | ## Root-Level Endpoints (defined directly in src/app.js) | Method | Path | Auth | Purpose | |---|---|---|---| | GET | `/health` | public | Liveness (alias for `/health/live`) | | GET | `/health/live` | public | Process-only check, no I/O | | GET | `/health/ready` | public | Checks config + services + Docker + Caddy-admin (3s timeout each) | | GET | `/healthz` | public | k8s alias for `/health/live` | | GET | `/readyz` | public | k8s alias for `/health/ready` | | GET | `/probe/:id` | public | Per-service health probe, sets `X-DashCaddy-HealthCheck: 1` | | GET | `/api/v1/network/ips` | protected | Detected network interfaces + IPs (cached) | | GET | `/api/v1/docs` | protected | Interactive Swagger UI | | GET | `/api/v1/docs/spec` | protected | Raw OpenAPI 3.0.3 spec | | GET | `/api/v1/version` | public (per PUBLIC_ROUTES) | API version | ## OpenAPI Spec Cross-Check - Routes defined in code: **236** - Paths in `openapi.yaml`: **112** ### In code but NOT documented in OpenAPI (142) - `/api/v1/:appId` - `/api/v1/:appId/backup-points` - `/api/v1/:appId/restore` - `/api/v1/:appId/revert/:filename` - `/api/v1/:recipeId` - `/api/v1/:recipeId/restart` - `/api/v1/:recipeId/start` - `/api/v1/:recipeId/stop` - `/api/v1/arr/quality-profiles` - `/api/v1/audit-logs` - `/api/v1/auth/jwt` - `/api/v1/auth/keys` - `/api/v1/auth/keys/:keyId` - `/api/v1/auth/login-page` - `/api/v1/backups/backup/:appId` - `/api/v1/backups/compare/:filename` - `/api/v1/backups/credentials/:provider` - `/api/v1/backups/files` - `/api/v1/backups/files/:appId` - `/api/v1/backups/restore-file/:filename` - `/api/v1/backups/schedule` - `/api/v1/backups/schedule/:appId` - `/api/v1/backups/storage-info` - `/api/v1/backups/test-destination` - `/api/v1/browse/directories` - `/api/v1/ca/cert/:domain` - `/api/v1/ca/certs` - `/api/v1/ca/info` - `/api/v1/ca/install-script` - `/api/v1/ca/root.crt` - ... and 112 more ### Documented but NOT in code (18) - `/api/v1/apps/:appId` - `/api/v1/apps/check-existing` - `/api/v1/apps/check-port/:port` - `/api/v1/apps/deploy` - `/api/v1/apps/suggest-port/:basePort` - `/api/v1/apps/templates` - `/api/v1/apps/templates/:appId` - `/api/v1/apps/update-subdomain` - `/api/v1/audit-log` - `/api/v1/browse/dir` - `/api/v1/caddy/get-cas` - `/api/v1/health` - `/api/v1/health-check/configure/:serviceId` - `/api/v1/health-check/incidents` - `/api/v1/health-check/incidents/history` - `/api/v1/health-check/stats/:serviceId` - `/api/v1/health-check/status` - `/api/v1/service-creds/:serviceId`