Keploy Cloud API

REST API for Keploy Cloud (base https://api.keploy.io/client/v1) providing AI test-suite generation and execution, app/test-suite management, recordings/mocks, test and load-test reports, schema coverage, smart sets, branches, jobs, API keys, clusters, and account/subscription info. Authenticated via scoped kep_-prefixed API keys (X-API-Key or Bearer). A first-party OpenAPI 3.0.3 (55 paths, 71 operations) is served at https://api.keploy.io/client/v1/openapi.yaml.

Operations 71

GET /apps List apps #
POST /apps Create an app #
GET /apps/{appId} Get an app #
PUT /apps/{appId} Update an app #
DELETE /apps/{appId} Delete an app #
GET /apps/{appId}/schema-coverage Get schema coverage #
GET /clusters List clusters #
POST /clusters Create a cluster #
GET /apps/by-cluster/{clusterId} List apps in a cluster #
GET /apps/with-recordings List proxy apps with network recordings #
GET /apps/{appId}/recordings List recording sessions #
GET /apps/{appId}/recordings/{testSetId} Get recorded test cases #
GET /apps/{appId}/generated-schema Get auto-generated OpenAPI schema #
GET /apps/{appId}/recordings/{testSetId}/mocks List mocks for a recording #
POST /apps/{appId}/recordings/{testSetId}/mocks Author one mock under a recording #
GET /apps/{appId}/recordings/{testSetId}/mocks/{mockId} Read one mock's canonical YAML #
PUT /apps/{appId}/recordings/{testSetId}/mocks/{mockId} Replace one mock's stored data #
DELETE /apps/{appId}/recordings/{testSetId}/mocks/{mockId} Drop one mock #
GET /apps/{appId}/recordings/{testSetId}/test-cases/{testCaseId}/mock-mapping Read the mocks currently linked to a test case #
POST /apps/{appId}/recordings/{testSetId}/test-cases/{testCaseId}/mock-mapping Link or unlink a mock from a test case #
POST /apps/{appId}/recordings/bundle Atomic test set + cases + mocks + mappings ingest #
GET /apps/{appId}/recordings/{testSetId}/test-cases/{testCaseId} Get a single test case #
PUT /apps/{appId}/recordings/{testSetId}/test-cases/{testCaseId} Update a test case #
GET /apps/{appId}/recordings/{testSetId}/export Export a recording bundle #
POST /apps/{appId}/recordings/{testSetId}/import Import test case changes into a recording #
GET /apps/{appId}/test-reports List test run reports #
GET /apps/{appId}/test-reports/{reportId} Get a test run report #
GET /apps/{appId}/test-reports/{reportId}/full Get a fully-inflated test run report #
GET /apps/{appId}/test-reports/{reportId}/test-set-reports List test set reports within a run #
GET /apps/{appId}/test-reports/{reportId}/test-set-reports/{testSetReportId}/test-cases List test case reports #
GET /apps/{appId}/test-suites List test suites #
POST /apps/{appId}/test-suites Create a test suite #
POST /apps/{appId}/test-suites/generate Generate test suites via AI #
POST /apps/{appId}/test-suites/run Run test suites #
POST /apps/{appId}/test-suites/bulk-delete Bulk-delete test suites #
GET /apps/{appId}/test-suites/{suiteId} Get a test suite #
PUT /apps/{appId}/test-suites/{suiteId} Update a test suite #
DELETE /apps/{appId}/test-suites/{suiteId} Delete a test suite #
POST /apps/{appId}/test-suites/{suiteId}/validate Validate a test suite #
GET /apps/{appId}/test-runs List test runs #
GET /apps/{appId}/test-runs/{runId} Get a test run #
POST /apps/{appId}/test-runs/{runId}/normalize Normalize a test run #
GET /apps/{appId}/test-runs/{runId}/suite-reports List suite reports for a test run #
GET /apps/{appId}/test-runs/{runId}/suite-reports/{reportId} Get a suite report #
POST /apps/{appId}/test-runs/{runId}/suite-reports/{reportId}/normalize Normalize a suite report #
POST /apps/{appId}/load-tests Start a load test #
GET /apps/{appId}/load-tests List load test runs #
GET /apps/{appId}/load-tests/{runId} Get a load test report #
POST /apps/{appId}/load-tests/{runId}/stop Stop a running load test #
GET /apps/{appId}/load-tests/{runId}/events Stream load test events (SSE) #
GET /apps/{appId}/generation-history List generation history #
GET /apps/{appId}/generation-history/{jobId} Get generation history details #
GET /apps/{appId}/branches List Keploy branches on an app #
POST /apps/{appId}/branches/ci Find-or-create a CI branch #
GET /jobs List jobs #
GET /jobs/{jobId} Get a job #
POST /jobs/{jobId}/stop Stop a running job #
GET /jobs/{jobId}/events Stream job events (SSE) #
GET /jobs/{jobId}/validation-result Get job validation result #
GET /company/subscription Get company subscription #
GET /company/usage Get company usage #
GET /users/me Get current user #
POST /api-keys Create an API key #
GET /api-keys List API keys #
DELETE /api-keys/{keyId} Revoke an API key #
GET /apps/{appId}/smart-set/cases List smart test cases #
PATCH /apps/{appId}/smart-set/cases/{name} Edit a smart test case (branch-only) #
DELETE /apps/{appId}/smart-set/cases/{name} Delete a smart test case (branch-only) #
POST /apps/{appId}/smart-set/cases/{name}/obsolete Mark a smart test case obsolete / restore it (branch-only) #
PUT /apps/{appId}/smart-set/mocks/{name} Create or replace a smart-set mock's content (branch-only) #
DELETE /apps/{appId}/smart-set/mocks/{name} Delete a smart-set mock (branch-only) #

Documentation

Specifications

Other Resources

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/keploy-cloud-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

keploy-cloud-api-openapi.yaml Raw ↑
openapi: 3.0.3
info:
  title: Keploy Public API
  version: 1.0.0
  description: |
    Programmatic access to the Keploy platform for CI/CD pipelines, scripts,
    and AI agents. See the full guide at
    <https://keploy.io/docs/running-keploy/public-api/>.

    **Scopes and 403 errors:** Every endpoint requires a minimum scope (`read`,
    `write`, or `admin`). If the API key lacks the required scope the server
    returns `403 Forbidden` with error code `INSUFFICIENT_SCOPE`.
  contact:
    email: support@keploy.io
  termsOfService: https://keploy.io/terms

servers:
  - url: https://api.keploy.io/client/v1
    description: Production
  - url: https://api.staging.keploy.io/client/v1
    description: Staging

security:
  - apiKeyAuth: []

tags:
  - name: Apps
  - name: Clusters
  - name: Recordings
  - name: Test Reports
  - name: Test Suites
  - name: Test Runs
  - name: Jobs
  - name: Load Tests
  - name: Generation History
  - name: Company
  - name: Users
  - name: API Keys

paths:
  # ── Apps ──────────────────────────────────────────────────────────────
  /apps:
    get:
      operationId: listApps
      x-required-scope: read
      summary: List apps
      description: "Returns the tenant's apps. Use the optional `q` query parameter to name-filter (case-insensitive substring, e.g. `?q=orderflow` → apps whose name contains 'orderflow'); without it the full paginated list is returned. Callers that know the app's folder / repo name should pass it as `q` to avoid paginating through hundreds of apps. Requires scope: `read`."
      tags: [Apps]
      parameters:
        - $ref: "#/components/parameters/offset"
        - $ref: "#/components/parameters/limit"
        - in: query
          name: q
          description: "Case-insensitive substring to filter app names by. Omit to list all apps."
          required: false
          schema:
            type: string
      responses:
        "200":
          description: App list
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EnvelopeAppList"
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "500": { $ref: "#/components/responses/InternalError" }
    post:
      operationId: createApp
      x-required-scope: write
      summary: Create an app
      description: "Requires scope: `write`."
      tags: [Apps]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateAppRequest"
      responses:
        "201":
          description: App created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EnvelopeApp"
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "409":
          description: App already exists
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EnvelopeError"
        "429": { $ref: "#/components/responses/RateLimited" }
        "413": { $ref: "#/components/responses/PayloadTooLarge" }
        "500": { $ref: "#/components/responses/InternalError" }

  /apps/{appId}:
    parameters:
      - $ref: "#/components/parameters/appId"
    get:
      operationId: getApp
      x-required-scope: read
      summary: Get an app
      description: |
        Requires scope: `read`.

        Optional `fields` query parameter projects the response to a subset
        of properties — useful for MCP / AI callers that only need a few
        identity fields (e.g. `["name","namespace","deployment","origin.clusterName"]`)
        and don't want the full ~16k-token embedded schema in their context.
        Supports dotted paths for nested objects. Omitting `fields` returns
        the full envelope as before.
      tags: [Apps]
      parameters:
        - name: fields
          in: query
          required: false
          description: |
            Optional comma-separated list of response field paths to keep.
            Each path is dotted (e.g. `origin.clusterName`). When set, the
            response is projected to just those paths inside `data`; the
            envelope shape (`{data, meta?}`) is preserved.
          schema:
            type: array
            items: { type: string }
          style: form
          explode: false
      responses:
        "200":
          description: App detail
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EnvelopeApp"
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "500": { $ref: "#/components/responses/InternalError" }
    put:
      operationId: updateApp
      x-required-scope: write
      summary: Update an app
      description: "Requires scope: `write`."
      tags: [Apps]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateAppRequest"
      responses:
        "200":
          description: App updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EnvelopeApp"
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "413": { $ref: "#/components/responses/PayloadTooLarge" }
        "500": { $ref: "#/components/responses/InternalError" }
    delete:
      operationId: deleteApp
      x-required-scope: admin
      summary: Delete an app
      description: "Requires scope: `admin`."
      tags: [Apps]
      responses:
        "200":
          description: App deleted
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EnvelopeDeleted"
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "500": { $ref: "#/components/responses/InternalError" }

  # ── Schema Coverage ───────────────────────────────────────────────────
  /apps/{appId}/schema-coverage:
    parameters:
      - $ref: "#/components/parameters/appId"
    get:
      operationId: getSchemaCoverage
      x-required-scope: read
      summary: Get schema coverage
      description: "Requires scope: `read`."
      tags: [Apps]
      responses:
        "200":
          description: Schema coverage data
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Envelope"
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "500": { $ref: "#/components/responses/InternalError" }

  # ── Clusters ─────────────────────────────────────────────────────────
  /clusters:
    get:
      operationId: listClusters
      summary: List clusters
      description: "Returns all clusters for the authenticated company. Requires scope: `read`."
      x-required-scope: read
      tags: [Clusters]
      responses:
        "200":
          description: Cluster list
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EnvelopeClusterList"
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "500": { $ref: "#/components/responses/InternalError" }
    post:
      operationId: createCluster
      summary: Create a cluster
      description: "Provisions a new cluster in the authenticated company and returns its access key. The access key is shown only once. Requires scope: `admin`."
      x-required-scope: admin
      tags: [Clusters]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateClusterRequest"
      responses:
        "201":
          description: Cluster created (includes access key, shown only once)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EnvelopeClusterCreated"
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "409": { $ref: "#/components/responses/Conflict" }
        "413": { $ref: "#/components/responses/PayloadTooLarge" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "500": { $ref: "#/components/responses/InternalError" }

  /apps/by-cluster/{clusterId}:
    parameters:
      - $ref: "#/components/parameters/clusterId"
    get:
      operationId: listAppsByCluster
      summary: List apps in a cluster
      description: "Returns apps belonging to a specific cluster. More efficient than iterating all apps. Requires scope: `read`."
      x-required-scope: read
      tags: [Clusters]
      responses:
        "200":
          description: Apps in cluster
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EnvelopeAppByClusterList"
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "500": { $ref: "#/components/responses/InternalError" }

  # ── Recordings (integration test recordings from k8s-proxy) ──────────
  /apps/with-recordings:
    get:
      operationId: listAppsWithRecordings
      summary: List proxy apps with network recordings
      description: "Returns all k8s-proxy apps (origin.type=PROXY). These apps are auto-created by the Keploy k8s-proxy agent on first recording and contain network recordings of ingress HTTP traffic (as Keploy test cases) and egress dependency calls — database queries, external API calls, message queues — captured as Keploy mocks. Use listRecordings and getRecording to access the recorded request/response pairs and dependency mocks from live environments. Requires scope: `read`."
      x-required-scope: read
      tags: [Recordings]
      responses:
        "200":
          description: Apps with recordings
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EnvelopeAppWithRecordingsList"
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "500": { $ref: "#/components/responses/InternalError" }

  /apps/{appId}/recordings:
    parameters:
      - $ref: "#/components/parameters/appId"
    get:
      operationId: listRecordings
      summary: List recording sessions
      description: "Returns test sets (recording sessions) for an app. Requires scope: `read`."
      x-required-scope: read
      tags: [Recordings]
      parameters:
        - $ref: "#/components/parameters/offset"
        - $ref: "#/components/parameters/limit"
      responses:
        "200":
          description: Recording sessions
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EnvelopeTestSetList"
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "500": { $ref: "#/components/responses/InternalError" }

  /apps/{appId}/recordings/{testSetId}:
    parameters:
      - $ref: "#/components/parameters/appId"
      - $ref: "#/components/parameters/testSetId"
    get:
      operationId: getRecording
      summary: Get recorded test cases
      description: "Returns individual recorded test cases within a test set, including HTTP request/response data. Requires scope: `read`."
      x-required-scope: read
      tags: [Recordings]
      parameters:
        - $ref: "#/components/parameters/offset"
        - $ref: "#/components/parameters/limit"
      responses:
        "200":
          description: Recorded test cases
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EnvelopeTestCaseList"
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "500": { $ref: "#/components/responses/InternalError" }

  /apps/{appId}/generated-schema:
    parameters:
      - $ref: "#/components/parameters/appId"
    get:
      operationId: getGeneratedSchema
      summary: Get auto-generated OpenAPI schema
      description: "Returns the OpenAPI schema auto-generated from recorded traffic. Requires scope: `read`."
      x-required-scope: read
      tags: [Recordings]
      responses:
        "200":
          description: Generated schema
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EnvelopeGeneratedSchema"
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "500": { $ref: "#/components/responses/InternalError" }

  /apps/{appId}/recordings/{testSetId}/mocks:
    parameters:
      - $ref: "#/components/parameters/appId"
      - $ref: "#/components/parameters/testSetId"
    get:
      operationId: listMocks
      summary: List mocks for a recording
      description: "Returns mock reference metadata and optionally parsed mock specs for a test set. Use ?include_specs=true to download and parse the actual mock YAML from object storage. Pass `branch_id` to scope the read to a branch overlay (bundle-uploaded recordings on a branch are invisible to main reads). Requires scope: `read`."
      x-required-scope: read
      tags: [Recordings]
      parameters:
        - name: include_specs
          in: query
          schema:
            type: boolean
            default: false
          description: "When true, download and parse the actual mock YAML specs from object storage."
        - $ref: "#/components/parameters/branchId"
      responses:
        "200":
          description: Mock reference metadata and optionally parsed specs
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EnvelopeMockList"
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "500": { $ref: "#/components/responses/InternalError" }
    post:
      operationId: createMock
      summary: Author one mock under a recording
      description: |
        Insert a single mock into the given test set. When `branch_id` is
        supplied, the mock lands on that branch's overlay
        (`branch_sandbox_ops`) and only surfaces to main on merge.
        Without `branch_id` the mock writes straight to main — same
        behaviour as the recording-driven agent path.

        Authoring shape — pick ONE:

        - **`mock_yaml`** (PREFERRED) — paste the canonical mock YAML
          envelope (`version` / `kind` / `name` / `spec` with the
          per-kind payload, exactly as it lives in `mocks.yaml` on
          disk). The server decodes via OSS DecodeMocks so kind-
          specific Spec contents (`req`, `resp`, `metadata`, …)
          round-trip without field-name loss. This is the only path
          that preserves payloads pasted from existing mocks.
        - **`mock`** — typed OSS Mock JSON object. Brittle: the OSS
          struct uses PascalCase JSON tags (`Metadata`, `Req`, `Res`),
          so lowercase canonical keys are silently dropped. Use only
          when authoring programmatically from typed Go shapes.

        When both are sent, `mock_yaml` wins.

        Requires scope: `write`.
      x-required-scope: write
      tags: [Recordings]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              # mock is no longer required at the schema level — mock_yaml
              # is the preferred path and either one suffices. The service
              # rejects requests that supply neither.
              properties:
                mock:
                  type: object
                  description: OSS Mock — see schema in keploy.io/server/v3 pkg/models/mock.go. Use only when authoring from typed Go shapes. Lowercase YAML keys are dropped; prefer mock_yaml.
                mock_yaml:
                  type: string
                  description: Canonical single-doc mock YAML (version/kind/name/spec). Preferred over `mock`. Round-trips kind-specific contents losslessly.
                branch_id:
                  type: string
                  description: Optional branch overlay id. Absent → write lands on main.
      responses:
        "200":
          description: Inserted mock
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EnvelopeMockSingle"
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "500": { $ref: "#/components/responses/InternalError" }

  /apps/{appId}/recordings/{testSetId}/mocks/{mockId}:
    parameters:
      - $ref: "#/components/parameters/appId"
      - $ref: "#/components/parameters/testSetId"
      - name: mockId
        in: path
        required: true
        schema: { type: string }
        description: Mock id (UUID assigned at insert) OR human-readable mock name. The server resolves names within the test set, so the UI can pass either form.
    get:
      operationId: getMock
      summary: Read one mock's canonical YAML
      description: |
        Returns the canonical mock YAML doc (version/kind/name/spec)
        for the named mock in the given test set. Branch-aware: when
        `branch_id` is supplied, a branch-only upsert or tombstone
        takes precedence over main.

        Authoring workflow for AI agents: call this BEFORE updateMock
        to fetch the existing payload, edit fields locally, then
        round-trip the result through `mock_yaml` on updateMock.

        Requires scope: `read`.
      x-required-scope: read
      tags: [Recordings]
      parameters:
        - name: branch_id
          in: query
          schema: { type: string }
          description: Optional branch overlay id. Absent → reads from main.
      responses:
        "200":
          description: Canonical mock YAML
          content:
            application/json:
              schema:
                type: object
                required: [success, metadata, mockName]
                properties:
                  success: { type: boolean }
                  metadata:
                    type: string
                    description: Canonical single-doc mock YAML.
                  mockName:
                    type: string
                    description: Resolved mock name (mirrors the path param when caller passed an id).
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "500": { $ref: "#/components/responses/InternalError" }
    put:
      operationId: updateMock
      summary: Replace one mock's stored data
      description: |
        Updates the mock identified by `{mockId}` in the path. The body
        carries the full replacement. Two shapes — pick ONE:

        - **`mock_yaml`** (PREFERRED) — canonical mock YAML envelope.
          See createMock for the field-loss rationale.
        - **`mock`** — typed OSS Mock JSON. Brittle for lowercase keys.

        When both are sent, `mock_yaml` wins.

        Branch-aware via the optional `branch_id` body field — same
        semantics as createMock.

        Accepts both the mock's UUID `_id` and its human-readable Name
        as `mockId` — the server resolves names within the test set, so
        UI callers (which don't have access to the mock's `_id`) can
        pass the Name directly.

        Requires scope: `write`.
      x-required-scope: write
      tags: [Recordings]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                mock:
                  type: object
                  description: OSS Mock — typed full replacement payload. Prefer mock_yaml.
                mock_yaml:
                  type: string
                  description: Canonical single-doc mock YAML. Preferred path.
                branch_id:
                  type: string
      responses:
        "200":
          description: Updated mock
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EnvelopeMockSingle"
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "500": { $ref: "#/components/responses/InternalError" }
    delete:
      operationId: deleteMock
      summary: Drop one mock
      description: |
        Idempotent — returns 200 even if the mock is already gone. Path
        `{mockId}` accepts both the UUID `_id` and the human-readable
        Name (resolved within the test set).

        Branch-aware via optional `branch_id` query param.

        Requires scope: `write`.
      x-required-scope: write
      tags: [Recordings]
      parameters:
        - name: branch_id
          in: query
          schema: { type: string }
          description: |
            Optional branch overlay id. When set, the delete writes a
            tombstone op onto the branch's overlay (main untouched
            until the branch merges). When absent, the delete applies
            to main directly — no tombstone is involved.
      responses:
        "200":
          description: Deleted (or already absent)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EnvelopeSuccess"
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "500": { $ref: "#/components/responses/InternalError" }

  /apps/{appId}/recordings/{testSetId}/test-cases/{testCaseId}/mock-mapping:
    parameters:
      - $ref: "#/components/parameters/appId"
      - $ref: "#/components/parameters/testSetId"
      - name: testCaseId
        in: path
        required: true
        schema: { type: string }
        description: Test case name (the mapping doc keys cases by name, not _id).
    get:
      operationId: getMockMapping
      summary: Read the mocks currently linked to a test case
      description: |
        Returns the mock entries in the mapping doc for the named
        test case. Branch-aware via `branch_id` — when set, the
        branch overlay's mapping wins over main's.

        Workflow: AI agents should call this BEFORE editMockMapping
        to inspect what's linked, then issue targeted add / remove
        ops with confidence. Empty result (mocks: []) is normal —
        means no mocks linked yet.

        Requires scope: `read`.
      x-required-scope: read
      tags: [Recordings]
      parameters:
        - name: branch_id
          in: query
          schema: { type: string }
          description: Optional branch overlay id. Absent → reads from main.
      responses:
        "200":
          description: Linked mock entries for this test case
          content:
            application/json:
              schema:
                type: object
                required: [success, mocks]
                properties:
                  success: { type: boolean }
                  mocks:
                    type: array
                    description: Mock entries — empty array when nothing is linked.
                    items:
                      type: object
                      properties:
                        name: { type: string }
                        kind: { type: string }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "500": { $ref: "#/components/responses/InternalError" }
    post:
      operationId: editMockMapping
      summary: Link or unlink a mock from a test case
      description: |
        Targeted mutation of the test case's entry in the mapping doc.
        Add appends a mock entry if not already present; remove drops
        the entry by name. Both idempotent — safe to retry on a network
        blip.

        The MCP layer exposes this as TWO tools (`link_mock` /
        `unlink_mock`) — they both call this endpoint with the
        appropriate `action`. Splitting at the MCP layer keeps each
        tool's description tighter and avoids the LLM having to
        remember the enum spelling.

        Requires scope: `write`.
      x-required-scope: write
      tags: [Recordings]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [action, mock_name]
              properties:
                action:
                  type: string
                  enum: [add, remove]
                mock_name:
                  type: string
                mock_kind:
                  type: string
                  description: Optional. Stamped on the mapping entry; useful when callers want the kind preserved on the mapping doc for downstream readers.
                branch_id:
                  type: string
      responses:
        "200":
          description: Updated mapping
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EnvelopeMappingEdit"
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "500": { $ref: "#/components/responses/InternalError" }

  /apps/{appId}/recordings/bundle:
    parameters:
      - $ref: "#/components/parameters/appId"
    post:
      operationId: uploadRecordingBundle
      summary: Atomic test set + cases + mocks + mappings ingest
      description: |
        Bundle ingest — creates the test set, every test case, every
        mock, and the mapping doc in a single call. Each step is its
        own DB write; partial failure leaves earlier rows in place,
        callers can replay safely.

        Branch-aware via optional `branch_id` — when set, every row
        lands on the overlay until merge.

        Use this when authoring a recording from scratch (LLM
        workflows, CLI imports). For incremental edits, prefer the
        per-resource endpoints (`createMock`, `createTestCase`, etc.).

        Requires scope: `write`.
      x-required-scope: write
      tags: [Recordings]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                  description: Test set name (unique within the app/branch).
                image_name:
                  type: string
                image_tag:
                  type: string
                branch_id:
                  type: string
                smart_set:
                  type: boolean
                  description: >
                    Route the bundle into the smart test set as a branch import
                    (new schema_refs only) instead of creating a legacy test
                    set. Requires branch_id. The CLI sets this only when
                    --smartTestSet is passed and the app has EnableSmartTestSet.
                test_cases:
                  type: array
                  items:
                    type: object
                    required: [name, http_req, http_resp]
                    properties:
                      name: { type: string }
                      http_req: { type: object }
                      http_resp: { type: object }
                      noise:
                        type: object
                        additionalProperties:
                          type: array
                          items: { type: string }
                      mock_names:
                        type: array
                        items: { type: string }
                mocks:
                  type: array
                  description: |
                    Per-mock authoring entries. For each entry, prefer
                    `mock_yaml` (canonical envelope as it lives in
                    mocks.yaml on disk) over `spec`. The typed `spec`
                    object hits the same lowercase-key-drop bug
                    documented on createMock when the input came from
                    on-disk YAML. When both are set on an entry,
                    `mock_yaml` wins.
                  items:
                    type: object
                    # name + kind remain authoritative for the index even
                    # if `mock_yaml` carries its own — the server stamps
                    # these onto the canonical doc post-decode so the
                    # bundle entry is unambiguous.
                    required: [name, kind]
                    properties:
                      name: { type: string }
                      kind: { type: string }
                      spec:
                        type: object
                        description: Typed OSS Mock spec. Prefer mock_yaml.
                      mock_yaml:
                        type: string
                        description: Canonical single-doc mock YAML for this entry.
      responses:
        "200":
          description: Bundle ingest result
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EnvelopeBundleUpload"
        "400": { $ref: "#

# --- truncated at 32 KB (144 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/keploy/refs/heads/main/openapi/keploy-cloud-api-openapi.yaml