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.
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/{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
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: 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