Calco

Backup & restore

You can export all of your organization's templates — the original files, their categories and tags, and every version — into a single .zip, and import that archive later to restore them. It is the supported way to snapshot your templates, move them between organizations or instances, or keep an off‑site backup.

This works in the web UI (Account → Data) and over the HTTP API. Like everything else, it is scoped to your active organization (see Organizations).

What's in the archive

The zip contains a root manifest.json plus one file per template version:

manifest.json
templates/<template-id>/v1.xlsx
templates/<template-id>/v2.xlsx
templates/<other-id>/v1.docx

manifest.json describes everything needed to rebuild the templates:

json
{
  "manifest_version": 1,
  "exported_at": "2026-06-23T10:00:00Z",
  "categories": ["Invoices", "Reports"],
  "templates": [
    {
      "id": "invoice-1718000000000-a1b2c3d4",
      "name": "Invoice Template",
      "slug": "invoice-template",
      "category": "Invoices",
      "tags": ["finance", "billing"],
      "versions": [
        {
          "version": 1,
          "original_filename": "invoice.xlsx",
          "sha256": "…",
          "size_bytes": 12345,
          "content_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
          "path": "templates/invoice-1718000000000-a1b2c3d4/v1.xlsx"
        }
      ]
    }
  ]
}

Entry paths are keyed by the template id (not the slug), because two templates can share a slug within one org.

Exporting

In the UI, open Account → Data and choose Export all templates; your browser downloads calco-templates-<org>.zip. Over the API:

bash
curl -X GET https://your-instance.example.com/api/templates/export \
  -H "Cookie: <your session cookie>" \
  -o calco-templates.zip

Export is available to any member of the organization.

Importing

In the UI, open Account → Data, pick the zip under Import templates, and upload it. Over the API:

bash
curl -X POST https://your-instance.example.com/api/templates/import \
  -H "Cookie: <your session cookie>" \
  -F "[email protected]"

Import is restricted to organization owners and admins. It is idempotent: templates and versions are deduplicated by content hash within the org, so a version your org already has (byte‑identical) is skipped rather than duplicated — re‑importing the same archive is safe and a no‑op for content that already exists. Categories listed in the manifest are created if missing.

The response summarizes what happened:

json
{
  "templates_created": 2,
  "versions_created": 3,
  "skipped": 0,
  "categories_created": 2,
  "errors": []
}

Import maps everything into your current active organization, regardless of the org the archive was exported from. This is how you copy templates from one org (or instance) into another: export there, import here.

The maximum import size is configurable with the TEMPLATE_IMPORT_MAX_MB environment variable (default 256 MB). A non‑zip upload, or a zip with no manifest.json, is rejected with HTTP 400.

Incomplete archives

If a version's underlying file is missing from storage at export time, the export does not silently drop it. The manifest flags that version with "missing": true (and writes no file for it) and lists it under a top‑level missing[] array, so the gap is explicit:

json
{
  "manifest_version": 1,
  "templates": [ /* … the version entry has "missing": true … */ ],
  "missing": [
    {
      "template_id": "invoice-1718000000000-a1b2c3d4",
      "version": 1,
      "original_filename": "invoice.xlsx",
      "sha256": "…",
      "path": "templates/invoice-1718000000000-a1b2c3d4/v1.xlsx",
      "reason": "blob not found"
    }
  ]
}

On import, those entries are reported in the response errors as "skipped: blob was missing at export time" rather than created.

API reference

MethodPathAuthPurpose
GET/api/templates/exportsessionDownload a zip of the org's templates + manifest.json.
POST/api/templates/importsession (owner/admin)Restore templates from a previously exported zip.