---
title: "Update a test case"
method: PATCH
path: "/api/cases/{caseId}"
tags: ["Test Cases"]
---

# Update a test case

`PATCH /api/cases/{caseId}`

## Request

- Base URL: `https://aximo.autify.com`
- URL: `https://aximo.autify.com/api/cases/{caseId}`
- Auth: API key in header `x-api-key`
- TypeScript: <https://skmtc.dev/autify/apis/aximo-api/docs/api/cases/:caseId/patch.ts> (a function with typed inputs; its response is checked against the schema)

Updates the provided fields of a test case; omitted fields are left
unchanged. At least one field is required. Set `startUrl` or
`description` to `null` to clear it; an empty-string `description` is
stored as an empty description, not treated as a clear.

**Default targets.** `apiMobileConfig`, `apiWebConfig`,
`apiDesktopConfig` and `apiMultiDeviceConfig` carry the case's
per-target defaults in the same shapes `GET /api/cases/{caseId}`
returns. Per key: omit = unchanged, `null` = clear, value = replaced
whole (no deep merge — to change one app, send that whole config).
Send only the keys you are changing; an unchanged config is not
re-validated. `[]` is not a clear for `apiMultiDeviceConfig` (2-3
entries are required). Device and upload identifiers are the encrypted
tokens from `GET /api/devices` (`arn`), `GET /api/uploads`
(`uploadArn`) or a previous `GET /api/cases/{caseId}`; tokens are
non-deterministic and must not be compared for equality.
`apiDesktopConfig` takes only `{ userDeviceId }` from
`GET /api/devices/user`; name/platform/os are resolved server-side.
`apiMobileConfig` binds either a Device Farm device (`deviceArn` +
`deviceName`) or a registered local phone (`userDeviceId`, with
`platform`/`localDeviceName` resolved server-side); never both.
`apiMobileConfig.platform` is `ANDROID|IOS`; a multi-device entry's
`mobilePlatform` is `android|ios`. The legacy single-app fields
(`uploadArn`/`uploadType`/`appName`/`bundleId`) are accepted but
`apps[]` is preferred. `startUrl` values must be http(s). Setting a config requires the matching feature
for the organization (BYOD for `apiDesktopConfig`, for a local phone
in `apiMobileConfig` and for local entries in `apiMultiDeviceConfig`; multi-device for
`apiMultiDeviceConfig`); clearing never does. `type` and the configs
are not cross-validated today (set both in one call to retarget). Field
names from the run trigger or tRPC (`userDeviceId`, `deviceArn`,
`apps`, `mobileConfig`, `devices`, …) at the top level are rejected
with a hint rather than silently ignored.

Validation order: caseId → auth → body schema → tokens → membership →
case lookup → configs, one key at a time in the order `apiWebConfig`,
`apiMobileConfig`, `apiDesktopConfig`, `apiMultiDeviceConfig` (a key's
feature gate runs before its own upload/device lookups; a config
identical to the stored one is skipped) → single write. The first
failing key is reported and nothing is written. Both `apps[]` and the
legacy top-level `uploadArn` are checked for ownership when present.

## Path parameters

- `caseId` string, uuid, required

## Request body

- object
  - `title` string
  - `description` string, nullable: Set to `null` to clear the description. Omitting the field leaves it unchanged; an empty string is stored as an empty description, not a clear.
  - `scenario` string: Scenario content; interpretation depends on `format`.
  - `type` 'web' | 'mobile' | 'desktop' | 'multi_device'
  - `format` 'gherkin' | 'markdown' | 'plain_text'
  - `startUrl` string, uri, nullable
  - `directoryId` string, uuid, nullable: Move the case into a directory of its workspace; `null` moves it to the workspace root.
  - `apiMobileConfig` CaseMobileConfig: Exactly one device binding: a Device Farm device (`deviceArn` + `deviceName`) or a registered local phone (`userDeviceId` from `GET /api/devices/user`). For a local phone, `platform` and `localDeviceName` are resolved from the device row server-side and `localDeviceName` is read-only; an app reference on a local phone must carry a `bundleId`.
    - `deviceArn` string: Encrypted device identifier token. Not set for a local phone.
    - `deviceName` string
    - `userDeviceId` string, uuid: Registered local phone to run on instead of Device Farm.
    - `localDeviceName` string: Display-name snapshot of the registered local phone.
    - `platform` 'ANDROID' | 'IOS'
    - `uploadArn` string: Encrypted upload identifier token.
    - `uploadType` 'own_app' | 'existing' | 'sample'
    - `appName` string
    - `bundleId` string
    - `apps` CaseAppInfo[]
      - `uploadArn` string, required: Encrypted upload identifier token.
      - `uploadType` 'own_app' | 'existing' | 'sample', required
      - `appName` string, nullable, required
      - `bundleId` string
  - `apiWebConfig` CaseWebConfig
    - `hostnameOverrideIds` string[]
  - `apiDesktopConfig` object, nullable: Default BYOD desktop target; `null` clears it. Requires BYOD.
    - `userDeviceId` string, uuid, required: A registered BYOD desktop device from `GET /api/devices/user`.
  - `apiMultiDeviceConfig` CaseMultiDeviceDevice[], nullable: Multi-device targeting (2-3 entries); `null` clears it. Requires the multi-device feature; local entries (`userDeviceId`) also require BYOD. `localDeviceName` is ignored on input and resolved server-side.
    - union
      - CaseMultiDeviceWebDevice
        - `kind` 'web', required
        - `name` string, required
        - `startUrl` string, nullable
        - `userDeviceId` string, uuid: When set, this entry runs on the user's registered desktop device.
        - `localDeviceName` string: Display-name snapshot of the registered device, resolved when the config was saved.
      - CaseMultiDeviceMobileDevice: On input, exactly one of a Device Farm device (`deviceArn` + `deviceName`) or a registered local phone (`userDeviceId`) is required; they cannot be combined. A `userDeviceId` entry that carries `uploadArn`/`apps` must include a `bundleId` (top-level or on `apps[0]`), and a top-level `bundleId` must match `apps[0].bundleId`. The same `userDeviceId` may appear only once across entries.
        - `kind` 'mobile', required
        - `name` string, required
        - `mobilePlatform` 'android' | 'ios', required
        - `deviceArn` string: Encrypted device identifier token.
        - `deviceName` string
        - `userDeviceId` string, uuid: When set, this entry runs on the user's registered phone.
        - `localDeviceName` string: Display-name snapshot of the registered device, resolved when the config was saved.
        - `uploadArn` string: Encrypted upload identifier token.
        - `uploadType` 'own_app' | 'existing' | 'sample'
        - `appName` string
        - `bundleId` string
        - `apps` CaseAppInfo[]
          - `uploadArn` string, required: Encrypted upload identifier token.
          - `uploadType` 'own_app' | 'existing' | 'sample', required
          - `appName` string, nullable, required
          - `bundleId` string
  - `tags` string[]: Caller-supplied labels, for example the commit and pull request a run was testing. At most 10, each 1-64 characters after trimming; duplicates are collapsed. A request exceeding a bound is refused with 400, never truncated. Tags are returned exactly as stored and are visible to everyone in your organization, so never put secrets in them.

## Response `200`

Case updated.

- object
  - `success` boolean, required
  - `case` object, required
    - `tags` string[], required: Caller-supplied labels, for example the commit and pull request a run was testing. At most 10, each 1-64 characters after trimming; duplicates are collapsed. A request exceeding a bound is refused with 400, never truncated. Tags are returned exactly as stored and are visible to everyone in your organization, so never put secrets in them.
    - `id` string, uuid, required
    - `projectId` string, uuid, required
    - `type` 'web' | 'mobile' | 'desktop' | 'multi_device', required
    - `title` string, required
    - `description` string, nullable
    - `scenario` string, required
    - `format` 'gherkin' | 'markdown' | 'plain_text', required
    - `startUrl` string, uri, nullable
    - `updatedAt` string, date-time, required
    - `apiMobileConfig` CaseMobileConfig: Exactly one device binding: a Device Farm device (`deviceArn` + `deviceName`) or a registered local phone (`userDeviceId` from `GET /api/devices/user`). For a local phone, `platform` and `localDeviceName` are resolved from the device row server-side and `localDeviceName` is read-only; an app reference on a local phone must carry a `bundleId`.
      - `deviceArn` string: Encrypted device identifier token. Not set for a local phone.
      - `deviceName` string
      - `userDeviceId` string, uuid: Registered local phone to run on instead of Device Farm.
      - `localDeviceName` string: Display-name snapshot of the registered local phone.
      - `platform` 'ANDROID' | 'IOS'
      - `uploadArn` string: Encrypted upload identifier token.
      - `uploadType` 'own_app' | 'existing' | 'sample'
      - `appName` string
      - `bundleId` string
      - `apps` CaseAppInfo[]
        - `uploadArn` string, required: Encrypted upload identifier token.
        - `uploadType` 'own_app' | 'existing' | 'sample', required
        - `appName` string, nullable, required
        - `bundleId` string
    - `apiWebConfig` CaseWebConfig
      - `hostnameOverrideIds` string[]
    - `apiDesktopConfig` CaseDesktopConfig: `name`, `platform` and `os` are snapshotted from the registered device when the config is saved; only `userDeviceId` is accepted on input. `name` and `platform` are absent when the case was saved from a session whose desktop had already been deleted.
      - `userDeviceId` string, uuid, required: The registered BYOD desktop device this case runs on.
      - `name` string
      - `platform` 'windows' | 'macos' | 'linux'
      - `os` string, nullable, required
    - `apiMultiDeviceConfig` CaseMultiDeviceDevice[], nullable
      - union
        - CaseMultiDeviceWebDevice
          - `kind` 'web', required
          - `name` string, required
          - `startUrl` string, nullable
          - `userDeviceId` string, uuid: When set, this entry runs on the user's registered desktop device.
          - `localDeviceName` string: Display-name snapshot of the registered device, resolved when the config was saved.
        - CaseMultiDeviceMobileDevice: On input, exactly one of a Device Farm device (`deviceArn` + `deviceName`) or a registered local phone (`userDeviceId`) is required; they cannot be combined. A `userDeviceId` entry that carries `uploadArn`/`apps` must include a `bundleId` (top-level or on `apps[0]`), and a top-level `bundleId` must match `apps[0].bundleId`. The same `userDeviceId` may appear only once across entries.
          - `kind` 'mobile', required
          - `name` string, required
          - `mobilePlatform` 'android' | 'ios', required
          - `deviceArn` string: Encrypted device identifier token.
          - `deviceName` string
          - `userDeviceId` string, uuid: When set, this entry runs on the user's registered phone.
          - `localDeviceName` string: Display-name snapshot of the registered device, resolved when the config was saved.
          - `uploadArn` string: Encrypted upload identifier token.
          - `uploadType` 'own_app' | 'existing' | 'sample'
          - `appName` string
          - `bundleId` string
          - `apps` CaseAppInfo[]
            - `uploadArn` string, required: Encrypted upload identifier token.
            - `uploadType` 'own_app' | 'existing' | 'sample', required
            - `appName` string, nullable, required
            - `bundleId` string

## Other responses

- `400`: Invalid request body or case ID. Schema failures carry the zod error tree in `details`; an undecryptable token carries `details.path`; misplaced top-level fields carry `details.unknownFields` and `details.hint`; unknown hostname override ids carry `details.ids`; a non-desktop device bound as `apiDesktopConfig` carries `details.platform`; a multi-device local entry whose platform does not match carries `details.path`.
- `401`: Missing or invalid API key.
- `403`: The API key's user is not a member of the organization, or a config was set that needs a feature the organization does not have (`details.feature` is `byodEnabled` or `multiDeviceEnabled`). Clearing a config with `null` is never gated.
- `404`: Test case not found, or a referenced upload / user device does not belong to the organization (`details.path` names the slot).
- `429`: Rate limit exceeded — e.g. the daily session limit was reached for the organization, or too many API requests were sent in a short window (retry after the number of seconds in the `Retry-After` header).
- `500`: Unexpected server error.

---

[API](https://skmtc.dev/autify/apis/aximo-api.md) - [All operations](https://skmtc.dev/autify/apis/aximo-api/llms.txt) - [OpenAPI document](https://skmtc.dev/autify/apis/aximo-api/revisions/590722011284?raw)
