Skip to content

Moving Event Types Between Environments

An event type is usually designed and tested in development, then promoted through staging to production. Sparrow moves definitions between environments as one JSON file: select some or all event types in one environment, export them, and import the file in the next.

  1. Export from the environment where the definitions are right.

    In the UI, tick the event types on the Events page and choose Export selected, or choose Export all. The browser downloads event-types.json. From the CLI:

    Terminal window
    sparrow events export --prefix order. -f event-types.json
  2. Preview the import in the target environment. Nothing is written.

    Terminal window
    sparrow events import -f event-types.json --dry-run

    In the UI, choose Import on the Events page and pick the file; the preview appears straight away.

  3. Import. Confirm in the UI, or run the command without --dry-run.

{
"apiVersion": "sparrow/v1",
"kind": "EventTypeList",
"stamp": { "sparrow_version": "1.4.0", "format": 1, "sha256": "9f2c…" },
"items": [
{
"name": "order.created",
"description": "A customer placed an order.",
"event_schema": { "type": "object", "required": ["order_id", "total"], "properties": { "…": {} } },
"metadata": { "owner": "payments" },
"active": true
}
]
}
  • One file holds any number of event types (up to 500 per import), sorted by name.
  • The file carries no version numbers, timestamps or sample payloads. Version numbers are per environment: development may be on v7 while production is on v3 of the same schema. Leaving them out means the same definitions always export to the same file, so it can be reviewed and diffed in a pull request.
  • Sparrow’s own sparrow.* event types are never exported or imported.
  • A file can be written by hand. Only items is required, and within each item only name.

Each entry replaces the event type with the same name, using the usual version rules: a changed schema creates a new version, a first schema fills in version 1, other changes update in place, and an identical entry writes nothing. A field left out of an entry is cleared (a missing event_schema removes the schema, which is a breaking change), except active, which defaults to true.

Event types that are not in the file are never touched. An import never deletes anything; there is no prune option.

The whole file is applied in one transaction: every entry, or none.

Import flow: validate every item, check the stamp, apply every item in one transaction, render affected templates for new versions, then either roll back and return the result (dry run, unacknowledged warning or unapproved breaking change) or optionally pause failing subscriptions and commit. Import flow: validate every item, check the stamp, apply every item in one transaction, render affected templates for new versions, then either roll back and return the result (dry run, unacknowledged warning or unapproved breaking change) or optionally pause failing subscriptions and commit.

Every import returns the full result, whether or not it was written:

  • each entry’s action (created, new_version, updated, unchanged), its version before and after, and which fields change, including whether it deactivates or reactivates the type;
  • for a new version, whether the change is breaking and why;
  • for a new version, every subscription that receives the type, with its transform template rendered strictly against the new schema, twice: with a full sample payload and with only the required fields. The second catches a template that reads an optional field without checking it is there. Subscriptions without a transform are counted, since Sparrow cannot check what the receiving system does with the payload.

An import writes nothing, and says why in blocked_by, when:

ReasonMeaningTo proceed
dry runYou asked for a preview.Run it again without dry_run.
version_differsThe file was exported by a different Sparrow version.UI checkbox, --accept-version-mismatch, or acknowledge: ["version_differs"]
format_unsupportedThe file uses a bundle format newer than this server.UI checkbox, --accept-version-mismatch, or acknowledge: ["format_unsupported"]
items_changedThe items were edited after export.UI checkbox, --accept-edited, or acknowledge: ["items_changed"]
breakingA schema change is breaking for existing subscriptions.Type each event type’s name in the UI, --allow-breaking, or allow_breaking: true

The CLI exits non-zero when an import is blocked, and when a dry run would be blocked, so a promotion script stops at the right point.

By default subscriptions keep running after an import. A template that no longer fits the new schema then fails its deliveries with error category template_error: nothing wrong is sent, nothing is retried automatically, and the webhook’s health is unaffected. Fix the template, then retry the failed deliveries.

To hold deliveries instead, import with --pause-affected (UI: Pause the subscriptions whose template fails; API: subscription_policy: "pause"). Each failing subscription is paused in the same transaction, with the import as the reason. See Pausing a Subscription.

Terminal window
# Export by name, by prefix, or everything
curl -X POST http://localhost:8080/v1/event-types:export \
-H 'Content-Type: application/json' -d '{"names": ["order.created", "order.shipped"]}'
# Import: the exported file as-is, plus options
jq '. + {dry_run: true}' event-types.json | curl -X POST http://localhost:8080/v1/event-types:import \
-H 'Content-Type: application/json' -d @-

Import options: dry_run, acknowledge (a list of stamp warnings you accept), allow_breaking, and subscription_policy (keep_active or pause). The response has applied, imported_at, stamp, blocked_by and one entry per item in items. An invalid file (duplicate names, reserved names, a schema that does not compile, too many items) is rejected with 400 listing every problem, and nothing is imported.