Desktop - Client Updates v3
Manage the Koldan Windows client's ZIP, MSI, and EXE updates through one global release library and one production feed. Upload a verified package, review its update settings, then publish it explicitly. Clients can fetch the feed and published packages.
Release lifecycle
| Term | Meaning |
|---|---|
| Release | One application AssemblyVersion, one immutable verified package, descriptive metadata, and default update settings. |
| Library | Uploaded releases. Uploading does not change the production feed. |
| Publication | An immutable snapshot of a release and its effective settings, with the publisher, UTC time, and reason. |
| Production feed | The stable XML URL representing the current publication. |
| Rollback | A new publication restoring a historical publication's release and complete settings. |
Published means offered to clients, not installed. Download counts are downloads, not installation confirmations. Changing release defaults never changes an existing publication; changing live settings requires another publication, even for the same package.
A release is READY, ARCHIVED, or REVOKED. Archiving removes it from publication choices while retaining any existing public downloads. Restoring its state to READY makes it eligible again. Revocation is permanent and blocks publication and further server downloads. Replace the active production release before revoking it. Revocation cannot remove files already downloaded to a client.
Rollback does not downgrade clients
Publishing an older version changes what clients are offered. Already-upgraded clients need a recovery build with a higher application version. Rollback does not automatically downgrade them.
Administration endpoints
Base path: /api/v1/desktop-services/admin/updates
Endpoint headings and tables show the full request path. {id} is a UUID; {ext} must be exactly zip, msi, or exe, matching the package type.
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/v1/desktop-services/admin/updates/releases |
Upload a release |
GET |
/api/v1/desktop-services/admin/updates/releases |
List releases |
GET |
/api/v1/desktop-services/admin/updates/releases/{id} |
Get a release |
PATCH |
/api/v1/desktop-services/admin/updates/releases/{id} |
Edit a release |
GET |
/api/v1/desktop-services/admin/updates/releases/{id}/package.{ext} |
Download a package for inspection |
POST |
/api/v1/desktop-services/admin/updates/releases/{id}/revoke |
Revoke a release |
POST |
/api/v1/desktop-services/admin/updates/releases/{id}/legacy-import |
Attest a legacy import |
GET |
/api/v1/desktop-services/admin/updates/production |
Get production |
POST |
/api/v1/desktop-services/admin/updates/publication-previews |
Preview a publication |
POST |
/api/v1/desktop-services/admin/updates/publications |
Publish or roll back |
GET |
/api/v1/desktop-services/admin/updates/publications |
List publications |
GET |
/api/v1/desktop-services/admin/updates/publications/{id} |
Get a publication |
GET |
/api/v1/desktop-services/admin/updates/events |
List change events |
Upload a release
POST /api/v1/desktop-services/admin/updates/releases
Requires Authentication - Scopes: admin:desktop-updates:write
Admin - Default tenant
Send multipart/form-data with a binary file part and a metadata part whose content type is application/json. The filename extension must match the contents: a ZIP archive, Windows Installer database, or Windows executable. The server computes all supported digests and verifies expectedSha256, if supplied, before accepting the release. ZIPs also undergo archive path and structure validation. Uploaded packages are never executed by the API.
UploadReleaseRequest
These fields belong in the JSON metadata part:
| Field | Type | Required | Description |
|---|---|---|---|
version |
string |
Yes | Application AssemblyVersion with two to four numeric components. 9.2 becomes 9.2.0.0. No prerelease labels. Normalized versions are unique and cannot be reused, including after archive or revocation. |
title |
string |
No | Up to 255 characters. Defaults to Koldan {normalizedVersion}. |
expectedSha256 |
string |
No | Expected SHA-256 as 64 hexadecimal characters. A mismatch rejects the upload. |
settings |
UpdateSettings | No | Initial defaults; omission gives an optional update with SHA-256 and no installer arguments. |
Supply the application's version explicitly; neither the MSI product version nor the filename determines it. Package bytes, type, version, filename, size, and digests cannot be replaced through subsequent edits. Upload a new application version for a different package.
Replace https://api.example.com with your API origin and the example UUIDs with IDs returned by your API. Set ACCESS_TOKEN to an administrator token. Do not store tokens in committed files.
curl -X POST https://api.example.com/api/v1/desktop-services/admin/updates/releases \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-F 'file=@./koldan-9.2.0.0.msi' \
-F 'metadata={
"version": "9.2.0.0",
"title": "Koldan 9.2",
"settings": {
"mandatory": false,
"mode": "NORMAL",
"checksumAlgorithm": "SHA256",
"releaseNotes": "Improved desktop stability."
}
};type=application/json'
A successful response has state: "READY" and publicAvailable: false. Review the package type, size, and verified digest before publishing. Use the same request for ZIP or EXE by changing the file and relevant settings.
ReleaseResponse
| Status | Description |
|---|---|
201 Created |
Verified release returned with its revision ETag. |
400 Bad Request |
Invalid package, checksum, version, or settings. |
409 Conflict |
The normalized version already exists. |
Update settings
UpdateSettings
The same settings shape is used for upload defaults, edits, and publication snapshots. A supplied settings object is a complete replacement, not a field-by-field merge: omitted fields receive the defaults below. Omit the entire object to retain existing defaults on edit or use release defaults on publication.
| Field | Type | Required | Description |
|---|---|---|---|
mandatory |
boolean |
No | Default false. This is a boolean, not a nested object. |
mode |
string enum |
No | NORMAL (default), FORCED, or FORCED_DOWNLOAD; emitted as XML modes 0, 1, or 2. Non-normal modes require mandatory: true. |
minVersion |
string |
No | Optional normalized version threshold for mandatory behavior. Requires mandatory: true. It is not an installation compatibility constraint. |
executable |
string |
No | ZIP only: safe relative .exe path within the package and installation directory, up to 512 characters. Absolute paths and traversal are rejected. |
installerArgs |
string |
No | MSI/EXE only, up to 8,192 characters. Defaults to empty. %path% is preserved for updater substitution. |
releaseNotes |
string |
No | Hosted plain-text release notes, up to 100,000 characters. Render ed as escaped HTML; no raw HTML or Markdown rendering. |
changelogUrl |
string |
No | External public HTTPS URL without credentials, up to 2,048 characters. Mutually exclusive with nonempty releaseNotes; ensure clients can open it anonymously. |
checksumAlgorithm |
string enum |
No | SHA256 (default), SHA384, SHA512, SHA1, or MD5. The XML uses the corresponding server-computed digest. |
| Package type | Client behavior | Applicable option |
|---|---|---|
ZIP |
Extract application files with the ZIP updater. | executable; nonempty installerArgs is rejected. |
MSI |
Run Windows Installer with the downloaded package. | installerArgs appended to the updater's msiexec /i invocation. |
EXE |
Launch the downloaded installer. | installerArgs passed to the executable. |
Nonempty executable is rejected for MSI/EXE. No silent-install arguments are supplied automatically; configure and review the switches supported by your installer. The current client uses RunUpdateAsAdmin=false; XML cannot change elevation behavior. Test each installer with the intended Windows privileges.
Get a release
GET /api/v1/desktop-services/admin/updates/releases/{id}
Requires Authentication - Scopes: admin:desktop-updates:read
Admin - Default tenant
Read the release and its quoted revision ETag, for example ETag: "3". To inspect its publication references, list /publications?releaseId={id}. Download unpublished packages using /releases/{id}/package.{ext} with the read permission; the public downloadUrl in the response is unavailable until publication or legacy attestation.
ReleaseResponse
| Status | Description |
|---|---|
200 OK |
Release returned with its revision ETag. |
404 Not Found |
Release not found. |
Edit a release
PATCH /api/v1/desktop-services/admin/updates/releases/{id}
Requires Authentication - Scopes: admin:desktop-updates:write
Admin - Default tenant
Send If-Match containing the release ETag. Returns the updated release and a new ETag.
EditReleaseRequest
| Field | Type | Required | Description |
|---|---|---|---|
title |
string |
No | Nonblank replacement title, up to 255 characters. |
settings |
UpdateSettings | No | Complete replacement defaults. Existing publications are unchanged. |
state |
string enum |
No | ARCHIVED or READY. Use the dedicated endpoint for revocation. |
reason |
string |
Yes | Nonblank explanation, up to 4,000 characters. |
For example, send {"state":"ARCHIVED","reason":"Superseded by the tested recovery build"} to archive. Send state: "READY" to restore an archived release before publishing it again.
ReleaseResponse
| Status | Description |
|---|---|
200 OK |
Updated release returned with a new ETag. |
412 Precondition Failed |
Release changed; fetch it and review again. |
428 Precondition Required |
Missing release If-Match header. |
Revoke a release
POST /api/v1/desktop-services/admin/updates/releases/{id}/revoke
Requires Authentication - Scopes: admin:desktop-updates:publish
Admin - Default tenant
Send the release's If-Match and JSON such as {"reason":"Installer withdrawn after compatibility testing"}. Revocation is permanent. Revoking the active production release returns 409; publish its replacement first. Both public and authenticated inspection downloads of a revoked release return 410.
ReleaseResponse
| Status | Description |
|---|---|
200 OK |
Release permanently revoked; updated ETag returned. |
409 Conflict |
Replace the production release before revoking it. |
412 Precondition Failed |
Release revision is stale. |
428 Precondition Required |
Missing release If-Match header. |
Get production
GET /api/v1/desktop-services/admin/updates/production
Requires Authentication - Scopes: admin:desktop-updates:read
Admin - Default tenant
Returns the current publication, feed URL, and production revision with a quoted ETag and Cache-Control: no-store. Before the first publication, publication is null and revision is 0.
ProductionResponse
| Status | Description |
|---|---|
200 OK |
Current production and ETag returned; publication is null before the first publication. |
Preview a publication
POST /api/v1/desktop-services/admin/updates/publication-previews
Requires Authentication - Scopes: admin:desktop-updates:publish
Admin - Default tenant
Send a PublicationRequest to obtain the proposed snapshot, current publication, generated XML, production revision, and a packageTypeChanged flag. No publication is created. Review installer arguments, mandatory behavior, notes, checksum algorithm, and package-type changes.
For hosted notes, preview XML contains a literal {publicationId} placeholder in the changelog URL; confirmation replaces it with the new publication's ID.
curl -X POST https://api.example.com/api/v1/desktop-services/admin/updates/publication-previews \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"releaseId": "550e8400-e29b-41d4-a716-446655440000",
"settings": {
"mandatory": false,
"mode": "NORMAL",
"checksumAlgorithm": "SHA256",
"releaseNotes": "Improved desktop stability."
},
"reason": "Approved after Windows installer verification"
}'
PreviewResponse
| Status | Description |
|---|---|
200 OK |
Proposed snapshot, XML, and production revision returned without publishing. |
409 Conflict |
The selected release is not ready for publication. |
503 Service Unavailable |
Required package storage is unavailable. |
Publish or roll back
POST /api/v1/desktop-services/admin/updates/publications
Requires Authentication - Scopes: admin:desktop-updates:publish
Admin - Default tenant
Send the reviewed PublicationRequest with both headers:
| Header | Required value |
|---|---|
If-Match |
Quoted production revision from the preview, such as "7". Do not use the release revision. |
Idempotency-Key |
Unique key for this publication attempt, 1–128 characters from letters, digits, ., _, :, and -. |
The package must be available and the release must be READY. Publication, the production change, and its change-history entry succeed together; a storage or history-persistence failure leaves the existing publication unchanged.
Reuse the same key, identical JSON request, and actor when retrying an uncertain result. A successful replay returns the original publication, including when its original production revision has since changed. Reusing the key for different input or another actor returns 409. A new attempt with a stale revision returns 412: fetch a fresh preview, review the changed production state, and confirm with a new key. A missing If-Match returns 428.
PublicationRequest
| Field | Type | Required | Description |
|---|---|---|---|
releaseId |
string (UUID) |
One selector | Release to publish. Mutually exclusive with restorePublicationId. |
restorePublicationId |
string (UUID) |
One selector | Historical publication to restore. Creates a new publication linked through restoredFrom. |
settings |
UpdateSettings | No | Complete effective settings for releaseId; omission uses current release defaults. Must be omitted when restoring a publication. |
reason |
string |
Yes | Nonblank explanation, up to 4,000 characters. Required for preview and confirmation. |
To publish exactly the settings reviewed, send the same complete settings and reason used in the preview. Replace "7" below with the preview's production revision, keeping the quotes. Choose a new idempotency key for each new publication attempt; retain it when retrying that same attempt.
curl -X POST https://api.example.com/api/v1/desktop-services/admin/updates/publications \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H 'Content-Type: application/json' \
-H 'If-Match: "7"' \
-H 'Idempotency-Key: publish-9.2.0.0-review-1' \
-d '{
"releaseId": "550e8400-e29b-41d4-a716-446655440000",
"settings": {
"mandatory": false,
"mode": "NORMAL",
"checksumAlgorithm": "SHA256",
"releaseNotes": "Improved desktop stability."
},
"reason": "Approved after Windows installer verification"
}'
Rollback uses the same preview and confirmation endpoints. It restores the complete historical snapshot, including settings and descriptive metadata, rather than the release's current defaults. If the historical release is archived, restore it to READY first; revoked releases cannot be restored or published.
First send the rollback body below to /publication-previews and review the restored snapshot. Then confirm using that preview's revision in If-Match ("8" is only an example). The UUID identifies a historical publication, not a release.
curl -X POST https://api.example.com/api/v1/desktop-services/admin/updates/publications \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H 'Content-Type: application/json' \
-H 'If-Match: "8"' \
-H 'Idempotency-Key: rollback-previous-release-review-1' \
-d '{
"restorePublicationId": "8f9760c2-81ac-4c27-9712-f247ce35d682",
"reason": "Restore the previous verified release"
}'
Previously published packages remain available after rollback, allowing clients with an earlier manifest to finish their download.
PublicationResponse
| Status | Description |
|---|---|
201 Created |
New publication or original idempotent result returned. |
409 Conflict |
Ineligible release or idempotency-key conflict. |
412 Precondition Failed |
Production changed; refresh and review the preview. |
428 Precondition Required |
Missing production If-Match header. |
503 Service Unavailable |
Storage unavailable; current production is unchanged. |
Library, publication history, and change history
The collection endpoints below accept zero-based page (default 0) and size (default 50, range 1–200). Results are newest first, with IDs breaking timestamp ties. Responses use items, not content:
| Collection | Additional query parameters |
|---|---|
| List releases | search: title/version substring; state: READY, ARCHIVED, or REVOKED; published: boolean indicating prior publication or legacy attestation, not whether the release is current production. |
| List publications | releaseId: UUID. Use GET /publications/{id} for one immutable snapshot. |
| List change events | releaseId, actorId: UUIDs; action: exact action name; from, to: inclusive ISO 8601 UTC timestamps, such as 2026-09-19T00:00:00Z. |
Change history records the actor, impersonator when applicable, UTC timestamp, reason, request ID when available, affected resources, and before/after values. This history is independent of ordinary request-log retention. Significant events are also discoverable in the console at /admin/audit.
Filter action using one of:
desktop-updates.release.uploaddesktop-updates.release.editdesktop-updates.release.revokedesktop-updates.release.legacy-importdesktop-updates.publication.publishdesktop-updates.publication.rollback
List releases
GET /api/v1/desktop-services/admin/updates/releases
Requires Authentication - Scopes: admin:desktop-updates:read
Admin - Default tenant
Search and paginate the release library using the filters and pagination above.
ReleaseResponse in items
| Status | Description |
|---|---|
200 OK |
Requested content returned. |
400 Bad Request |
Invalid filter or pagination parameter. |
List publications
GET /api/v1/desktop-services/admin/updates/publications
Requires Authentication - Scopes: admin:desktop-updates:read
Admin - Default tenant
Browse immutable publication history, optionally filtered by releaseId, with the pagination above.
PublicationResponse in items
| Status | Description |
|---|---|
200 OK |
Requested content returned. |
400 Bad Request |
Invalid filter or pagination parameter. |
Get a publication
GET /api/v1/desktop-services/admin/updates/publications/{id}
Requires Authentication - Scopes: admin:desktop-updates:read
Admin - Default tenant
Read one immutable publication snapshot, including its reason, actor, and rollback reference.
PublicationResponse
| Status | Description |
|---|---|
200 OK |
Requested content returned. |
404 Not Found |
Publication not found. |
List change events
GET /api/v1/desktop-services/admin/updates/events
Requires Authentication - Scopes: admin:desktop-updates:read
Admin - Default tenant
Browse change history using the actor, release, action, and UTC time filters above.
ChangeResponse in items
| Status | Description |
|---|---|
200 OK |
Requested content returned. |
400 Bad Request |
Invalid filter or pagination parameter. |
Download a package for inspection
GET /api/v1/desktop-services/admin/updates/releases/{id}/package.{ext}
Requires Authentication - Scopes: admin:desktop-updates:read
Admin - Default tenant
Download a library package before publication using an authenticated request. The extension must match the package type. Archived packages remain available; revoked packages cannot be downloaded. See the public download contract for filenames, cache revalidation, and byte ranges.
Package bytes
| Status | Description |
|---|---|
200 OK |
Requested content returned. |
206 Partial Content |
Requested byte range returned. |
410 Gone |
Release has been revoked. |
503 Service Unavailable |
Package storage is unavailable. |
Response models
ReleaseResponse
| Field | Type | Nullable | Description |
|---|---|---|---|
id |
string (UUID) |
No | Release identifier. |
version |
string |
No | Normalized four-component application version. |
packageType |
string enum |
No | ZIP, MSI, or EXE. |
originalFilename |
string |
No | Uploaded filename, also used for legacy package redirects. |
downloadFilename |
string |
No | Safe name such as koldan-9.2.0.0.msi. |
sizeBytes |
integer |
No | Verified package byte length. |
digests |
object |
No | Server-computed hexadecimal values keyed by MD5, SHA1, SHA256, SHA384, and SHA512. |
title |
string |
No | Current descriptive title. |
settings |
UpdateSettings | No | Current release defaults. |
state |
string enum |
No | READY, ARCHIVED, or REVOKED. |
publicAvailable |
boolean |
No | Prior publication or legacy attestation. Check state too: revocation blocks downloads. |
revision |
integer |
No | Release concurrency revision. |
createdAt |
string (ISO 8601) |
No | Upload timestamp in UTC. |
createdBy |
string (UUID) |
No | Uploading actor. |
downloadUrl |
string |
No | Public package URL; its presence does not grant public access to an unpublished release. |
ReleaseSnapshot
The immutable snapshot contains releaseId, version, packageType, downloadFilename, sizeBytes, digests, title, and settings. Their types match the release response (releaseId is a UUID). Values are captured at publication and do not follow later edits to the release.
PublicationResponse
| Field | Type | Nullable | Description |
|---|---|---|---|
id |
string (UUID) |
No | Publication identifier. |
releaseId |
string (UUID) |
No | Selected release. |
restoredFrom |
string (UUID) |
Yes | Historical publication restored by this rollback. |
snapshot |
ReleaseSnapshot | No | Immutable package identity, metadata, and complete effective settings. |
reason |
string |
No | Publisher's explanation. |
createdAt |
string (ISO 8601) |
No | Publication timestamp in UTC. |
createdBy |
string (UUID) |
No | Publishing actor. |
revision |
integer |
No | Production revision resulting from this publication; it may no longer be current. |
downloadUrl |
string |
No | Public immutable package URL. |
changelogUrl |
string |
No | Hosted changelog endpoint. When settings specify an external URL, XML uses that external URL instead. |
ProductionResponse
| Field | Type | Nullable | Description |
|---|---|---|---|
revision |
integer |
No | Current production concurrency revision. |
publication |
PublicationResponse | Yes | Current publication; null before the first publication. |
feedUrl |
string |
No | Stable public XML URL. |
PreviewResponse
| Field | Type | Nullable | Description |
|---|---|---|---|
revision |
integer |
No | Production revision to quote in If-Match on confirmation. |
current |
PublicationResponse | Yes | Current publication for comparison. |
proposed |
ReleaseSnapshot | No | Proposed effective snapshot. |
xml |
string |
No | Proposed updater manifest, with a publication-ID placeholder for hosted notes. |
rollback |
boolean |
No | Whether the request restores a historical publication. |
packageTypeChanged |
boolean |
No | Whether the proposed package type differs from current production. |
ChangeResponse
| Field | Type | Nullable | Description |
|---|---|---|---|
id |
string (UUID) |
No | Change identifier. |
releaseId |
string (UUID) |
Yes | Affected release. |
publicationId |
string (UUID) |
Yes | Affected publication, when applicable. |
action |
string |
No | Full action name listed above. |
actorId |
string (UUID) |
No | Actor responsible for the change. |
impersonatorId |
string (UUID) |
Yes | Impersonating administrator, when applicable. |
createdAt |
string (ISO 8601) |
No | Event timestamp in UTC. |
requestId |
string |
Yes | Request correlation identifier, when available. |
reason |
string |
No | Recorded explanation. |
beforeValue |
object |
Yes | Prior values; absent for creation. |
afterValue |
object |
Yes | Resulting values. |
Public delivery
Base path: /api/v1/desktop-services/updates/v3
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/v1/desktop-services/updates/v3/updates.xml |
Get the production manifest |
GET |
/api/v1/desktop-services/updates/v3/releases/{id}/package.{ext} |
Download a published package |
HEAD |
/api/v1/desktop-services/updates/v3/releases/{id}/package.{ext} |
Inspect package headers |
GET |
/api/v1/desktop-services/updates/v3/publications/{id}/changelog |
Get a publication changelog |
Get the production manifest
GET /api/v1/desktop-services/updates/v3/updates.xml
Public - No authentication or tenant selection required
Returns application/xml;charset=UTF-8 from the current publication, with Cache-Control: no-store. The API generates version, package URL, and checksum; administrators do not upload XML. There is no automatic publication of the newest upload. Before the first publication, the feed returns 503.
XML manifest
| Status | Description |
|---|---|
200 OK |
Current publication manifest returned. |
503 Service Unavailable |
No release has been published yet. |
Download a published package
GET /api/v1/desktop-services/updates/v3/releases/{id}/package.{ext}
Public - No authentication or tenant selection required
Returns package content directly through the API. The extension must be lowercase and match the stored type. Unknown, unpublished, or extension-mismatched packages return 404; revoked packages return 410. Storage unavailable before streaming returns 503; a failure during streaming can terminate the download, so clients must verify the length and checksum.
| Behavior | Contract |
|---|---|
| Full download | 200, exact package bytes, Content-Length, Accept-Ranges: bytes. ZIP uses application/zip; MSI/EXE use application/octet-stream. |
| Filename | Content-Disposition: attachment with a safe name such as koldan-9.2.0.0.msi. Its extension agrees with the URL; preserve it because the updater selects installer behavior by filename. |
HEAD |
Same full-package metadata without a response body; Range is ignored. |
| Range | One byte range, including open-ended or suffix ranges, returns 206 and Content-Range. Invalid or multiple ranges return 416 with Content-Range: bytes */{size}. |
| Revalidation | SHA-256-based quoted ETag and Cache-Control: no-cache, must-revalidate. Matching If-None-Match returns 304. |
If-Range |
A matching ETag permits a partial response; a nonmatching value causes a full response. |
Package bytes
| Status | Description |
|---|---|
200 OK |
Full package returned. |
206 Partial Content |
Requested byte range returned. |
304 Not Modified |
ETag matches; no response body. |
404 Not Found |
Unknown, unpublished, or extension-mismatched package. |
410 Gone |
Release has been revoked. |
416 Range Not Satisfiable |
Invalid or multiple byte ranges. |
503 Service Unavailable |
Package storage unavailable before streaming. |
Inspect package headers
HEAD /api/v1/desktop-services/updates/v3/releases/{id}/package.{ext}
Public - No authentication or tenant selection required
Return full-package headers without downloading its contents. Content-Length is the complete package size; Range is ignored. Publication eligibility, revocation, storage checks, and ETag revalidation match the download endpoint.
Package headers (no response body)
| Status | Description |
|---|---|
200 OK |
Full-package metadata returned. |
304 Not Modified |
ETag matches. |
404 Not Found |
Unknown, unpublished, or extension-mismatched package. |
410 Gone |
Release has been revoked. |
503 Service Unavailable |
Package storage is unavailable. |
Get a publication changelog
GET /api/v1/desktop-services/updates/v3/publications/{id}/changelog
Public - No authentication or tenant selection required
Returns the publication's hosted notes as escaped HTML with Cache-Control: no-cache, must-revalidate. Unknown publications return 404; notes for revoked releases return 410. Historical publication notes remain tied to their snapshot. For an external changelog, the manifest points to the configured HTTPS URL.
HTML changelog
| Status | Description |
|---|---|
200 OK |
Hosted publication notes returned. |
404 Not Found |
Publication not found. |
410 Gone |
Release has been revoked. |
Anonymous delivery examples
Discovery and legacy URL migration
Anonymous GET /api/v1/global/siteproperties and the gRPC site-properties response expose the additive updatesUrlV3 field. By default, both updatesUrlV2 and updatesUrlV3 point to /api/v1/desktop-services/updates/v3/updates.xml on the advertised HTTP origin. This lets existing clients use v3 through their existing v2 discovery field. Explicit URL overrides remain in effect and must be migrated deliberately. See site properties for exact configuration names.
Legacy URL migration
| Legacy URL | Behavior |
|---|---|
/api/update/v2/updates.xml |
302 redirect to the current v3 feed, with Cache-Control: no-store. |
/api/update/v2/{filename} |
302 redirect to the original immutable release package matching its original or safe download filename, after publication or legacy attestation. It does not redirect to whichever package is currently published. |
| Unknown or ambiguous filename | 404; no arbitrary release is selected. |
| Revoked legacy package | 410. |
Only a single filename under the v2 base is resolved. Inventory any additional XML names or nested legacy paths and configure explicit proxy redirects for them. The old local-file and wyUpdate delivery mechanisms are retired; existing updatesUrl discovery fields remain for wire compatibility.
Back up old XML and packages, then upload each package through the normal verification endpoint with its original filename, application version, settings, and expected SHA-256. When a historical package must remain public without selecting it as production, record legacy provenance using the following endpoint.
Attest a legacy import
POST /api/v1/desktop-services/admin/updates/releases/{id}/legacy-import
Requires Authentication - Scopes: admin:desktop-updates:publish
Admin - Default tenant
Send If-Match containing the release revision and JSON such as {"reason":"Imported previously published v2 installer; backup and checksum verified"}. The release must be READY and storage must be available. The response is the updated release and ETag. This operation makes that package publicly available and records the attestation in change history; it does not change the production feed.
ReleaseResponse
| Status | Description |
|---|---|
200 OK |
Legacy attestation recorded; updated release and ETag returned. |
409 Conflict |
Release is not ready. |
412 Precondition Failed |
Release revision is stale. |
428 Precondition Required |
Missing release If-Match header. |
503 Service Unavailable |
Package storage is unavailable. |
Verify imported package bytes, filename redirects, and the initial v3 publication before switching discovery. Follow the PVC transition procedure before retiring the old volume.
Error responses
The status tables above highlight endpoint-specific outcomes. Every administration method also returns 401 for missing or invalid credentials or 403 for insufficient scope or default-tenant authority.
| Status | Meaning and next step |
|---|---|
400 Bad Request |
Invalid package, checksum mismatch, unsafe ZIP path, invalid version/settings/reason, service validation limit, invalid paging, or missing/invalid idempotency key. Correct the request. |
401 Unauthorized / 403 Forbidden |
Missing or invalid administration credentials, insufficient permission, or user outside the default tenant. Public delivery requires no credentials. |
404 Not Found |
Unknown resource, unpublished public package, wrong extension, or unknown/ambiguous legacy filename. |
409 Conflict |
Duplicate normalized version, reuse of an idempotency key for different input/actor, ineligible release, or attempt to revoke current production. |
410 Gone |
Downloads or hosted notes for a revoked release. |
412 Precondition Failed |
Stale revision. Refresh the resource or publication preview and review again before confirming. |
413 Content Too Large |
Multipart or gateway upload limit exceeded; check deployment limits. Package-validator limits may instead return 400. |
416 Range Not Satisfiable |
Invalid, unsatisfiable, or multiple byte ranges. |
428 Precondition Required |
Missing If-Match on a mutation requiring a revision. |
503 Service Unavailable |
No publication yet, or required package storage is unavailable. A failed publication does not replace production. |
Errors use the API's problem-detail response where applicable; use the status and detail to guide recovery. Preserve request correlation identifiers when reporting failures.