Agent Download URL Endpoint Redesign Plan
Goal
Replace the V1 agent download URL surface with a documented V2 design that:
- follows API Design Guidelines
- removes the bespoke
GET /internal/v1/agent/downloads/linksresponse - supports the current deployment-instructions UI in
apps/amm-web - leaves room for redirect-based downloads and future signed URLs
This plan addresses the missing agent release/download surface between the existing V1 API and the documented V2 design.
Current Problem
V1 exposes six endpoints under a global /internal/v1/agent namespace:
GET /internal/v1/agent/releases/latestGET /internal/v1/agent/downloads/linksGET /internal/v1/agent/releases/{version}GET /internal/v1/agent/releasesGET /internal/v1/agent/downloads/latestGET /internal/v1/agent/downloads/{version}
The main design problem is GET /internal/v1/agent/downloads/links.
It returns a synthetic payload that mixes two concepts:
- release metadata for the current stable channel (
stable.version) - convenience download URLs that point at "latest" alias paths (
latest.windows,latest.macos.arm64, etc.)
That shape does not match the V2 resource style used elsewhere in the docs. It is not a resource collection, not a detail resource, and not a summary endpoint.
Frontend Usage Today
The current deployment instructions flow is split across two concerns:
DeploymentInstructionsDrawerContent.tsxloads the enrollment tokenWindowsInstructions.tsxandMacOSInstructions.tsxload installer URLs throughagentReleaseLatestQueryOptions
The important detail is what the UI actually needs from the download API:
- one display version label for the current stable release
- one Windows download action
- two macOS download actions (
arm64,x86_64)
The UI does not need:
- a combined
stable+latestwrapper object - a separate "download links" domain
- platform-specific filtering on the metadata request
The UI can render correctly from a single "current stable release" resource as long as that resource includes artifact download actions for Windows and macOS architectures.
Design Constraints
From Design Guidelines:
- agent download data is not client-specific, so it should not live under
/clients/{client_id} - this is shared reference-style data, so a root-scoped domain is appropriate, similar to catalog domains
- platform artifacts are a small bounded relationship and are always needed when viewing a release, so they should be embedded in the release DTO
- custom composite payloads should be avoided when a normal resource object can represent the use case
- path names should use explicit resource nouns and resource-specific parameter names
This leads to a simple rule:
Model releases as the resource. Model downloads as actions on release artifacts, not as a separate "links" payload.
Recommended V2 Domain
Create a new root-scoped domain:
/agent-releases
Reasoning:
- the data is global, not tenant-scoped
- "release" is the stable business resource already present in V1
- it aligns better with existing V2 docs than a verb-like
/downloadsdomain
Do not embed download URLs into Client.
Reasoning:
- enrollment token is client-specific and already embedded there
- agent installer artifacts are not always needed on every client fetch
- mixing client identity with global release metadata violates the embedding rule
Proposed Resource Model
AgentRelease
Primary resource returned by detail and collection endpoints.
Suggested fields:
{
"channel": "stable",
"version": "1.8.3",
"published_at": "2026-02-24T21:23:53.082Z",
"notes_url": "https://github.com/Scalepad/scalepad-agent/releases/tag/v1.8.3",
"artifacts": {
"windows": {
"filename": "ScalepadAgent-1.8.3.msi",
"content_type": "application/octet-stream",
"size_bytes": 12345678,
"sha256": "abcdef...",
"download_url": "/v2/agent-releases/latest/download?channel=stable&platform=windows"
},
"macos": {
"arm64": {
"filename": "ScalepadAgent-1.8.3.pkg",
"content_type": "application/octet-stream",
"size_bytes": 23456789,
"sha256": "123456...",
"download_url": "/v2/agent-releases/latest/download?channel=stable&platform=macos&arch=arm64"
},
"x86_64": {
"filename": "ScalepadAgent-1.8.3.pkg",
"content_type": "application/octet-stream",
"size_bytes": 23456780,
"sha256": "654321...",
"download_url": "/v2/agent-releases/latest/download?channel=stable&platform=macos&arch=x86_64"
}
}
}
}
Notes:
artifactsshould replace V1'splatformsfor clearer namingdownload_urlshould be the API-owned download endpoint, not a raw CDN URL- the server may still redirect to CDN/S3/CloudFront internally
AgentArtifact
Embedded object. No separate top-level endpoint is required initially.
Do not use a Ref suffix.
Reasoning:
- there is no fuller standalone artifact resource in this plan
- the embedded artifact shape is the full shape consumers need
Proposed Endpoint Surface
1. Get current release for a channel
GET /agent-releases/latest?channel=stable
Purpose:
- powers the deployment instructions drawer
- returns one
AgentRelease - includes bounded embedded artifacts with download actions
Why this replaces /downloads/links:
- the UI gets the version and all installer actions from one canonical resource
- no extra wrapper object is needed
2. Get release by version
GET /agent-releases/{version}
Purpose:
- direct lookup for audit, support, or release-history views
- returns one
AgentRelease
3. List releases
GET /agent-releases
Query parameters:
channeloptionalpage_sizecursorsort_by=version|published_atsort_order=asc|desc
Purpose:
- supports future admin/release history UI
- follows standard paginated collection rules
4. Download current release artifact
GET /agent-releases/latest/download?channel=stable&platform=windows
GET /agent-releases/latest/download?channel=stable&platform=macos&arch=arm64
Behavior:
- responds with
302to the real artifact location - keeps CDN/storage layout private behind an API contract
- preserves room for signed URLs later
5. Download versioned release artifact
GET /agent-releases/{version}/download?platform=windows
GET /agent-releases/{version}/download?platform=macos&arch=x86_64
Behavior:
- same redirect behavior as above
- useful for support, rollback, and release-history workflows
Endpoints To Remove Or Deprecate
Deprecate these V1 shapes once V2 consumers are migrated:
GET /internal/v1/agent/downloads/linksGET /internal/v1/agent/downloads/latestGET /internal/v1/agent/downloads/{version}
Keep the V1 endpoints temporarily as compatibility shims if needed, but document them as legacy-only.
GET /internal/v1/agent/releases/latest, GET /internal/v1/agent/releases/{version}, and GET /internal/v1/agent/releases should map conceptually to the new V2 /agent-releases domain and can be retired after clients move.
Why This Fits The Frontend Better
Windows
Current UI behavior:
- show
Latest (x.y.z) - open one Windows download URL
With the proposed design:
- call
GET /v2/agent-releases/latest?channel=stable - read
version - read
artifacts.windows.download_url
macOS
Current UI behavior:
- show
Latest (x.y.z) - open one
arm64URL and onex86_64URL
With the proposed design:
- same single request as Windows
- read
artifacts.macos.arm64.download_url - read
artifacts.macos.x86_64.download_url
Query simplification
The current frontend query API accepts platform and channel, but the UI does not need per-platform metadata requests.
After redesign:
- keep only
channelon the metadata endpoint - remove
platformfrom the main query key - fetch one release object and render all cards from it
Recommended Frontend Follow-Up
Once the V2 endpoint exists, update apps/amm-web as follows:
- Replace
GET /internal/v1/agent/downloads/linkswithGET /v2/agent-releases/latest?channel=stable. - Read Windows/macOS download actions from the returned
AgentRelease.artifacts. - Use
versiondirectly for the label instead of combiningstable.versionwithlatest.*. - Rename
agentReleaseLatestQueryOptionsto reflect the real resource, for exampleagentLatestReleaseQueryOptions.
Related but separate cleanup:
- the drawer currently fetches the enrollment token from V1
GET /internal/v1/enrollment-tokens - the documented V2 contract already embeds the active token on
GET /clients/{client_id}
That token migration is not part of finding #1, but the deployment instructions flow should eventually switch to:
- one client request for the active enrollment token
- one root-scoped release request for installer artifacts
Backend Implementation Plan
Phase 1. Document the domain
- add
/agent-releasestodocs/api/index.md - add domain overview and endpoint pages under
docs/api/agent-releases/ - define the
AgentReleaseand embedded artifact DTOs in docs first
Phase 2. Add V2 controller surface
- create a V2 controller under the root
/v2/agent-releasesnamespace - reuse the existing releases service where possible
- map the current service output into the new DTOs
Phase 3. Move frontend to V2
- update the deployment instructions query to call the new V2 endpoint
- simplify the query cache key to channel-based metadata
- update tests and mocks to the new response shape
Phase 4. Deprecate V1 routes
- keep V1 routes during the transition window
- mark them deprecated in generated OpenAPI
- remove them after the frontend and any other consumers have migrated
Testing Plan
Per the repo rules for new API surface:
- add unit/contract tests for every new V2
/agent-releasesendpoint - add integration coverage to
amm-services/apps/agent-web-api/scripts/validate_api_contract.py
Specific cases to cover:
GET /v2/agent-releases/latest?channel=stableGET /v2/agent-releases/{version}GET /v2/agent-releasespagination and sorting- Windows download redirect
- macOS
arm64redirect - macOS
x86_64redirect - invalid
platform/archcombinations 404for unknown version
Frontend tests to update:
DeploymentInstructionsDrawerContentintegration coverage- installer-card rendering for Windows and both macOS architectures
- error state when release metadata fails to load
Decisions
Recommended decisions for the redesign:
- Make
agent-releasesa new root-scoped V2 domain. - Treat
AgentReleaseas the canonical resource. - Remove the synthetic
downloads/linksresponse from the contract. - Embed bounded artifacts inside the release DTO.
- Expose API-owned redirect download URLs from the artifact objects.
- Keep enrollment token retrieval separate and client-scoped.
If the team agrees with those six points, the resulting V2 design is straightforward and the current amm-web deployment instructions screen can migrate without needing another special-case endpoint.