Developer Docs · 06

Data Exchange (Import / Export)

One generic envelope and one registry give every entity — core or plugin — import and export over CLI, REST and the admin UI.

The admin Import / Export (data-exchange) UI.
The admin Import / Export (data-exchange) UI.

The envelope

Every export is a self-describing JSON envelope keyed by entity:

{
  "vbwd_export": "shop_products",
  "version": 1,
  "shop_products": [ /* one row per record */ ]
}

Identity travels by natural key (a slug or other stable field) and foreign keys are resolved by slug on import — so an export from one instance re-resolves cleanly on another. Imports are upserts.

The seam

Core owns an agnostic EntityExchanger contract and a registry. Each exchanger declares an entity_key, a natural_key, permissions and supported formats. Core iterates the registry; it never names a plugin entity.

Using it

# REST (require_admin)
POST /api/v1/admin/data-exchange/<key>/export
POST /api/v1/admin/data-exchange/<key>/import

# CLI
flask data-exchange export <key> out.json
flask data-exchange import <key> out.json

# fe-admin: Import / Export page, one control per entity

Extending from a plugin

# plugins/<name>/.../data_exchange/<name>_exchangers.py
class MyExchanger(EntityExchanger):
    entity_key = "my_things"
    natural_key = "slug"
    # export()/import_() …

# in the plugin's on_enable():
data_exchange_registry.register(MyExchanger(session))

The CMS documentation pages you are reading are imported exactly this way: categories via the cms_terms exchanger, pages via the cms_post importer.