---
title: "Cancel an in-flight chat stream"
method: POST
path: "/conversations/{conversationId}/cancel"
tags: ["Conversations"]
---

# Cancel an in-flight chat stream

`POST /conversations/{conversationId}/cancel`

## Request

- Base URL: `https://app.pipeshub.com/api/v1`
- URL: `https://app.pipeshub.com/api/v1/conversations/{conversationId}/cancel`
- Auth: one of:
  - HTTP bearer
  - OAuth 2
    - Authorization code: `/api/v1/oauth2/authorize`
    - Client credentials
    - Token URL: `/api/v1/oauth2/token` scopes: `conversation:chat`

Cooperatively stop a `POST /conversations/stream` or
`POST /conversations/{conversationId}/messages/stream` run that is
still generating, using the `runId` sent when that stream started.

This is a synchronous JSON ack, not another SSE stream. The
cancelled run's own stream (if still connected) receives a terminal
frame with a `stopped` status and whatever partial answer had
already generated; nothing further is delivered here.

A `runId` that already finished or was never registered is not an
error either, but it is answered `{ cancelled: true }`, not `false`:
the query service's KV-backed run registry publishes the cancel for
other workers and acknowledges it as cancelled (a known defect).
`{ cancelled: false }` is only returned when that publish fails.

Only the conversation's owner (matched on `userId`) can cancel; any
other caller, or a missing, deleted or agent conversation, is `404`.

`runId` must belong to a run started on THIS `conversationId` — a
`runId` that exists but was registered under a different
conversation (even one owned by the same caller) is rejected with
`403`, same as a `runId` owned by a different user/org.

## Path parameters

- `conversationId` string, objectId, required

## Request body

- object
  - `runId` string, uuid, required: The `runId` sent when the stream was started.

## Response `200`

Cancellation requested, or the run had already finished.

- object
  - `cancelled` boolean, required

## Other responses

- `400`: `runId` missing or not a UUID, or `conversationId` not an ObjectId (`VALIDATION_ERROR`).
- `401`: Unauthorized
- `403`: The token is missing the `conversation:chat` scope (`Insufficient scope`), or `runId` is a running stream registered under a different user, org or conversation than this request (`You do not own this run`, relayed from the query service).
- `404`: Conversation not found or unauthorized
- `500`: Internal server error. A JSON body larger than 10 MB, or a form-encoded body larger than 100 KB or with more than 1000 fields, is answered `413` by the app-wide body parsers instead, also before authentication.

## Changes

- **2026-10-28** `dbb5e203b445`: 1 breaking, 7 info
  - added the pattern `^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$` to the request property `runId`
  - added the media type `application/x-www-form-urlencoded` to the request body
  - added the media type `application/json` for the response with the status `400`
  - added the media type `application/json` for the response with the status `401`
  - …4 more
- **2026-10-08** `bb0c59adf026`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/pipeshub-ai/apis/pipeshub-api/changes/conversations/:conversationId/cancel/post.md)

---

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