# Cancel stream

Cancels an in-progress streaming response with intelligent behavior based on timing.

Stops a streaming response that is currently being generated. The cancellation behavior depends on how long the stream has been running:

- **Quick Edit Mode** (< 5 seconds): Both user message and assistant response are deleted. Frontend should restore the message to input.
- **Keep Partial Mode** (≥ 5 seconds): User message remains, partial response is saved with `cancelled: true` metadata.


#### Path Parameters

**`thread_id`** string required

The thread ID containing the active stream to cancel.

## Cancellation Modes

### Quick Edit Mode (< 5 seconds)

When cancelled within 5 seconds of starting:

- Both user message and assistant response are deleted from the thread
- Frontend should restore the user's message to the input field
- Attachments should be restored
- Chat appears as if the message was never sent


**Use case**: User realizes they made a typo or want to rephrase immediately.

### Keep Partial Mode (≥ 5 seconds)

When cancelled after 5 seconds:

- User message remains in the thread
- Partial assistant response is saved with `cancelled: true` metadata
- Response stays visible in the chat
- No message restoration to input


**Use case**: User got enough information or wants to stop a long response.

## SSE Events

When an in-progress stream is cancelled via this endpoint, the streaming connection emits a single `response.cancelled` event and then the terminal `data: [DONE]` line. The normal `response.completed` event is **not** emitted on a cancelled turn.


```
data: {"event":"response.cancelled","status":"cancelled","thread_id":"thrd_abc123","partial_content_length":1234}

data: [DONE]
```

The `response.cancelled` payload contains:

| Field | Type | Description |
|  --- | --- | --- |
| `event` | string | Always `response.cancelled` |
| `status` | string | Always `cancelled` |
| `thread_id` | string | The thread whose stream was cancelled |
| `partial_content_length` | integer | Number of characters generated before cancellation |


> Note: this event is emitted only for an explicit cancellation through this endpoint. If the client simply closes the SSE connection (disconnect), no `response.cancelled` event is sent — the stream just stops — but partial usage is still billed.


## Notes

- **Idempotent**: Can be called multiple times safely after first call
- **Usage Tracking**: Tokens generated before cancellation are still tracked for billing
- **Tool Calls**: Current tool execution completes, but no further tools are called
- **All Output Modes**: Works with `text`, `plain`, and `structured` output modes


## Returns

Returns a JSON object with the following fields:

**`success`** boolean

Whether the cancellation was successful.

**`cancel_mode`** string

The cancellation mode applied: `quick_edit` or `keep_partial`.

**`elapsed_seconds`** number

How long the stream was running before cancellation.

**`user_message_deleted`** boolean

Whether the user's message was deleted (true in quick_edit mode).

**`assistant_response_deleted`** boolean

Whether the assistant's response was deleted (true in quick_edit mode).

**`partial_content_saved`** boolean

Whether partial content was preserved (true in keep_partial mode).

Cancel Stream

```bash
curl -X POST https://api.aitronos.com/v1/model/response/thrd_abc123/cancel \
  -H "X-API-Key: $FREDDY_API_KEY"
```


```python Python SDK
from aitronos import Aitronos

client = Aitronos(api_key="your-api-key")

result = client.responses.cancel_stream(thread_id="thrd_abc123")
print(f"Mode: {result.cancel_mode}")
print(f"Elapsed: {result.elapsed_seconds}s")
```


```python
import os
import requests

api_key = os.environ["FREDDY_API_KEY"]
thread_id = "thrd_abc123"

response = requests.post(
    f"https://api.aitronos.com/v1/model/response/{thread_id}/cancel",
    headers={"X-API-Key": api_key}
)

result = response.json()
print(f"Mode: {result['cancel_mode']}")
print(f"Elapsed: {result['elapsed_seconds']}s")
```


```javascript
const apiKey = process.env.FREDDY_API_KEY;
const threadId = 'thrd_abc123';

const response = await fetch(
  `https://api.aitronos.com/v1/model/response/${threadId}/cancel`,
  {
    method: 'POST',
    headers: {
      'X-API-Key': apiKey
    }
  }
);

const result = await response.json();
console.log(`Mode: ${result.cancel_mode}`);
console.log(`Elapsed: ${result.elapsed_seconds}s`);
```

Frontend Integration

```javascript
// Cancel button implementation
async function cancelStream(threadId, userMessage) {
  // Close SSE connection immediately for instant UI feedback
  eventSource.close();

  // Call cancel endpoint
  const response = await fetch(
    `https://api.aitronos.com/v1/model/response/${threadId}/cancel`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${token}`
      }
    }
  );

  const result = await response.json();

  // Handle based on cancel mode
  if (result.cancel_mode === 'quick_edit') {
    // Restore message to input
    restoreMessageToInput(userMessage);
    // Remove both messages from chat UI
    removeMessagesFromChat(threadId);
  } else {
    // Keep partial response visible
    markResponseAsCancelled(threadId);
  }
}

// Listen for cancellation event
eventSource.addEventListener('message', (event) => {
  const data = JSON.parse(event.data);

  if (data.event === 'response.cancelled') {
    console.log('Stream cancelled:', data);
    eventSource.close();
  }
});
```

**Response:**

Quick Edit Mode
Response when cancelled within 5 seconds:


```json
{
  "success": true,
  "cancel_mode": "quick_edit",
  "elapsed_seconds": 2.3,
  "user_message_deleted": true,
  "assistant_response_deleted": true,
  "partial_content_saved": false
}
```

Keep Partial Mode
Response when cancelled after 5 seconds:


```json
{
  "success": true,
  "cancel_mode": "keep_partial",
  "elapsed_seconds": 8.7,
  "user_message_deleted": false,
  "assistant_response_deleted": false,
  "partial_content_saved": true
}
```

SSE Cancelled Event
Server-Sent Events emitted on the streaming connection when the stream is cancelled (followed by the terminal `[DONE]` line):


```
data: {"event":"response.cancelled","status":"cancelled","thread_id":"thrd_abc123","partial_content_length":1234}

data: [DONE]
```

404 No Active Stream

```json
{
  "success": false,
  "error": {
    "code": "RESOURCE_NOT_FOUND",
    "message": "No active stream found for this thread",
    "system_message": "No active stream found for this thread",
    "type": "client_error",
    "status": 404,
    "details": {},
    "trace_id": "req_abc123xyz",
    "timestamp": "2025-12-22T15:30:00Z"
  }
}
```

403 Unauthorized

```json
{
  "success": false,
  "error": {
    "code": "INSUFFICIENT_PERMISSIONS",
    "message": "You don't have permission to cancel this stream",
    "system_message": "You don't have permission to cancel this stream",
    "type": "authorization_error",
    "status": 403,
    "details": {},
    "trace_id": "req_abc123xyz",
    "timestamp": "2025-12-22T15:30:00Z"
  }
}
```

401 Unauthorized

```json
{
  "success": false,
  "error": {
    "code": "AUTHENTICATION_REQUIRED",
    "message": "Invalid or missing API key",
    "system_message": "Invalid or missing API key",
    "type": "authentication_error",
    "status": 401,
    "details": {},
    "trace_id": "req_abc123xyz",
    "timestamp": "2025-12-22T15:30:00Z"
  }
}
```

## Related Resources

- [Create Response](/docs/api-reference/responses/create)
- [Threads Overview](/docs/documentation/core-concepts/threads-overview)