> ## Documentation Index
> Fetch the complete documentation index at: https://docs.scalacomputing.com/llms.txt
> Use this file to discover all available pages before exploring further.

# API Changelog

> All notable API changes will be documented in this file.

All notable API changes will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

***

## \[Unreleased] - 2026-03-06

### Added

* **`workloadMetadata` on `POST /api/v1/tracesets/upload-url`**: `TracesetUploadUrlRequest` now documents `workloadMetadata`, which the endpoint already accepted. It is stored on the traceset as given, up to 65,536 bytes when serialized, and must be a JSON object. It declares the traceset's rank count, either as `shape.rankCount` or as parallelism degrees (`dp`, `tp`, `sp`, `ep`, `pp`, multiplied together, with an absent degree counting as 1); the traceset reports that count as `rankCountDerived`, and attaching it to a configuration uses it. The Python client's `TracesetUploadUrlRequest` gains `workload_metadata`, and `central-cli traceset upload` gains `--workload-metadata <PATH>`, a JSON file that the command requires.

* **Simple simulation endpoint**: `POST /api/v1/simple/simulations` — submit a collective-operation workload (AllReduce, Broadcast, etc.) with a single request. The platform handles topology derivation, traceset synthesis, and simulation launch automatically.

* **Topology constraints**: `topologyConstraints` object on `SimpleSimulationRequest` with `switchRadix` (enum: `64x800G`, `128x400G`, `256x200G`) and `subscriptionRatio` (enum: `1:1`, `2:1`, `3:1`, `4:1`). Enables arbitrary rank counts by deriving a valid 2-tier CLOS topology from hardware constraints.

* **Chakra mapping files**: a mapping-file resource that pins each Chakra logical rank to a server, and optionally declares scale-up groups and their fabric parameters. Upload a file once, attach it to a simulation configuration, and every simulation created from that configuration is launched with it. Without one, placement is unchanged: rank `i` runs on the `i`-th Chakra-capable server. Running a simulation to completion with a mapping file is not yet available: a simulation on one of the platform's own Chakra tracesets fails before the simulator starts until an update to the platform's simulation hosts is in service. A mapping file attaches behind an uploaded traceset only when that traceset has a rank count, which it reports as `rankCountDerived`. For a new upload that rank count is the one its workload metadata declares, as `shape.rankCount` or as parallelism degrees (`dp`, `tp`, `sp`, `ep`, `pp`). Behind a traceset with no rank count, the mapping-file attach, like the traceset attach, answers `422` `UNPROCESSABLE_ENTITY`, which retrying does not change (see Current limitations in [Chakra Mapping Files](https://docs.scalacomputing.com/v0.1.2376/chakra-mapping-files.md)). Eight new operations, all tagged `mapping-files`:

  | Method | Path | Purpose |
  | - | - | - |
  | POST | `/api/v1/mapping-files/upload-url` | Presigned multipart upload URLs for a new mapping file; `name` is optional (default `chakra-mapping.txt`) and `sizeBytes` is at most 64,000,000 |
  | POST | `/api/v1/mapping-files/upload-url/complete` | Validate and store the file: `201` with the new file, or `200` with `duplicateOfExisting: true` when the workspace already holds the same bytes; `422` `MAPPING_FILE_INVALID` names the first offending line |
  | GET | `/api/v1/mapping-files` | A workspace's live mapping files, newest first; `workspaceId` is required; cursor pagination through `next` and `nextCursor` |
  | GET | `/api/v1/mapping-files/{mapping_file_id}` | One mapping file's details; `410` `MAPPING_FILE_DELETED` once deleted |
  | GET | `/api/v1/mapping-files/{mapping_file_id}/download` | Presigned URL serving the exact uploaded bytes; `410` `MAPPING_FILE_DELETED` once deleted |
  | DELETE | `/api/v1/mapping-files/{mapping_file_id}` | Delete a mapping file, whether or not a configuration refers to it |
  | PATCH | `/api/v1/configurations/{config_id}/mapping-file` | Attach a mapping file to a configuration (`If-Match` supported) |
  | DELETE | `/api/v1/configurations/{config_id}/mapping-file` | Detach it and restore in-order placement (`If-Match` supported) |

  The stored file is the simulator's own text format, byte for byte, with a 64 MB per-file limit and no per-workspace quota. `MappingFileSummary` carries `id`, `name`, `workspaceId`, `sizeBytes`, `rankCount`, `scaleUpGroupCount`, `hasScaleUp` and `createdAt`; `MappingFileDetails` adds `description`, `hash`, `etag` and `duplicateOfExisting`. New `error.code` values: `MAPPING_FILE_INVALID`, `MAPPING_FILE_HASH_COLLISION`, `MAPPING_FILE_DELETED`, `MAPPING_REQUIRES_TRACESET`, `MAPPING_REQUIRES_CHAKRA_APP`, `MAPPING_RANK_COUNT_MISMATCH`, `MAPPING_PARAMS_LOCKED`, `TRACESET_CONFLICTS_WITH_MAPPING` and `MAPPING_FILE_DELETED_REFERENCE`, plus `MAPPING_FILE_MISSING` and `MAPPING_PARAMS_INCONSISTENT` on `500` platform faults. See [Chakra Mapping Files](https://docs.scalacomputing.com/v0.1.2376/chakra-mapping-files.md) for the file format and the full flow.

* **`activeMappingFile` on `ConfigurationDetails`**: names the attached mapping file as `{ "id", "name", "deleted" }`, or is `null`. `deleted` is resolved when the configuration is read, so a configuration that still refers to a deleted file reports it as deleted. Only attach and detach set it: a configuration update whose body carries `activeMappingFile` returns `400`. Attach requires at least one Chakra application (`422` `MAPPING_REQUIRES_CHAKRA_APP`), an attached traceset (`409` `MAPPING_REQUIRES_TRACESET`) that has a rank count (`422` `UNPROCESSABLE_ENTITY` otherwise, not resolved by retrying), a mapping file in the configuration's workspace that has not been deleted (`404` otherwise), and equal rank counts (`422` `MAPPING_RANK_COUNT_MISMATCH`, naming both counts). While a file is attached, a configuration update, an application-parameter update or an application create that would change `UseMapping` or `MappingFileName` returns `422` `MAPPING_PARAMS_LOCKED`, and a traceset with a different rank count returns `409` `TRACESET_CONFLICTS_WITH_MAPPING` on traceset attach and on configuration update.

* **`appliedMappingFile` on `SimulationDetails`**: names the mapping file the simulation was created with, as `{ "id", "name", "deleted" }`, and is absent for a configuration with no mapping file. It keeps naming the file after a delete, with `deleted` reading `true`; deleting a mapping file never changes a simulation already created. `POST /api/v1/simulations` from a configuration whose attached mapping file has been deleted returns `409` `MAPPING_FILE_DELETED_REFERENCE`, naming the file. A mapped run never falls back to in-order placement: a file that cannot be delivered to the simulator fails the simulation. When the platform rejects the file while staging it, the `failureReason` reads `Mapping file <name> (<id>) could not be delivered`, with a long `<name>` possibly shortened to end in `…` and the id always in full; a failure not attributed to the mapping file reports a general failure reason.

### Fixed

* **Simulation results endpoints**: Removed non-functional workspace ownership check that blocked all users from accessing results. `DefaultWorkspace.owner = 'system'` caused `verify_workspace_access` to reject every real user. Endpoints now correctly return results for any authenticated user. Affected: `GET /simulations/{id}/results`, `GET /simulations/{id}/results/download-url`, `GET /simulations/{id}/results/data`.

### Changed

* **Upload part URLs carry their size and real expiry**: `POST /api/v1/tracesets/upload-url` and `POST /api/v1/mapping-files/upload-url` presign each part URL for one hour (was 15 minutes). `PartUploadUrl` gains `size`, the number of bytes to PUT for that part; every part but the last is the server's part size, and a mapping file's part may be shorter, since its `sizeBytes` is an upper bound. The part URLs' expiry is now reported as `partUrlsExpireAt`, on each `FileUploadUrls` entry and on `MappingFileUploadUrlResponse`. `expiresAt` is unchanged: it is the 24-hour upload session lifetime, which it always was, not the URLs' expiry. A URL can stop working before `partUrlsExpireAt` if the platform's signing credentials expire first. `central-cli traceset upload` now uploads files over 2 MiB whole: it sent at most 2 MiB per part.

* **BREAKING**: `GET /api/v1/models` reports the Pod and Rack topology container models as `type: "pod"` and `type: "rack"`. They were reported as `"switch"`. `ModelType` gains `rack` and `pod`, the values `GET /api/v1/models/{model_id}` already reports for these models, and `type=rack` and `type=pod` become valid filters on the list (the server already accepted them). A client generated from an earlier spec with a closed `ModelType` enum fails to parse a list response that contains these models; to migrate, regenerate or upgrade the client.

* **BREAKING**: `POST /api/v1/tracesets/upload-url` requires an upload with `primary` files to declare its rank count in `workloadMetadata`, as `shape.rankCount` or as parallelism degrees (`dp`, `tp`, `sp`, `ep`, `pp`). Without one it returns **400** `VALIDATION_ERROR` naming both forms, before any upload URL is issued. It also returns **400** for a declaration it cannot accept, naming the field: a `shape.rankCount` or degree that is not a whole number of at least 1, a `shape.rankCount` that differs from the parallelism product (naming both), a count above 2,147,483,647, or a `workloadMetadata`, `shape` or `parallelism` that is neither an object nor `null`. An upload of supporting files only needs no declaration. To migrate: add `workloadMetadata` declaring the rank count to every upload of trace files; with `central-cli`, pass `--workload-metadata <PATH>`.

* **`POST /api/v1/tracesets/upload-url/complete`** reports the traceset `ready` for every completed upload, including one with new files, which previously reported `processing`. Every file of the upload reports `metadataStatus` `complete`. A traceset uploaded with exactly one `primary` file that declares N ranks reports that file's `rankRange` as `0` to N−1.

* **Traceset promotion** (the Python client's `promote_traceset`) returns **422** `UNPROCESSABLE_ENTITY` when the new `workloadMetadata` would change the rank count the traceset declared at upload, naming both counts. A declared rank count cannot be changed after upload: for a traceset that carries one, the new `workloadMetadata` must declare the same count, and metadata that declares none differs from it.

* **BREAKING**: `min` and `max` in a component schema (`ParameterValueSchema`, returned by `GET /api/v1/components/{id}`) are now `ParameterBound` objects instead of numbers. A numeric type's bound is `{"value": 1}`. A dimensional type's (`datarate`, `timeval`, `bytes`, `queuesize`) bound carries its unit, such as `{"value": 10, "unit": "us"}`, which can differ from the unit of the parameter's value. A dimensional bound of `0` is `{"value": 0}` with no unit. A model's bound that the platform cannot read is left out of the schema. To migrate: read `min.value` and `min.unit` where you read `min`.

* **BREAKING**: `GET /api/v1/simulations` now accepts only a single `field:direction` pair in `sort` (for example `created_at:desc`). A compound sort such as `status:asc,created_at:desc` returns **400**; it previously returned 200 but could not be paginated correctly past the first page. A `next` cursor replayed with a different `sort` than the one that produced it also returns **400** instead of a wrong page. To migrate: send one sort field, and when you change `sort`, restart pagination without `next`.

* **Presigned URL expiry reduced**: Simulation result download URLs (`GET /simulations/{id}/results` and `GET /simulations/{id}/results/download-url`) now expire after **15 minutes (900 s)** instead of the previous 60 minutes (3600 s). This is a defense-in-depth hardening to limit exposure if a URL leaks. Clients that fetch a presigned URL and consume it after a delay greater than 15 minutes will receive an HTTP 403 from S3. The `expiresAt` field in the `download-url` response reflects the actual expiry. No known consumers cache presigned URLs beyond this window.

* **BREAKING**: `SimpleWorkload.ranks` changed from integer enum (`[64, 128, 256, 512, 1024, 4096]`) to `integer` with `minimum: 1`. Without `topologyConstraints`, the server still restricts to the 6 reference values. With `topologyConstraints`, any positive value satisfying the constraint solver is accepted.

* **BREAKING**: `TracesetFile` renamed to `FileResponse` in the Python client. Adds required field `metadata_status`. Update imports: `from python_client.models import FileResponse` (was `TracesetFile`).

* **BREAKING**: `ContainerDefinition.type_` and `AllowedTypesResponse.container_type` now use `ContainerType` instead of `ComponentType` in the Python client. Both are `str` enums with identical string values, so equality comparisons by value still work. Update `isinstance` checks if used.

* **BREAKING**: `DlrmMetadata`, `TraceMetadata`, and `TraceMetadataWorkloadType` removed from the Python client. These types are no longer part of the API surface. Remove any imports that reference them.

* **Traceset listing scope widened**: `GET /api/v1/tracesets?workspaceId=X` now returns all non-deleted tracesets visible to the workspace, including promoted tracesets with scope `tenant` or `public`. Previously, only `workspace`-scoped tracesets were returned. This ensures promoted tracesets remain visible after promotion.

* **TrafficType output consolidation**: API responses now return `"chakra"` where `"coordinated"` was previously emitted for the `trafficType` field. Input is backward-compatible: both `"chakra"` and `"coordinated"` are accepted via serde alias. This reflects the A5 schema migration consolidating Coordinated into Chakra.

* **BREAKING**: `PATCH /api/v1/configurations/{config_id}/traceset` now requires the traceset to have a positive rank count before attaching. The endpoint previously accepted any valid traceset regardless of library state; it now enforces that the rank count is present and positive (required to populate `NumOfChakraFiles` in the Chakra workload model).

  Two new error responses are returned when the traceset's rank count is missing or invalid:

  | Condition | Status | Error code |
  | - | - | - |
  | The traceset has no rank count | `422 Unprocessable Entity` | `UNPROCESSABLE_ENTITY` |
  | `rank_count` is present but ≤ 0 — data invariant violation | `422 Unprocessable Entity` | `UNPROCESSABLE_ENTITY` |

  **Migration**: Both responses are permanent for that traceset and do not resolve on retry. This endpoint first answered a traceset with no rank count with a retry-safe `409 Conflict`; see the next entry.

* **BREAKING**: a traceset with no rank count is now answered `422 Unprocessable Entity` (`UNPROCESSABLE_ENTITY`) in place of the retry-safe `409 Conflict` (`CONFLICT`), by `PATCH /api/v1/configurations/{config_id}/traceset`, by `PATCH /api/v1/configurations/{config_id}/mapping-file`, and by `PATCH /api/v1/configurations/{config_id}` when a mapping file is attached and the body carries `activeTraceset`. The `409` said to retry after metadata extraction completed, but a traceset you upload never gained a rank count that way, so the retry never succeeded. The `422` names the traceset and does not resolve on retry.

  **Migration**: Stop retrying the request on this response. A client that retries on `409` `CONFLICT` for these operations should treat the `422` as permanent for that traceset.

## \[Previous Unreleased] - 2024-12-23

### Changed

* **BREAKING**: `Pagination.limit` renamed to `Pagination.count` in API responses
  * Affected: All paginated list endpoints
  * Before: `{"limit": 10, "hasMore": true, "cursor": "..."}`
  * After: `{"count": 10, "hasMore": true, "cursor": "..."}`
  * Semantic change: `count` represents the actual number of items returned (may be less than requested limit)
  * Note: Query parameter `?limit=N` remains unchanged; only the response field name changed
  * Migration: Update client code to read `count` instead of `limit` from pagination responses

* **BREAKING**: `ParameterDefinition.default` type changed from JSON value to string
  * Affected: `GET /api/v1/models/{id}`, `GET /api/v1/components/{id}`
  * Before: `{"default": 100}` or `{"default": true}`
  * After: `{"default": "100"}` or `{"default": "true"}`
  * Migration: Parse the string value according to the parameter's `type` field

* **BREAKING**: `ComponentDeclaration.model` format changed to use standardized model reference
  * Affected: `GET /api/v1/templates/{id}`, `GET /api/v1/configurations/{id}`, `GET /api/v1/configurations/{id}/versions/{v}`, `POST /api/v1/templates/{id}/clone`
  * Before: `{"model": "ScalaSwitch:3.2.1", "name": "spine-1"}` (model in internal format, name user-defined)
  * After: `{"model": "scala-switch-v3.2.1", "name": "spine-1"}` (model standardized to API format, name remains user-manageable)
  * Format: Model field is standardized to API format (`scala-switch-v{version}`)
  * Name field: Remains user-manageable and unchanged from input
  * Benefit: Model field can be parsed to extract model reference for use with `GET /api/v1/models/{model_id}`
  * Migration: Update any code that parses or displays component model fields

* **BREAKING**: Only referenced components are returned in configuration responses
  * Affected: `GET /api/v1/templates/{id}`, `GET /api/v1/configurations/{id}`, `POST /api/v1/templates/{id}/clone`
  * Before: All components declared in the configuration were returned, including unused ones
  * After: Only components referenced in `topology.components`, `topology.containers`, or `containers[].components` are returned
  * Rationale: Reduces response size and focuses on components actually used in the topology
  * Migration: If your code relies on seeing all declared components, use the stored configuration data directly

* **Template cloning preserves original data format**
  * Affected: `POST /api/v1/templates/{id}/clone`
  * Change: Cloned configurations now preserve the original template's data format (legacy or new format)
  * Before: Template data was transformed to new format during cloning
  * After: Template data format is preserved, only metadata fields (version, workspaceId, name, status, parent\_config\_id) are updated
  * Benefit: Maintains compatibility with existing templates and allows format-specific handling

### Removed

* **BREAKING**: Removed fields from `ParameterDefinition`:
  * `componentType` - was used for nested component type references
  * `model` - was used for model references within parameters
  * `parameters` - was used for nested parameter definitions
  * Migration: These fields were not widely used. If you relied on nested parameter structures, contact the API team for migration guidance.

### Added

* **Model Metadata Enrichment**: Templates API now fetches model metadata from `model_version`
  * Affected: `GET /api/v1/templates/{id}`
  * Components are enriched with parameter schemas and type information from the database
  * When a component references a model (e.g., `ScalaSwitch:4.0.0`), the API looks up the model in `model_version` and includes the full parameter schema
  * Benefit: Clients receive complete model metadata without needing separate API calls to `/api/v1/models/{model_id}`

* **Parameter Extraction**: Extended parameter extraction to include coordinator-level parameters
  * Application models now expose parameters from `Coordinators`, `ModelProfiles`, and `TrafficPattern` sections
  * Parameters include group information (e.g., `Delay.CoordinatorAttributes`)

***


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.