Skip to content

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:

{"items": [], "totalElements": 0, "page": 0, "size": 50}
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.upload
  • desktop-updates.release.edit
  • desktop-updates.release.revoke
  • desktop-updates.release.legacy-import
  • desktop-updates.publication.publish
  • desktop-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

curl https://api.example.com/api/v1/desktop-services/updates/v3/updates.xml
curl https://api.example.com/api/v1/desktop-services/updates/v3/releases/550e8400-e29b-41d4-a716-446655440000/package.msi \
  --output koldan-9.2.0.0.msi
curl --head https://api.example.com/api/v1/desktop-services/updates/v3/releases/550e8400-e29b-41d4-a716-446655440000/package.msi
curl https://api.example.com/api/v1/desktop-services/updates/v3/releases/550e8400-e29b-41d4-a716-446655440000/package.msi \
  -H 'Range: bytes=0-1023' \
  --output package-first-1024.bin
curl https://api.example.com/api/v1/desktop-services/updates/v3/publications/8f9760c2-81ac-4c27-9712-f247ce35d682/changelog

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.