flow-as-code

Flow as code for Amazon Connect.

An Amazon Connect flow is a JSON document full of ARNs, and the ARNs differ per account, per region, per instance. So moving one flow from dev to prod means maintaining a mapping from "the queue I mean" to "the ARN it has in this environment", and something has to walk the flow and swap one for the other before deployment. That mapping table is the thing this tooling deletes.

Open the studio demo See it promote across environments Use it from Terraform Read the docs The demo needs a desktop-width screen. It is read-only, in your browser, no account and no network calls.

References are tokens

A flow refers to a queue by name, not by ARN:

new UpdateContactTargetQueue({
  id: "set-working-queue",
  queue: Refs.queue("appointments"),
  next: "look-up-appointment",
  onError: "apologize",
});

Refs.queue("appointments") becomes ${cdref:queue:appointments} in the document. It stays a token through authoring, lint, version control and review. It becomes an ARN at exactly one moment: when your IaC tool resolves it, per environment. Under CDK the binding is a CloudFormation intrinsic, a Fn::GetAtt at a resource the stack creates or an Fn::ImportValue of one it does not. Under Terraform it is a templatefile variable your own resource addresses fill in.

That is a lint rule, not a convention: a literal ARN in authored content fails a hard rule, and in the studio a failing hard rule disables saving.

One flow, two environments

The promotion example is that argument as commands rather than prose: one FlowDoc reaching a dev and a prod environment, on both deploy paths. Two facts about it are worth stating because a test asserts each, on every push to main and every pull request.

tests/promoteAcrossEnvironments.test.ts runs the example's own inputs and holds both. The example's README also names what it does not show.

The map that is not that table

The Terraform path does take an --address-map, one small JSON file per environment, and it looks like the table described above. It is a different object. Its values are Terraform addresses of resources your own configuration manages, aws_connect_queue.appointments.arn where the environment creates the queue and data.terraform_remote_state.platform.outputs.appointments_queue_arn where a platform team owns it. The emitter refuses any value matching /arn:aws/i outright, telling you to map it to a terraform address instead, so the file cannot decay into an ARN table.

The CDK path needs no such file because the flows and the resources are objects in one program, so the app already holds the reference and there is nothing to name. HCL has no equivalent, so something has to name the address in text. That, and only that, is why one path takes a map and the other takes none.

Or write the flow in Terraform

The flow-as-code/flowascode provider, on the Terraform and OpenTofu registries, takes a flow as HCL action blocks and manages it through the Amazon Connect API. The actions name what they use by key; one refs map says which queue that is in this environment:

resource "flowascode_contact_flow" "support_line" {
  instance_id = var.connect_instance_id
  name        = "support-line"
  type        = "CONTACT_FLOW"

  refs = {
    "queue:support" = aws_connect_queue.support.arn
  }

  action {
    id   = "set-queue"
    next = "transfer"
    update_contact_target_queue {
      queue_id = "queue:support"
    }
    error {
      type = "NoMatchingError"
      next = "hang-up"
    }
  }
  ...
}

Your first flow with the provider, promoting it from dev to prod, bringing existing flows under Terraform, and the flow cookbook walk through it with output from a live instance. The studio edits a .flow.tf the way it edits TypeScript, in both directions.

For AI coding agents

The repository ships Agent Skills that teach a coding agent the rules Connect enforces, how to promote flows and how to adopt existing ones, with a reference to every action type generated from the same catalog lint reads. In Claude Code: /plugin marketplace add flow-as-code/flow-as-code. Any other agent can read them as plain markdown from llms.txt.

What it is

Open-source tooling for Amazon Connect flows: typed flow authoring, lint, simulate-based testing, export from a live instance, and a visual editor, all round-tripping through one interchange format, with CDK and Terraform/OpenTofu code as outputs. Everything but the live commands works offline.

FlowDoc is the pivot. It is a JSON document holding Connect Flow-language content with ${cdref:type:name} reference tokens, plus layout and metadata. Every tool in the set reads and writes it, so there is no second format to keep in sync.

typed TS builder --synth--> FlowDoc --codegen--> typed TS builder
studio canvas   <--edit--> FlowDoc
FlowDoc <--codegen/synth--> .flow.tf (flowascode provider resource)
FlowDoc --cdk--> CDK stacks (CloudFormation tokens)
FlowDoc --tf-->  .tf + .tftpl files (resource references)
live instance --export--> FlowDoc (+ codegen to TS or HCL)

Packages

Try it

Published on npm under the @flow-as-code scope. The six packages version together and are released as a set, so install them at matching versions. Node 22.12 or newer.

npm i -D @flow-as-code/cli
npx flow-cli init flows/              # a first flow to open: FlowDoc + .flow.ts

npx flow-cli studio flows/            # visual editor, live-synced
npx flow-cli lint flows/              # the rule set over the document set
npx flow-cli emit flows/ --target tf  # or --target cdk

Every command after the install works on a directory of FlowDocs, so init comes first: it writes a small demo flow and the typed .flow.ts that synthesizes to it. If nothing above the directory is already a package it adds a package.json marking the pair as modules; inside a project that has one it leaves yours alone, and the pair loads either way. Edit either side and the studio keeps them in step.

@flow-as-code/cli depends on the other five packages, so installing it alone is enough for the CLI. For the Terraform provider, declare flow-as-code/flowascode in required_providers; there is nothing to install from npm. The studio demo above needs no install at all, and the promotion example runs from a clone of the repository.