Skip to main content
Export gateway conversations to a portable JSON file so you can back them up, move them between hosts, or restore them after disaster.

Quick Start

1

Back up and restore from the CLI

Export every session to one file, then restore it on any host.
2

Do it from Python

The gateway helpers wrap the same store the gateway runs on.

How It Works

Export reads live sessions into a versioned payload; import writes them back and resets live routing fields so restored state stays inert until the gateway re-binds it.
Gateway bindings survive clear_session. Calling store.clear_session(session_id) empties the active transcript but keeps gateway_session_id and agent_id on the session — subsequent get_by_gateway_session(...) and list_sessions_by_gateway_agent(...) lookups continue to resolve the same session as an indexed lookup on SqliteSessionStore. To unbind and remove the routes, call delete_session(session_id). See PraisonAI PR #5681 / issue #5521.
Malformed turns are dropped on import. import_sessions reconstructs each payload through SessionData.from_dict, which skips any entry in messages or archived_messages that is not a JSON object (null, a string, a number). Healthy turns survive — a single non-object turn in an imported transcript no longer crashes gateway routing (get_by_gateway_session, list_sessions_by_gateway_agent). When dropped active turns precede the compaction anchor, last_compaction.message_index is shifted down so get_working_history() still slices the retained tail correctly. The “live-field reset” and “version guard” behaviours above are unaffected. See PraisonAI PR #5676 / issue #5672.

Choosing the right options

Pick a scope, decide on lineage, and choose whether to overwrite.

CLI Reference


Python API Reference

The same surface exists on the store and as thin gateway helpers.
Gateway helpers target the store the gateway actually runs on:
DefaultSessionStore, SqliteSessionStore, and SqliteTranscriptStore (the default gateway backend) all implement PortableSessionStoreProtocol. The payload format is identical across all three — a backup from one restores cleanly into another.
Not to be confused with PraisonAIDB.export_session / aexport_session / import_session / aimport_session. Those live on the DB adapter (conversation-store layer, one session at a time) — see Async Conversation Store. The methods on this page are the store-level portability API (include_lineage= / reset_live_fields= / export_all), a different surface for whole-store backup and migration.
ImportReport is JSON-serialisable via as_dict():

Payload format

The envelope is versioned and symmetric across the built-in stores.
Each session dict is the SessionData.to_dict() shape, so SessionData.from_dict() reconstructs it verbatim on import.

Concurrency & no-clobber guarantee

Concurrent importers racing on the same session_id are safe: one wins, the other is reported — never silently overwritten.
Both DefaultSessionStore and SqliteSessionStore enforce this guarantee identically. overwrite=False is checked twice. The import loop first scans with session_exists, then _save_imported_session rechecks os.path.exists inside the per-file write lock — so a peer that creates the same session between the two checks still cannot be clobbered. The loser lands in ImportReport.skipped as {"session_id": "shared", "reason": "already exists (use overwrite)"}. As of PraisonAI PR #5631, SqliteSessionStore honours overwrite end-to-end, so this protection holds identically whether the destination is JSON-on-disk or the SQLite-indexed store.

Common Patterns

Daily backup — schedule a cron job that writes one dated file per day.
Staging → production migration — export from staging, then overwrite only the ids being cut over.
Move one user’s conversation between hosts — export a single lineage-aware session, plain-import on the new host.
Restore after disaster — import a backup; live fields reset by default, so sessions stay inert until the gateway re-binds them.

Best Practices

Resetting clears gateway_session_id and agent_id (top-level and inside metadata) so a restored session cannot masquerade as an active connection. Only opt out with --keep-live-fields when re-importing onto the exact same live gateway.
--max-sessions (default 10_000) bounds how many sessions land. The remainder is skipped and reported — never silently truncated.
A skipped record — already-exists, malformed, or over the cap — is listed with a reason, never dropped in silence. Read skipped before assuming a clean restore.
--out writes atomically via a temp file + os.replace, so a crashed export never leaves a half-written backup.
PORTABLE_VERSION = 1. Older/unversioned payloads are accepted best-effort; a payload with a newer version is rejected wholesale rather than partially applied.
As of PraisonAI PR #5686 (issue #5517), SqliteTranscriptStore.export_all scans rows in sessions.db and ignores stray *.json sidecars; import_sessions writes DB rows and reports a read-only database (PRAGMA query_only=ON) as a failed write in ImportReport.skipped instead of silently writing a sidecar. Lineage-aware exports (lineage_id / root_session_id / thread_id) follow DB continuations end-to-end.

Troubleshooting

Fixed in PraisonAI PR #5631. Upgrade praisonaiagents and re-run the import; SqliteSessionStore now accepts and honours the overwrite flag end-to-end.
Fixed in PraisonAI PR #5518 and PR #5686. Before the fix, SqliteTranscriptStore inherited JSON-sidecar implementations of export_all / _collect_lineage / _save_imported_session — exports omitted every DB session and imports silently wrote to invisible .json sidecars. Upgrade praisonaiagents and re-run the export/import; the DB-row path is now used end-to-end.

Session Persistence

Durability across restarts on the same host.

Session Continuity

Survive disconnects without losing in-flight state.

Session Store

Where and how gateway sessions are stored.

Session Protocol

The pluggable store contract, including portability.