Chat lifecycle
Overview​
Enterprise h2oGPTe can archive inactive chat sessions automatically, so that administrators can control chat retention and storage growth. Chat sessions follow a three-state lifecycle driven by a background sweep, with controls at the system, collection, and individual chat level.
The global inactivity policy is off by default. h2oGPTe doesn't stamp an inactivity limit onto new chats until an administrator sets chat_session_inactivity_days to a positive value. Four other paths archive a chat regardless of the current value of that setting: an inactivity limit already stamped on the chat, an expiry date set on the chat itself, the inactivity interval or expiry date on the chat's collection, and archiving the chat's collection. See Effective deadline for collection-backed chats.
The UI calls this feature Auto-Archive, and the API calls the per-chat control keep_forever. The two are inverses: turning Auto-Archive this chat on sets keep_forever to false. See Preserve a chat from auto-archive.
chat_session_inactivity_days is not retroactive, in either direction. h2oGPTe stamps the inactivity interval onto each chat when it creates the chat, so moving the setting from -1 to a positive value doesn't start a countdown on chats that already exist. Setting it back to -1 is equally one-way: chats stamped while the setting was positive keep their interval and continue to age out. Changing the value between two positive numbers behaves the same way.
Five events re-apply the current setting to an existing chat: turning off keep_forever, recovering an archived chat, recovering the chat's collection, removing a collection exemption, and moving a chat between collections. Because each of these applies whatever the setting holds at the time, they also clear a stamped interval while the setting is -1. A preserved chat is the exception: it keeps its stamped interval through a move.
Chat session states​
The status field on a chat session holds one of three values. The background sweep never moves a chat straight from active to archived, because its archiving pass only considers chats already in expiring. Archiving the chat's collection is the exception: it archives every chat in the collection at once, whatever their status.
| From | To | Trigger |
|---|---|---|
| active | expiring | The effective expiry date or the effective inactivity deadline falls within chat_expiration_limit_days. |
| expiring | active | Every deadline that applies moves outside the warning window. Posting a new message moves the inactivity deadline only. |
| expiring | archived | The effective deadline passes. |
| active or expiring | archived | The chat's collection is archived, by an administrator or by the collection lifecycle. Preserved chats are left alone. |
| archived | Deleted | archived_at becomes older than chat_expiration_limit_days. A chat in an archived collection waits for h2oGPTe to delete the collection instead. |
| archived | active | An administrator recovers the chat. |
The sweep that applies these transitions runs once when the service starts and then once an hour. A status change can lag a configuration change or a new message by up to an hour.
The sweep runs its three passes back to back, so a chat whose deadline has already passed can move from active through expiring to archived within a single sweep. The warning window isn't always observable.
A separate sweep runs every minute and archives collections whose expiry date has passed, along with their chats, so a collection expiry date takes effect within a minute rather than within an hour.
Lifecycle settings​
Two global settings control chat lifecycle behavior. Neither can be overridden per user or per role, and both are visible to non-administrators. Set each one in System Settings, through the API, or through an environment variable of the same name.
| Setting | Default | Description |
|---|---|---|
chat_session_inactivity_days | -1 | Days of inactivity before a chat becomes eligible for auto-archive. Set to -1 to turn off inactivity-based auto-archive. Any other value below 1 is rejected. Upper bound: 365. System Settings lists it as Chat Session Inactivity Limit (days). |
chat_expiration_limit_days | 30 | The warning window, the post-archive grace period, and the ceiling on user-set expiry dates. Range: 1 to 365. System Settings lists it as Chat Session Expiration Limit (days). |
Each setting also accepts an upper bound override through the chat_session_inactivity_days_upper_bound and chat_expiration_limit_days_upper_bound environment variables.
What chat_expiration_limit_days controls​
chat_expiration_limit_days is a single number with three distinct jobs. Changing it re-evaluates the status of every chat session in the system, in both directions.
- Warning window. An active chat flips to expiring once its effective deadline falls within this many days. With the default of
30, a chat enters the expiring state a month before it archives. - Grace period. h2oGPTe permanently deletes an archived chat once
archived_atis older than this many days. Recovery works only inside this window. - Ceiling on expiry dates. A request that sets a chat expiry date further out than this many days fails with
Expiry date cannot be more than N days from now.A date in the past fails withThe expiry date must be in the future.
Raising the limit widens the warning window and lengthens the recovery grace period at the same time.
Configure lifecycle settings​
Set the inactivity limit to 90 days:
curl -X PUT "https://<YOUR_DOMAIN>/api/v1/configurations/chat_session_inactivity_days" \
-H "Authorization: Bearer <API_KEY>" \
-H "Content-Type: application/json" \
-d '{"string_value": "90", "can_overwrite": false, "is_public": true}'
Narrow the warning window and the recovery grace period to 14 days:
curl -X PUT "https://<YOUR_DOMAIN>/api/v1/configurations/chat_expiration_limit_days" \
-H "Authorization: Bearer <API_KEY>" \
-H "Content-Type: application/json" \
-d '{"string_value": "14", "can_overwrite": false, "is_public": true}'
string_value, can_overwrite, and is_public are all required. Both chat lifecycle settings ship with can_overwrite set to false and is_public set to true, so keep those values unless you intend to change the visibility of the setting.
Effective deadline for collection-backed chats​
A chat that belongs to a collection inherits the stricter of the chat-level and collection-level policies. h2oGPTe evaluates two independent axes and takes the earlier value on each.
| Axis | Standalone chat | Collection-backed chat |
|---|---|---|
| Expiry date | The chat's own expiry_date. | The earlier of the chat's expiry_date and the collection's expiry_date. |
| Inactivity deadline | Chat updated_at plus chat inactivity_interval. | The earlier of the chat deadline and the collection's updated_at plus its inactivity_interval. |
Whichever axis comes due first drives the transition. Consider a chat last used one day ago, carrying a 90-day inactivity interval and no expiry date of its own, inside a collection that expires on 2026-09-30. Its inactivity deadline sits about three months out, so the collection's expiry date wins and the chat archives on 2026-09-30.
Moving a chat into or out of a collection restarts its inactivity clock and re-applies the current global setting, adopting the target collection's exemption. The same applies when you migrate every chat from one collection to another. A preserved chat keeps no inactivity interval instead of taking the global setting, though its clock still restarts. The move leaves every chat's expiry date alone.
Chats the lifecycle skips​
Three categories of chat sit outside the lifecycle:
- Preserved chats. A chat with
keep_foreverset totrueis never auto-archived and never removed by the automatic cleanup. An on-demand cleanup withforceset totruestill deletes it. - Internal chats. The chat sweeps and the cleanup pass never archive or delete the chats h2oGPTe creates for its own operations, even under an on-demand cleanup with
forceset totrue. - Archived chats inside an archived collection. h2oGPTe keeps them until it permanently deletes the collection itself.
Permissions​
| Action | Required access |
|---|---|
| Set or remove a chat expiry date | Full administrator, or the owner of the chat. No dedicated permission exists. |
Set keep_forever | Full administrator on any chat. A non-administrator needs the h2ogpte/chat/keep_forever permission, listed as Preserve chat sessions from global expiration, and must own the chat. |
| Exempt a collection | The permission to manage collections. |
| Archive, recover, or list archived chats | Full administrator. |
| On-demand cleanup | Full administrator. |
h2oGPTe grants h2ogpte/chat/keep_forever by default, and you can assign it to both roles and individual users. See Roles and Permissions.
Per-chat controls​
Each request in this section returns 204 No Content on success.
Set a chat expiry date​
Give a chat a fixed date on which it becomes eligible for auto-archive:
curl -X PUT "https://<YOUR_DOMAIN>/api/v1/chats/{session_id}/expiry_date" \
-H "Authorization: Bearer <API_KEY>" \
-H "Content-Type: application/json" \
-d '{"expiry_date": "2026-12-31", "timezone": "Europe/Berlin"}'
timezone is optional and accepts an IANA timezone name. Without it, the server interprets the date in its own local timezone, which shifts the deadline for anyone working in a different region.
The request fails with Cannot set expiry date on a preserved chat session. Disable auto-archive first. when the chat has keep_forever set to true. Set keep_forever to false before setting a date.
Remove a chat expiry date​
Drop the fixed date and fall back to the inactivity policy alone:
curl -X DELETE "https://<YOUR_DOMAIN>/api/v1/chats/{session_id}/expiry_date" \
-H "Authorization: Bearer <API_KEY>"
Preserve a chat from auto-archive​
Set keep_forever to true to take a chat out of the lifecycle:
curl -X PUT "https://<YOUR_DOMAIN>/api/v1/chats/{session_id}/keep_forever" \
-H "Authorization: Bearer <API_KEY>" \
-H "Content-Type: application/json" \
-d '{"keep_forever": true}'
The UI exposes this field, inverted, as the Auto-Archive this chat toggle on the Auto-Archive tab:
| UI control | keep_forever value | Result |
|---|---|---|
| Auto-Archive this chat turned on | false | The chat follows the expiry date and inactivity policies. |
| Auto-Archive this chat turned off | true | The chat never auto-archives. The chats table shows its status as Preserved. |
The two values behave differently:
trueclears the chat'sexpiry_dateandinactivity_intervaland forces its status back to active.falserestarts the inactivity clock and re-applies the current value ofchat_session_inactivity_days. A chat in an exempt collection gets no inactivity interval.
Exempt a collection from auto-archive​
Exempting a collection removes its chats from the global inactivity policy without touching any expiry date a user set explicitly.
curl -X PUT "https://<YOUR_DOMAIN>/api/v1/collections/{collection_id}/exempt_from_chat_expiry" \
-H "Authorization: Bearer <API_KEY>" \
-H "Content-Type: application/json" \
-d '{"exempt": true}'
The request returns 204 No Content on success.
Turning the exemption on clears inactivity_interval on the collection's chats and moves any expiring chat back to active where it now qualifies. It leaves preserved, internal, and already-archived chats alone.
Turning the exemption off re-applies the current chat_session_inactivity_days to those chats and re-evaluates their status in both directions, so some chats can land in expiring immediately.
The exemption covers the global inactivity policy only. A chat inside an exempt collection that carries an explicit expiry_date still archives on that date. Clear the expiry date as well if you need the chat to survive indefinitely, or set keep_forever on it.
Every event that re-applies the global setting honors an active exemption. Turning keep_forever off, recovering an archived chat, moving a chat into the collection, and recovering the collection itself all skip the inactivity interval while the collection is exempt.
A collection must be active to change its exemption. The request fails with Collection not found for a collection in the archived state.
Administrators can set the exemption from the Manage collections page.
Collection deletion and chat sessions​
The way a collection goes away decides what happens to its preserved chats:
- Automatic cleanup of an archived collection. h2oGPTe detaches preserved chats from the collection, and they survive as standalone chats, keeping their generated documents. Their answers lose their references, because the collection's documents are gone.
- An explicit delete by a user or an administrator. Every chat in the collection goes with it, even the ones carrying
keep_forever.
For a broader view of what a delete removes, see User Data Deletion.
Archive and recover chat sessions​
Administrators review archived sessions from the Archived Chats filter in the Chats list, which also offers the Recover Chat action.
Archive a chat session​
Archiving bypasses the warning state and puts a chat straight into archived:
curl -X POST "https://<YOUR_DOMAIN>/api/v1/chats/{session_id}/archive" \
-H "Authorization: Bearer <API_KEY>"
The operation stamps archived_at, then clears expiry_date, inactivity_interval, and keep_forever. Because it clears keep_forever, archiving a preserved chat starts the grace-period clock on it. The request returns 204 on success, or 404 when the chat does not exist or is already archived.
Recover an archived chat session​
Recovery is administrator-only. It resets the status to active, clears archived_at, restarts the inactivity clock, and restores inactivity_interval from the current global setting unless the collection is exempt.
curl -X POST "https://<YOUR_DOMAIN>/api/v1/chats/{session_id}/unarchive" \
-H "Authorization: Bearer <API_KEY>"
h2oGPTe recomputes the recovered chat's status immediately, so a chat that still sits inside the warning window of its collection returns to expiring rather than looking active until the next sweep. The request returns 204 on success, or 404 when the chat does not exist or isn't archived.
Recover a chat before the grace period ends. Once the cleanup pass deletes an archived chat, you can't recover it.
Recovery fails with a conflict and the message Cannot unarchive a chat session from an archived collection when the chat's collection is still archived. Recover the collection instead. Recovering a collection returns every chat it archived to active and restarts their inactivity clocks, so you don't need a separate chat recovery call. It doesn't restore the expiry dates those chats carried, because archiving cleared them. Users have to set them again. Until you recover the collection, its archived chats don't appear in the Archived Chats filter.
List archived chat sessions​
Retrieve archived sessions with pagination, filtering, and sorting. The request returns 200 with an items array and a total count.
curl -s "https://<YOUR_DOMAIN>/api/v1/admin/chats/archived?offset=0&limit=100&sort_column=archived_at&ascending=false" \
-H "Authorization: Bearer <API_KEY>"
| Parameter | Default | Description |
|---|---|---|
offset | 0 | Number of sessions to skip. |
limit | 100 | Number of sessions to return. |
filter | empty | Matches on chat name, chat ID, collection name, collection ID, or username. |
sort_column | archived_at | One of name, updated_at, archived_at, username, or collection_name. |
ascending | false | Sort direction. |
The list omits internal chats and any chat whose collection is archived.
Delete old chat sessions on demand​
Run a one-off cleanup instead of waiting for the automatic passes. The endpoint dispatches an asynchronous job and returns its details with a 201.
curl -X POST "https://<YOUR_DOMAIN>/api/v1/admin/chats/cleanup" \
-H "Authorization: Bearer <API_KEY>" \
-H "Content-Type: application/json" \
-d '{"older_than_days": 180, "force": false}'
| Field | Default | Description |
|---|---|---|
older_than_days | required | Selects chats whose updated_at is older than this many days. Must be 1 or greater. |
force | false | When false, the job skips preserved chats and chats already in archived status. When true, the job covers both. |
This cleanup selects on updated_at alone, not on lifecycle status. It deletes matching active chats outright, without an archived state to recover from, and it ignores collection exemptions. Run it with force set to false first and check the resulting job before repeating with force set to true.
The job skips internal chats in both modes.
Python client examples​
The h2ogpte Python SDK covers every operation on this page. These examples use the synchronous client. H2OGPTEAsync exposes the same methods for asynchronous code.
from h2ogpte import H2OGPTE
admin = H2OGPTE(address="https://<YOUR_DOMAIN>", api_key="<API_KEY>")
# Turn on inactivity-based auto-archive at 90 days
admin.set_global_configuration(
"chat_session_inactivity_days", "90", can_overwrite=False, is_public=True
)
# Narrow the warning window and the recovery grace period to 14 days
admin.set_global_configuration(
"chat_expiration_limit_days", "14", can_overwrite=False, is_public=True
)
# Pin a single chat to a fixed date, then hand it back to the inactivity policy
admin.set_chat_session_expiry_date("<CHAT_SESSION_ID>", "2026-12-31", timezone="Europe/Berlin")
admin.remove_chat_session_expiry_date("<CHAT_SESSION_ID>")
# Preserve a chat from auto-archive (equivalent to turning Auto-Archive this chat off)
admin.set_chat_session_keep_forever("<CHAT_SESSION_ID>", True)
# Exempt a whole collection from the global inactivity policy
admin.set_collection_exempt_from_chat_expiry("<COLLECTION_ID>", True)
# Review archived sessions, then recover one
archived = admin.list_archived_chat_sessions(
offset=0, limit=100, sort_column="archived_at", ascending=False
)
admin.unarchive_chat_session("<CHAT_SESSION_ID>")
# Delete chats untouched for 180 days, skipping preserved and archived ones
job = admin.admin_cleanup_chat_sessions(older_than_days=180, force=False)
set_chat_session_expiry_date accepts the date as a YYYY-MM-DD string and raises ValueError on any other format. When you omit timezone, the client sends the local timezone of the machine running the script, which is worth pinning explicitly in scheduled jobs.
list_archived_chat_sessions requires offset, limit, sort_column, and ascending explicitly, because the endpoint defaults don't apply to the client. It returns the list of sessions without the total count.
admin_cleanup_chat_sessions waits for the job to finish and returns it. Pass timeout to bound the wait.
Audit trail coverage​
Chat lifecycle changes that a user or an administrator makes are recorded in the Audit trail. Each of these actions produces a generic CUSTOM_WRITE record:
- Archiving and recovering a chat
- Setting and removing a chat expiry date
- Changing
keep_forever - Changing a collection's exemption from the global chat inactivity limit
A generic record names the method that ran, such as set_chat_session_keep_forever, the user who ran it, and the workspace. It doesn't carry the ID of the chat or collection, or the values that changed, such as the new expiry date. Other domains have dedicated audit events with that detail.
Transitions that the background sweep applies on its own, such as a chat moving to expiring, archived, or deleted, aren't user actions and don't produce these records. Use the Archived Chats filter and the job history as evidence for those.
- Submit and view feedback for this page
- Send feedback about Enterprise h2oGPTe to cloud-feedback@h2o.ai