Skip to content

Importing an existing catalog

document:docs/events/importing

Bring in events from GitHub, AsyncAPI, or an EventCatalog project.

View as Markdown

Three importers, for the three places event contracts usually already live. All of them write the same generated event folders, so an import is a starting point rather than a parallel format you now have to maintain.

Pull schemas and examples from another repository without copying files by hand.

Single event:

Terminal window
ac import \
--github-repo acme/contracts \
--ref main \
--id payment.completed \
--domain payments \
--version 1.0.0 \
--schema-path schemas/payment.completed.json \
--example-path examples/payment.completed.json

Bulk, from a manifest committed in that repository:

Terminal window
ac import \
--github-repo acme/contracts \
--ref main \
--manifest-path autocatalog.sources.json
{
"events": [
{
"id": "payment.completed",
"domain": "payments",
"version": "1.0.0",
"name": "Payment Completed",
"summary": "Payment has completed",
"schemaPath": "schemas/payment.completed.json",
"examplePaths": ["examples/payment.completed.json"]
}
]
}

Add --dry-run to plan the import without writing anything.

ac import uses gh by default, so a private repository works if gh is authenticated. --github-token-env GITHUB_TOKEN passes a token instead, and --clone-with git falls back to plain git. Remote URL imports are restricted to raw.githubusercontent.com and github.com unless you pass --allow-host.

Terminal window
ac from-asyncapi ./asyncapi.yaml --out ./events

Both 2.x and 3.x documents are supported. Everything the importer cannot resolve is reported rather than dropped: an unresolvable $ref on one message costs that message, not the channel it lives on, and the run ends with a count of what was skipped and why.

Fail the command when anything was skipped — the right setting for a migration running in CI:

Terminal window
ac from-asyncapi ./asyncapi.yaml --out ./events --strict

A message’s own version comes from EventCatalog’s x-eventcatalog-message-version where it has one. An event is its domain, id and version together, so billing’s created and sales’ created stay two events, and two versions of one event import as the newest with the older as its history. A payload that refers to components.schemas keeps those definitions in its own $defs, so the schema stands alone. An example named duplicate/redelivered is the redelivered example in the duplicate case, which is also where EventCatalog’s generator writes it. A document ac to-asyncapi wrote comes back exactly: ids, versions, owners and delivery promises ride along under x-autocatalog.

Terminal window
ac from-eventcatalog ./my-eventcatalog --out ./events
ac resources scan .
EventCatalogBecomes
events, with their historyevents under --out
commandscommands under commands/eventcatalog/: senders from the services that send them, the handler from the one service that receives them
queriescommands whose outcome is a result, tagged query
servicesservices, under the domain that files or lists them
containers, and services’ writesTo / readsFromtables (collections for a document store) and the reads and writes on them, so drift sees one team reaching into another’s data
owners, and teams/ and users/ pagesthe first owner, team or user, of each of these, named as its page names it, with its Slack or Teams link or email to reach it
systemsthe system each service is part of, whether the system lists it or holds it in its folder
channelseach message’s transport, and its delivery guarantee

Commands go to commands/eventcatalog/ and services and datastores to .autocatalog/imports/eventcatalog.json, both under --cwd, which defaults to the folder holding --out. Both belong to the import: running it again replaces them, so a command or service the catalog dropped goes too, and commands of your own beside them are left alone. A command that fails to import keeps the version imported before, and the import names it. A command, service or datastore page whose frontmatter cannot be read could be any of its kind, so then none of that kind imported before is dropped, and each one kept is named. ac resources scan adds them to the graph. Schemas are read in the JSON Schema dialect they declare, so draft-07, which EventCatalog’s own examples use, validates as draft-07.

Entities, flows and ADRs have no autocatalog counterpart, and neither do a team or person that owns nothing or a system that groups no service. The import names each kind it left behind and how many, rather than leaving them out quietly. A team’s members and a system’s own summary and owners stay behind too. A command that several services receive gets no handler, and the import says so: a command has exactly one. Two services that share a name become one service, as their events already treat them.

Markdown documents become generated manifests. A document with unparseable frontmatter, an invalid schema.json, or no id is skipped and named, and the rest of the catalog still imports. --strict turns any skip into a non-zero exit.

An event’s classification has no EventCatalog field, so ac to-eventcatalog writes it as a classification: <value> badge and ac from-eventcatalog turns that badge back into the field.

Examples come across with their case folders, and a channel’s deliveryGuarantee becomes the guarantee of the events on it when every channel an event travels on agrees. ac to-eventcatalog writes the other way: the guarantee on each channel whose events agree, the rest of delivery under x-autocatalog, and an examples.config.yaml naming each case for EventCatalog’s readers.

ac to-eventcatalog writes all of it back: commands and queries, services with their owners, repositories and the datastores they read and write, the datastores, the systems grouping the services, and a page for each owner with its name and contact, from the .autocatalog/resources.json beside each events/ root. A team, person or system page EventCatalog already has is left as it is. Importing what it wrote gives back what was imported, so a team can keep EventCatalog as its portal while autocatalog gates the pull requests:

Terminal window
ac from-eventcatalog ./my-eventcatalog --out ./events
ac resources scan .
ac to-eventcatalog ./my-eventcatalog --root ./events --narrative

A page the export wrote opens with where the contract came from and ends with EventCatalog’s schema viewer; importing it again leaves both out of the narrative, since the next export adds them. A datastore a service names but that nothing describes stays a pointer, as it was, rather than gaining a page. Entities, flows and ADRs are not touched.

Placeholder schemas are reported as a caveat rather than a skip: the event imports, and the catalog tells you its schema needs attention.

Imported events are ordinary generated state. Run validation, then decide whether to keep importing or to move the contracts into code with Zod or Pydantic — which is where the drift gate starts paying for itself.

Owned by MaintainersLast changed 12 Oct 2026SourceEdit this page