openapi: 3.2.0 info: title: Keploy Public Test Runs 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 . **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: Test Runs paths: /apps/{appId}/test-runs: parameters: - $ref: '#/components/parameters/appId' get: operationId: listTestRuns x-required-scope: read summary: List test runs description: 'List test runs for an app. Optional `kind` query param filters by run kind: `rerecord` (record_sandbox_test runs), `sandbox_run` (replay_sandbox_test runs), or `test_suite_run` (replay_test_suite live runs). Omit to return runs of every kind. Requires scope: `read`.' tags: - Test Runs parameters: - $ref: '#/components/parameters/offset' - $ref: '#/components/parameters/limit' - name: kind in: query required: false schema: type: string enum: - rerecord - sandbox_run - test_suite_run description: Filter by run kind. Omit to return runs of every kind. responses: '200': description: Paginated test runs content: application/json: schema: $ref: '#/components/schemas/Envelope' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' /apps/{appId}/test-runs/{runId}: parameters: - $ref: '#/components/parameters/appId' - $ref: '#/components/parameters/runId' get: operationId: getTestRun x-required-scope: read summary: Get a test run description: 'Requires scope: `read`.' tags: - Test Runs responses: '200': description: Test run detail 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' /apps/{appId}/test-runs/{runId}/normalize: parameters: - $ref: '#/components/parameters/appId' - $ref: '#/components/parameters/runId' post: operationId: normalizeTestRun x-required-scope: write summary: Normalize a test run description: 'Requires scope: `write`.' tags: - Test Runs responses: '200': description: Normalization applied 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' /apps/{appId}/test-runs/{runId}/suite-reports: parameters: - $ref: '#/components/parameters/appId' - $ref: '#/components/parameters/runId' get: operationId: listSuiteReports x-required-scope: read summary: List suite reports for a test run description: 'Requires scope: `read`. Supports cursor-based pagination.' tags: - Test Runs parameters: - name: page_size in: query schema: type: integer description: Number of items per page - name: after in: query schema: type: string description: Cursor for forward pagination - name: before in: query schema: type: string description: Cursor for backward pagination responses: '200': description: Suite reports 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' /apps/{appId}/test-runs/{runId}/suite-reports/{reportId}: parameters: - $ref: '#/components/parameters/appId' - $ref: '#/components/parameters/runId' - $ref: '#/components/parameters/reportId' get: operationId: getSuiteReport x-required-scope: read summary: Get a suite report description: 'Requires scope: `read`.' tags: - Test Runs responses: '200': description: Suite report detail 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' /apps/{appId}/test-runs/{runId}/suite-reports/{reportId}/normalize: parameters: - $ref: '#/components/parameters/appId' - $ref: '#/components/parameters/runId' - $ref: '#/components/parameters/reportId' post: operationId: normalizeSuiteReport x-required-scope: write summary: Normalize a suite report description: 'Requires scope: `write`.' tags: - Test Runs responses: '200': description: Normalization applied 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' components: responses: RateLimited: description: Too many requests content: application/json: schema: $ref: '#/components/schemas/EnvelopeError' BadRequest: description: Validation error content: application/json: schema: $ref: '#/components/schemas/EnvelopeError' Unauthorized: description: Authentication required content: application/json: schema: $ref: '#/components/schemas/EnvelopeError' NotFound: description: Resource not found content: application/json: schema: $ref: '#/components/schemas/EnvelopeError' InternalError: description: Internal server error content: application/json: schema: $ref: '#/components/schemas/EnvelopeError' Forbidden: description: 'Insufficient scope or role (error code: INSUFFICIENT_SCOPE)' content: application/json: schema: $ref: '#/components/schemas/EnvelopeError' parameters: reportId: name: reportId in: path required: true schema: type: string limit: name: limit in: query description: 'Page size. Defaults to 20 when omitted; `limit=0` is also treated as "use default" (so existing clients sending an explicit zero keep the prior behaviour). Capped at 100 — the spec rejects higher values with 400 so callers fail loudly instead of being silently clamped, and the handler enforces the same cap as a defence-in-depth fallback. ' schema: type: integer default: 20 minimum: 0 maximum: 100 appId: name: appId in: path required: true schema: type: string offset: name: offset in: query description: 'Zero-based pagination offset. Negative values are rejected — the handler also clamps to 0 as a defence-in-depth fallback. ' schema: type: integer default: 0 minimum: 0 runId: name: runId in: path required: true schema: type: string schemas: Envelope: type: object properties: data: {} error: $ref: '#/components/schemas/APIError' meta: $ref: '#/components/schemas/Meta' Meta: type: object properties: request_id: type: string trace_id: type: string timestamp: type: string format: date-time pagination: $ref: '#/components/schemas/Pagination' Pagination: type: object properties: has_next_page: type: boolean has_previous_page: type: boolean next_cursor: type: - string - 'null' previous_cursor: type: - string - 'null' total_count: type: - integer - 'null' APIError: type: object properties: code: type: string message: type: string details: type: array items: $ref: '#/components/schemas/ErrorDetail' ErrorDetail: type: object properties: field: type: string message: type: string EnvelopeError: type: object properties: error: $ref: '#/components/schemas/APIError' meta: $ref: '#/components/schemas/Meta' securitySchemes: apiKeyAuth: type: apiKey in: header name: X-API-Key description: Personal Access Token (`kep_`-prefixed). Generate from Settings > API Keys.