---
id: cloud/features/smart-orchestration/run-cancellation-api
title: Cancel a run via API in Cypress Cloud
description: >-
  Cancel a Cypress Cloud run programmatically with the Run Cancellation API,
  using your project ID and record key, without needing the Cypress Cloud
  dashboard.
section: cloud
source_path: docs/cloud/features/smart-orchestration/run-cancellation-api.mdx
version: 55944c28ec019cc8496c1abe8ea6f77b3bea443b
updated_at: '2026-09-15T11:51:53.604Z'
---
# Run Cancellation API

This API is **experimental**. Breaking changes are unlikely, but may be made if necessary.

A run can be canceled manually by opening Cypress Cloud and clicking **Cancel** on the run, or configuring [Auto Cancellation](/llm/markdown/cloud/features/smart-orchestration/run-cancellation.md) to stop a run automatically once failures cross a threshold.

Neither helps when the decision to stop a run is made somewhere else entirely, for example:

*   A deployment pipeline that cancels in-flight test runs for a commit that has already been superseded by a newer push.
*   A separate CI job or orchestrator that determines the tests are no longer needed and frees up CI machines immediately.
*   An internal tool that lets a developer cancel a run without needing Cypress Cloud dashboard access.

In these cases, your CI process will cancel the jobs that are running Cypress, and the Cypress Cloud run will be left in an in-progress state, awaiting further communication from your CI, until your Project's configured [Run Timeout](/llm/markdown/cloud/account-management/projects.md#Run-Timeout) expires.

## Why use the Run Cancellation API

The Run Cancellation API lets you cancel a run with a single authenticated HTTP request, using the same [project ID](/llm/markdown/cloud/account-management/projects.md#Project-ID) and [record key](/llm/markdown/cloud/account-management/projects.md#Record-key) credentials you already use to [record the run](/llm/markdown/cloud/get-started/setup.md#Setup).

Canceling through the API has the same effect as canceling manually from the dashboard: the run is marked **Canceled**, [`run.completed` webhooks](/llm/markdown/cloud/integrations/webhooks.md) fire, and any in-progress `cypress run --record` calls for the run exit with an error. See [What happens when a run is canceled?](/llm/markdown/cloud/features/recorded-runs.md#What-happens-when-a-run-is-canceled) for the full list of effects.

This is especially useful when:

*   You want to use [Re-run optimization](/llm/markdown/cloud/features/smart-orchestration/rerun-optimization.md) to execute only the tests that did not pass in a previously canceled run.
*   You want to trigger [`run.completed` webhooks](/llm/markdown/cloud/integrations/webhooks.md) in a timely manner after your CI has stopped.
*   You want any other end-of-run integrations like [GitHub](/llm/markdown/cloud/integrations/github.md) or [Slack](/llm/markdown/cloud/integrations/slack.md) to happen at the moment of the CI cancellation, not after the Run Timeout expires.
*   You want Cypress [Cloud MCP](/llm/markdown/cloud/integrations/cloud-mcp.md) and [CLI](/llm/markdown/cloud/integrations/cloud-cli.md) to reflect the intentional canceled state immediately when working with results, instead of a partial "timing out" state.

**Cypress Run exit code in CI**

Cancellation of a run does not change its exit code. Any `cypress run --record` process that's still executing specs for that run exits **non-zero** (`1`), the same as it would if tests had actually failed.

Cypress prints `Exiting with non-zero exit code because the run was canceled.` to the console when this happens, but the exit code itself doesn't distinguish a cancellation from a real test failure.

**Note:** If you call this API from a step that runs separately from the test job itself, the **canceled run's own CI job** will report as failed rather than canceled. This may happen example in deployment-pipeline and orchestrator use cases. You should account for that in the job that ran the canceled specs, so a run you intentionally canceled doesn't get treated as a broken build.

## Endpoint

```
POST https://api.cypress.io/runs/cancel
```

Requests are authenticated with your **project ID** and **record key**, the same credentials used to [record the run](/llm/markdown/cloud/get-started/setup.md#Setup).

## Request body

The request body is JSON. `projectId` and `recordKey` are always required, and the run is identified by **exactly one** of `runUrl` or `runNumber`.

| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `projectId` | `string` | Yes | The [project ID](/llm/markdown/cloud/account-management/projects.md#Project-ID) of the run. |
| `recordKey` | `string` | Yes | A valid [record key](/llm/markdown/cloud/account-management/projects.md#Record-key) for the project. |
| `runUrl` | `string` | Conditional | The Cypress Cloud run URL (Cypress prints this when a run starts). Provide this **or** `runNumber`. |
| `runNumber` | `number` | Conditional | The run's sequential number within the project. Provide this **or** `runUrl`. |

### Example: `fetch`

```
const response = await fetch('https://api.cypress.io/runs/cancel', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    projectId: 'abc123',
    recordKey: 'f858a2bc-b469-4e48-be67-0876339ee7e1',
    runUrl: 'https://cloud.cypress.io/projects/abc123/runs/42',
    // runNumber: 42, // optional, in lieu of runUrl
  }),
})

const result = await response.json()
console.log(result) // e.g. { runId: 'f858a2bc-b469-4e48-be67-0876339ee7e1', cancelledAt: '2024-01-01T00:00:00.000Z' }
```

### Example: `curl`

```
curl -X POST https://api.cypress.io/runs/cancel \
  -H 'Content-Type: application/json' \
  -d '{
    "projectId": "abc123",
    "recordKey": "f858a2bc-b469-4e48-be67-0876339ee7e1",
    "runUrl": "https://cloud.cypress.io/projects/abc123/runs/42"
  }'
```

## Finding the run URL or run number

Use the [`before:run`](/llm/markdown/api/node-events/before-run-api.md) Node event to get the Cypress Cloud `runUrl`. This can be accessed using your cypress config file:

*   cypress.config.js
*   cypress.config.ts

```
const { defineConfig } = require('cypress')

module.exports = defineConfig({
  // setupNodeEvents can be defined in either
  // the e2e or component configuration
  e2e: {
    setupNodeEvents(on, config) {
      const { defineConfig } = require('cypress')

      module.exports = defineConfig({
        // setupNodeEvents can be defined in either
        // the e2e or component configuration
        e2e: {
          setupNodeEvents(on, config) {
            on('before:run', async (details) => {
              // `results` is undefined in `cypress open` mode, and `runUrl` is only
              // set when the run was recorded. Bail out otherwise.
              if (!details?.runUrl) {
                return
              }

              console.log(details.runUrl)

              // store the RUN URL where your CI process can access it later for cancellation purposes
            })
          },
        },
      })
    },
  },
})
```

```
import { defineConfig } from 'cypress'

export default defineConfig({
  // setupNodeEvents can be defined in either
  // the e2e or component configuration
  e2e: {
    setupNodeEvents(on, config) {
      const { defineConfig } = require('cypress')

      module.exports = defineConfig({
        // setupNodeEvents can be defined in either
        // the e2e or component configuration
        e2e: {
          setupNodeEvents(on, config) {
            on('before:run', async (details) => {
              // `results` is undefined in `cypress open` mode, and `runUrl` is only
              // set when the run was recorded. Bail out otherwise.
              if (!details?.runUrl) {
                return
              }

              console.log(details.runUrl)

              // store the RUN URL where your CI process can access it later for cancellation purposes
            })
          },
        },
      })
    },
  },
})
```

## Responses

A successful cancellation always responds immediately and will end a Cypress run in progress.

**Note**: If you are looking for a less-immediate signal that allows the queued tests to complete before ending the run, use the [Run Completion API](/llm/markdown/cloud/features/smart-orchestration/parallelization.md#Run-Completion-API) instead.

| Status | Body | Meaning |
| --- | --- | --- |
| `200` | `{ "runId": "...", "cancelledAt": "..." }` | The run was canceled. |
| `409` | `{ "message": "Run is not currently running and cannot be canceled.", "code": "RUN_NOT_RUNNING" }` | The run is not currently running. It may have already completed, already been canceled, or timed out. |

Malformed or unauthenticated requests are rejected before a cancellation is attempted: a missing or invalid [record key](/llm/markdown/cloud/account-management/projects.md#Record-key) returns `401`, a missing `projectId` or run identifier returns `400`, and a `runUrl` or `runNumber` that doesn't resolve to a run in the project returns `404`. Every error response is a JSON body of the form `{ "message": "...", "code": "..." }`.

## See also

*   [Auto Cancellation](/llm/markdown/cloud/features/smart-orchestration/run-cancellation.md) - cancel a run automatically once failures cross a threshold
*   [Manually canceling a run](/llm/markdown/cloud/features/recorded-runs.md#Run-cancellation) - cancel a run from the Cypress Cloud dashboard
*   [Run Completion API](/llm/markdown/cloud/features/smart-orchestration/parallelization.md#Run-Completion-API) - mark a run complete early instead of canceling it
*   [`run.completed` webhooks](/llm/markdown/cloud/integrations/webhooks.md) - get notified when a run finishes, including by cancellation
