HELIX Docs
Platform API

Live wearable and Unreal contract

The deployed Universal Item upload, finalize, status, download, and Unreal rendition contract, audited against helix3.

Live wearable and Unreal contract

This page records the currently deployed Universal Item transport behavior. It was audited on July 29, 2026 against the helix3 API and validator worker. The broader Interactive Universal Items handoff remains the executable target for shared behavior, authoritative state, runtime adapters, and default-on scrubbed Vault publication. Those target features are not implied by the live upload API described here.

Item-first upload

Create one product, upload each source rendition, finalize each source asset, then wait for every rendition the runtime may consume to become ready:

  1. POST /api/v1/universal-items once.
  2. PUT the web GLB and every Unreal file to the returned presigned URLs.
  3. Finalize the web asset once and the Unreal asset once.
  4. Poll the item until the required assets report status: "ready".
  5. Download only from the public URLs in the read response.

One dual-format wearable has one Universal Item ID and two creator-uploaded source asset rows:

Source rowShape
rendition: "web"One GLB declared with filename, mimeType, and sizeBytes.
rendition: "unreal"Cooked packaged output—normally the matching .pak, .utoc, and .ucas files—declared through files[], plus engineVersion and targetPlatforms.

Do not create separate items for web and Unreal. A generated thumbnail asset row may appear asynchronously after processing. It is a derived rendition, not a third source upload, so consumers must not assume the final assets array has exactly two rows.

Wearables also require the live Character Creator fields:

  • kind: "wearable"
  • usage: "wear"
  • one valid slot, such as Cosmetic.Slot.Clothing.Top
  • genders: ["male"], ["female"], or ["male", "female"]

The generic asset DTO can represent mixed fields, but the canonical integration shape keeps web and Unreal on separate rows. Unreal-only engine and platform claims cannot truthfully live on the web row.

Finalize and readiness

Create returns presigned PUT URLs without waiting for bytes. Finalize operates on one asset, not one file. It checks every Unreal file that declared a SHA-256, runs the applicable moderation gate, records metadata.pipeline.finalizedAt, and moves the asset from awaiting_upload to pending. Finalize means verified and queued; it does not mean usable.

awaiting_upload → pending → processing → ready
                                      ↘ rejected | failed

Status is the trust boundary

The CDN URLs are allocated before upload. During the deployed probe, all three uploaded objects were downloadable while both source assets still reported awaiting_upload. Never load or mount a rendition merely because its URL exists or returns HTTP 200. Require asset.status === "ready".

Fresh deployed requests returned HTTP 201 from both asset finalize calls. The deployed OpenAPI document currently describes the same success response as HTTP 200. This is an unresolved contract discrepancy, tracked in backend task CH-1162. Until that task settles the stable status code, record 201 as observed runtime behavior and 200 as the published OpenAPI metadata; neither response code makes an asset ready.

Read and select renditions

The public item read returns the item and all of its current asset facts. Runtimes own selection:

  • Web may use only a ready web rendition.
  • Unreal should prefer a ready unreal rendition whose engine version matches the running client and whose targetPlatforms includes Win64.
  • Unreal may fall back to a ready web GLB when the client supports that path.
  • A thumbnail row is presentation metadata, never a runtime replacement for either source.

cdnUrl and files[].url are public CDN URLs. uploadUrl is temporary and appears only in create/update responses.

Public URLs are mutable pointers

The public URLs are durable and deterministic, but they are not immutable Vault versions. PATCH /:itemId/assets/:assetId can re-declare files and overwrite the same object key. Invalidate caches when status or build metadata changes. Pinned immutable URLs belong to the target Vault version contract.

Live Unreal metadata and integrity

FieldDeployed behavior
engineVersionThe Unreal version used to cook the package and the runtime compatibility version consumers must match. It is stored per Unreal asset; x-engine-version wins over the body and omission defaults to 5.7. HELIX currently targets Unreal 5.7, but 5.7 is a HELIX target/default—not a requirement of the .pak/.utoc/.ucas format or transport API.
targetPlatformsCurrent Unreal runtime support is Win64 only. New Unreal assets must declare ["Win64"]; all other target values are unsupported.
files[].sha256Optional, normalized to lowercase. When supplied, finalize reads the object, rejects missing or mismatched bytes with HTTP 422 sha256_mismatch, and corrects stored size to the measured length.
Asset revisionNot available. There is no immutable revision, build ID, release note, or asset history on a Universal Item asset.

Publish Win64 packages only

HELIX does not currently support native Unreal packages for any target other than Win64. Cook, publish, select, and mount the .pak/.utoc/.ucas set for Win64, and keep targetPlatforms: ["Win64"] on the asset.

Do not relabel an older cook

Unreal 5.5 packages are not compatible with the current HELIX 5.7 runtime. Keep an existing 5.5 package row labeled engineVersion: "5.5"; changing only its metadata does not change the cooked bytes. Re-cook the package with Unreal 5.7, publish the new .pak/.utoc/.ucas set with engineVersion: "5.7", and wait for that asset to become ready before selecting it. Explicit patch values such as 5.7.1 are accepted when they reflect the exact engine used for the cook.

Upload cooked packaged output, not loose Content Browser .uasset files. For an Io Store build, keep the matching .pak, .utoc, and .ucas files together and send a SHA-256 for every file. The live platform validates the envelope—file-key syntax, the 512-file cap, engine/platform syntax, and optional checksums—but it does not parse or mount the package, prove the declared engineVersion can load it, validate cook compatibility or dependencies, require a manifest, enforce a complete expected file set, or validate Unreal naming conventions. Unreal binaries are treated as non-visual and skipped by media moderation.

The current multipart endpoint, POST /api/v1/universal-items/upload, is web-only. Publish both formats through the presigned JSON create flow, or add the Unreal source row afterward with POST /:itemId/assets.

Live catalogue mode versus target Vault publication

The live libraryContribution field contributes a public, free Universal Item to the catalogue. It is not the target scrubbed Vault-definition publication. The backend has universal_items.vault_asset_id and immutable Vault versions, but Universal Item asset finalize does not yet create or link that definition automatically.

The target remains:

  • one immutable definition shared by Web and Unreal presentation adapters;
  • separate owned, placement, session, and private state partitions;
  • one authoritative function/event choke point with validation, permissions, idempotency, capabilities, and replication filtering;
  • a first-party ItemInteractionAbility for abstract input, sockets, animation, hands, camera, and movement channels;
  • default-on publication of a scrubbed definition to Vault, respecting the creator preference and an explicit per-publish opt-out.

The publish transform keeps manifests, clean defaults, functions/events, sockets, logic, compatibility, and pinned assets. It removes owner/inventory identifiers, live owned values, placement/world identifiers and transforms, bindings, occupants, scores, ammunition, cooldowns, timers, private values, runtime handles, and session state. Installing creates fresh IDs and revision-zero state. None of this target runtime/package surface is shipped by the rendition API today.

Verification and sources

The deployed transport round trip is preserved as a dual-format test item. It returned ready web and Unreal source assets, matching downloaded SHA-256 values, and a generated thumbnail row after processing. That fixture proves the deployed byte-integrity flow only; it does not prove a cooked package mounts. The sendable test below requires real .pak, .utoc, and .ucas cooking outputs for that compatibility check.

On this page