Platform docs
Auth, users, webhooks & more

Deleting Channels

You can delete or truncate a channel to remove its contents. To remove only messages while preserving the channel, see Truncate Channel.

Deleting a Channel

You can delete a single Channel using the  delete  method. This marks the channel as deleted and hides all the messages.

# Get a channel
channel = client.chat.channel("messaging", "general")

channel.delete()
Info:

If you recreate this channel, it will show up empty. Recovering old messages is not supported, unless the channel was deleted with skip_truncate (see Preserving message history below). Use the disable method if you want a fully reversible change.

Preserving Message History

By default, deleting a channel hides its messages, so a channel later recreated with the same ID starts out empty. Set skip_truncate to true to preserve message history through the delete instead, so a channel later recreated with the same ID (for example with GetOrCreateChannel) restores the full message history.

This is useful for apps where the same pair of users can end up back in the same channel, for example a dating app where two users unmatch and later re-match.

Warning:

skip_truncate is server-side only and only supported on distinct channels. It cannot be combined with hard_delete, and it cannot be used if the channel's messages were already hidden by an earlier delete or truncate.

channel = client.chat.channel("messaging", "general")

# Preserve history through the soft delete
channel.delete(skip_truncate=True)

Deleting Many Channels

You can delete up to 100 channels and optionally all of their messages using this method. This can be a large amount of data to delete, so this endpoint processes asynchronously, meaning responses contain a task ID which can be polled using the getTask endpoint to check status of the deletions. Channels will be soft-deleted immediately so that channels no longer return from queries, but permanently deleting the channel and deleting messages takes longer to process.

By default, messages are soft deleted, which means they are removed from client but are still available via server-side export functions. You can also hard delete messages, which deletes them from everywhere, by setting "hard_delete": true in the request. Messages that have been soft or hard deleted cannot be recovered.

You can also set skip_truncate to true to preserve message history for every channel in the request, the same as for a single channel delete. If any of the given CIDs is not eligible (it isn't a distinct channel, or its messages were already hidden by an earlier delete or truncate), the entire request is rejected and none of the channels are deleted.

This is currently supported on the following SDK versions (or higher):

  • Javascript 4.3.0, Python 3.14.0, Ruby 2.12.0, PHP 2.6.0, Go 3.13.0, Java 1.4.0, Unity 2.0.0 and .NET 0.22.0
# soft deletion
response = client.chat.delete_channels(cids=[cid1, cid2])
# hard deletion
response = client.chat.delete_channels(cids=[cid1, cid2], hard_delete=True)

task_response = client.get_task(id=response.data.task_id)
if task_response.data.status == "completed":
    # success!
    pass

# preserve history (distinct channels only)
response = client.chat.delete_channels(cids=[cid1, cid2], skip_truncate=True)

The  deleteChannels  response contain a taskID which can be polled using the getTask endpoint to check the status of the deletions.

Finding and purging soft-deleted channels

A soft-deleted channel no longer appears in query channels results, but its channel record and messages are kept until the channel is hard deleted. To erase that data, for example to handle a GDPR erasure request, list the soft-deleted channels with queryDeletedChannels, then pass their CIDs to delete many channels with hard_delete set to true.

Info:

queryDeletedChannels is server-side only. Calls made with a user token are rejected with a 403.

filter_conditions is optional. Omit it, or pass {}, to list every soft-deleted channel of your app. In a multi-tenant app, filter by team to list the soft-deleted channels of one tenant. See Multi-tenancy for assigning channels to a team.

The example below purges every soft-deleted channel of the app. To purge a single tenant instead, use the commented team filter. Each page holds up to 100 channels, which is also the maximum number of CIDs a single delete call accepts, so every page maps to one delete call.

# Every soft-deleted channel of the app
filter_conditions = {}
# Only the soft-deleted channels of one team
# filter_conditions = {"team": "tenant-a"}

next_cursor = None

while True:
    response = client.chat.query_deleted_channels(
        filter_conditions=filter_conditions,
        limit=100,
        next=next_cursor,
    )

    cids = [channel.cid for channel in response.data.channels]
    if cids:
        client.chat.delete_channels(cids=cids, hard_delete=True)

    next_cursor = response.data.next
    if not next_cursor:
        break

Purge with the delete many channels endpoint, as above. The single-channel delete does not accept a channel that is already soft-deleted: it responds that the channel does not exist, so it cannot purge the channels that queryDeletedChannels returns.

Hard deletes are processed asynchronously, so a channel keeps appearing in queryDeletedChannels results until its deletion task completes. Follow the next cursor rather than starting again from the first page, and poll the returned task IDs with the getTask endpoint if you need to confirm that the deletions have finished.

Request parameters

Name Type Description Default Optional
filter_conditions object Filter for the soft-deleted channels to return. Omit it, or pass {}, to list every soft-deleted channel of the app. See filterable fields below. {} Yes
sort array Sort order. Only created_at is supported, ascending (1) or descending (-1). [{ field: "created_at", direction: 1 }] Yes
limit integer Number of channels per page, from 1 to 100. 25 Yes
next string Cursor for the next page, taken from the previous response. Yes
prev string Cursor for the previous page, taken from the previous response. A request can set next or prev, not both. Yes

The response contains channels, the soft-deleted channels matching the filter, plus the next and prev cursors. Each channel is returned as a channel object with deleted_at set. Messages, members and read state are not included.

Filterable fields

Field Operators Notes
team $eq, $in Narrows the list to one tenant.
cid $eq, $in $in accepts at most 100 CIDs.
type $eq, $in
created_by_id $eq, $in
created_at $gt, $gte, $lt, $lte Use a range to split a large result.
deleted_at $gt, $gte, $lt, $lte

Only fields that a soft delete leaves intact can be filtered on:

  • Custom fields are not supported. Soft-deleting a channel clears its custom data, so a tenant identified only by a custom field cannot be found this way. Use team to identify tenants, or keep a record of the CIDs you delete.
  • Membership filters such as members are not supported. Deleting a channel removes its members.

Things to know

  • Errors: the request is rejected with a 400 when the filter uses an unsupported field or operator (such as a custom field or members), when sort uses a field other than created_at, when a cid $in lists more than 100 CIDs, when limit is below 1, or when both next and prev are set. A page that takes too long to read returns a 408. Narrow the filter with a created_at range, or query by cid, and retry.
  • Duplicate channel.deleted events: hard-deleting a channel that was already soft-deleted sends the channel.deleted event again. Make your webhook handlers idempotent for this event.
  • Deleted channel types: a soft-deleted channel whose channel type was deleted afterwards is still listed, with config set to null, and can still be hard deleted server-side. No channel.deleted event is sent when it is hard deleted.
  • Recreated channels: if a listed channel is recreated with the same ID before you delete it, it is a live channel again, and passing its CID to the delete call deletes it again. Delete each page promptly after listing it.