Skip to Content
Polyant is open source under AGPL-3.0 — star us on GitHub.
How-toExport & Import an Instance

Export and Import an Instance

Polyant can serialise an entire instance into a JSON bundle and re-create it elsewhere — useful for backup, promoting from staging to production, or sharing a template between teams.

The bundle is safe to share over normal channels: secret VALUES are never included. Only the list of which secret keys are configured travels with the export. The same is true for skill env vars and channel credentials.

This guide covers the full round trip and the manual steps you must run after import.

Prerequisites

  • An authenticated admin session (cookie or Bearer token).
  • The slug of the source instance (e.g. my-assistant).
  • For “overwrite mode”, the destination instance must already exist.

Step 1 — Export the bundle

curl -s http://localhost:4000/api/instances/my-assistant/export \ -H "Cookie: authjs.session-token=$TOKEN" \ -o my-assistant.bundle.json

The export endpoint returns indented JSON with a Content-Disposition header so browsers download it as a file. curl -o works fine; piping through jq also works.

What the bundle contains (assembled in export.service.ts):

  • Instance metadataslug, name, description, status, provider, model, icon, sttProvider, langsmithProject, plus the toggles memoryEnabled, knowledgeEnabled, langsmithEnabled, authEnabled, thinkingEnabled.
  • Prompts — all 8 sections, full text.
  • Skills — assignments only (slug + enabled + autoLoad + pinnedVersion). The skill DEFINITIONS live in the global catalog; if the destination engine lacks a skill, the import will warn.
  • Manual tools — the list of tool names that were explicitly added on top of the skill-derived defaults.
  • Secrets[{ key, configured: true }]. Values are stripped.
  • Channels[{ channelType, enabled }]. Credentials are stripped.
  • Skill env[{ skillSlug, key, encrypted, hasValue }]. Values are stripped.
  • Room config — prompt, outbound channel/target, eval interval.
  • Event sources — with their definitions (matching prompt + interpretation prompt).
  • Scheduled tasks — schedule, prompt, outbound channel/target, retention flags.

What the bundle does NOT contain: conversation history, memories, audit logs, pipeline traces — these are operational data, not configuration.

Step 2a — Import as a new instance

This is the safest mode. The bundle is parsed through instanceBundleSchema and the slug is taken from the bundle itself; if it collides with an existing instance, the engine auto-resolves to a unique slug (e.g. my-assistant-2, my-assistant-3, …).

curl -s -X POST http://localhost:4000/api/instances/import \ -H "Content-Type: application/json" \ -H "Cookie: authjs.session-token=$TOKEN" \ --data-binary @my-assistant.bundle.json

If you want to control the destination slug, edit the bundle’s instance.slug field before posting (the slug lives under instance, not at the top level — the bundle’s top-level keys are version, exportedAt, type, instance):

jq '.instance.slug = "my-assistant-staging"' my-assistant.bundle.json > staging.bundle.json curl -s -X POST http://localhost:4000/api/instances/import \ -H "Content-Type: application/json" \ --data-binary @staging.bundle.json

The response includes the created instance and a warnings array. Each warning is an object with type and message:

{ "warnings": [ { "type": "secret_required", "message": "Secret \"openai_api_key\" needs to be configured" }, { "type": "skill_not_in_catalog", "message": "Skill 'foo' not found in catalog — assignment skipped" }, { "type": "tool_not_registered", "message": "Manual tool 'bar' not registered on this engine" } ] }

Step 2b — Import as overwrite

Use this to update an existing instance in-place. The target slug is taken from the URL.

curl -s -X POST http://localhost:4000/api/instances/my-assistant/import \ -H "Content-Type: application/json" \ -H "Cookie: authjs.session-token=$TOKEN" \ --data-binary @my-assistant.bundle.json

Overwrite replaces prompts, skill assignments, manual tools, room config, event sources, and scheduled tasks. Existing instance secrets and channel credentials are preserved — the bundle has nothing to overwrite them with anyway.

Step 3 — Re-enter secrets

Because the bundle ships only the list of configured keys, you must add each value back on the destination engine:

curl -s -X PUT http://localhost:4000/api/instances/my-assistant-staging/secrets \ -H "Content-Type: application/json" \ -d '{ "secrets": [ { "key": "openai_api_key", "value": "sk-..." }, { "key": "langsmith_api_key", "value": "ls_..." } ] }'

See Manage secrets for the full reference.

Step 4 — Reconnect channels

No channel rows are created on import — GET /api/instances/:slug/channels on a freshly imported instance returns {"channels": []}. You must PUT each channel manually. Open each channel tab in the admin panel and paste the tokens, or use the channel endpoints:

curl -s -X PUT http://localhost:4000/api/instances/my-assistant-staging/channels/telegram \ -H "Content-Type: application/json" \ -d '{ "config": { "botToken": "12345:ABC..." }, "enabled": true }'

See Connect a channel for the per-channel payload.

Bonus — Export and import the global skill catalog

The catalog is independent of any instance. Use these endpoints before importing instances that depend on custom skills (note the catalog/ segment — easy to miss):

# Export curl -s http://localhost:4000/api/skills/catalog/export -o skills.bundle.json # Import curl -s -X POST http://localhost:4000/api/skills/catalog/import \ -H "Content-Type: application/json" \ --data-binary @skills.bundle.json

Verification

After Step 2, before Steps 3–4:

curl -s http://localhost:4000/api/instances/my-assistant-staging/secrets | jq '.secrets[] | select(.configured == false)'
  • All non-default secret keys appear with configured: false — they need values.
  • GET /api/instances/my-assistant-staging/channels returns {"channels": []} — channels must be configured manually via PUT.
  • The prompts, skill assignments, room config, event sources, and scheduled tasks match the source — the configuration round-tripped cleanly.

Once Steps 3 and 4 are done, a test message via the Playground (or POST /v1/chat/completions) should succeed end-to-end.

See also

Last updated on