Module 3: Exporting, Normalizing, and Structuring the Repository

5. Structuring the repository

Description

By the end of this lesson you will be able to give cumbre-automations a professional layout: a folder structure where every file has an obvious place, with workflows/, credentials/ (schema and example only, never values), docs/, scripts/, .env.example, and a README that welcomes whoever arrives. You'll know how to map the ways n8n organizes your workflows —tags, folders, projects— onto the repository's layout, and which naming conventions make the repo "navigate itself" without anyone having to explain it.

This matters because a repository is, first and foremost, a way of communicating. The files you exported are clean and secret-free, but if they live piled up in the root with names like aBcD1234EfGh5678.json, nobody —including you in three months— knows what's what. Structure is what turns a pile of correct files into a readable system. And it's exactly what another developer evaluates when they open your repo: in the first thirty seconds, the root folder tells them whether this was built by someone who thinks about whoever comes next, or someone who just dumped files.

Connection to the module: lessons 2, 3, and 4 gave you the material —exported workflows, secrets out, normalized JSON. This lesson gives that material shape. It's the first of two "organize" steps: here you build the skeleton (where each thing goes), and lesson 6 puts the flesh of documentation on it (what each workflow does). The layout you define here is also the one lesson 7's script is going to populate automatically, and the one Module 4 is going to extend with the environments dimension. Think of this lesson as the building's blueprint before you furnish it.

A repository is a message

Before drawing folders, it's worth internalizing why structure matters so much, because it's easy to see it as a cosmetic detail, and it isn't.

When someone clones cumbre-automations for the first time, the first thing they see is the root's list of files and folders. That list is the book's cover. In half a second, that person forms a hypothesis about what this repo is, how well cared for it is, and where to start reading. A repo whose top level says workflows/, docs/, scripts/, README.md tells a clear story: "here are workflows, here's their documentation, here are the tools, start with the README." A repo whose top level is a rain of workflow (1).json, workflow (final).json, test2.json tells another one: "someone dumped files here and left."

Think of it as the difference between walking into an organized hardware store and a warehouse where everything's on the floor. In both, the screw you're looking for might be there. But in the organized one, you find it yourself, following the aisle signs; in the warehouse, you have to ask the owner, and if the owner isn't there, there's no screw. A well-structured repository is the hardware store: the "signs" —the folder names— guide anyone without the author having to be present. And "the author doesn't have to be present" is, literally, the definition of a good handoff.

The cumbre-automations layout

This is the skeleton we're going to build. It isn't the only possible one —every team adjusts details— but it follows conventions a developer recognizes instantly:

cumbre-automations/
├── README.md               ← the cover: what this is and how to use it
├── .gitignore              ← what does NOT enter the repo (secrets, noise) — lesson 3
├── .gitattributes          ← line-ending norms — lesson 4
├── .env.example            ← which secret variables are needed, WITHOUT values
├── workflows/              ← the exported, normalized workflows
│   ├── order-triage.json
│   ├── inventory-sync.json
│   ├── weekly-report.json
│   └── support-autoresponder.json
├── credentials/            ← schema and example ONLY, NEVER values
│   └── README.md           ← which credentials each workflow needs
├── docs/                   ← handoff documentation — lesson 6
│   ├── order-triage.md
│   └── ...
└── scripts/                ← the automation tools — lesson 7
    ├── export.sh
    └── normalize.js

Let's go folder by folder, because each one answers a question another developer is going to ask.

workflows/ — answers "what does this system do?" It holds the exported, normalized workflows, one per file, with readable names. It's the repo's central content: the versioned logic. Everything else exists to support this folder.

credentials/ — answers "what does it need to run?", without revealing the secrets. And here's the warning we're carrying over from lesson 3, repeated on purpose because it's the one that costs the most: never, in this folder, does a credential's value go. Not even encrypted. What goes here is a document that lists which credentials each workflow requires, with its type and purpose —"order-triage needs a Header Auth credential for the CRM and a language-model one for the AI Agent." It's a schema, an inventory of "which keys are needed," not the keys. The folder is named credentials/ because it describes what it's about, but its content is documentation, not secrets. If you ever see a .json with a real key here, something broke badly.

docs/ — answers "how do I understand each workflow in detail?" One Markdown file per workflow, with its purpose, trigger, dependencies, and node diagram. It's lesson 6's full topic; for now, reserve its spot in the layout.

scripts/ — answers "how do I maintain this?" The tools: the export script (export.sh) and the normalization one (normalize.js) you build in lessons 4 and 7. It's the repo's toolbox, kept separate from the content so business logic and the machinery that manages it don't mix.

The root filesREADME.md, .gitignore, .gitattributes, .env.example— are the building's front desk. The README gives the welcome and instructions; the three dotfiles are the security and consistency infrastructure you already set up in lessons 3 and 4.

Notice the logic of the separation: content (workflows/), what the content needs but can't contain (credentials/, .env.example), documentation (docs/, README.md), and tools (scripts/). Four categories, four places. When a new file arrives, the question "which of the four is it?" almost always has an obvious answer, and that's where it goes.

From ugly id names to readable names

Remember the problem we left pending in lesson 2: when you export with --all --separate, n8n names each file after the workflow's id, not its name. You end up with aBcD1234EfGh5678.json instead of order-triage.json. Unreadable. It needs fixing, and there are two paths.

Path 1 — rename after exporting. You export everything at once with --separate, and then rename each file to its readable name. This is what lesson 7's script is going to do on its own, reading the name field from inside each JSON and using it as the filename. By hand, for a few workflows, it's simply:

mv workflows/aBcD1234EfGh5678.json workflows/order-triage.json

(mv renames; the first name is the current one, the second the new one.)

Path 2 — export one at a time with a name. If there are few, you export each workflow with --id and --output pointing to the name you want, like you did with order-triage in lesson 2:

docker exec -u node -it n8n n8n export:workflow --id=aBcD1234EfGh5678 --output=workflows/order-triage.json --pretty

For two or three workflows, path 2 is comfortable. For an instance with twenty, automated path 1 is the only sensible one. That's why lesson 7 exists: to turn renaming into something that happens on its own.

The naming convention

Deciding how files get named seems trivial until you have forty and none match the others. Adopt a convention and stick to it with no exceptions. The one I recommend, and the most common in the ecosystem:

  • kebab-case: all lowercase, words separated by hyphens. order-triage, not OrderTriage nor order_triage nor Order Triage. It's easy to type in a terminal (no uppercase or spaces to complicate things), it reads well, and it's the de facto standard for filenames in software projects.
  • The filename mirrors the workflow's name in n8n. If the workflow is called order-triage inside n8n, the file is order-triage.json. That one-to-one correspondence eliminates the question "which file is this workflow?": the name says it.
  • In English, like all the code. Even though your documentation's prose is in Spanish, filenames, just like identifiers and JSON keys, go in English. It's the convention across the whole tech ecosystem and what any developer opening the repo expects.
  • No spaces, no accents, no odd characters. A space in a filename is a source of pain in the terminal and in scripts. weekly-report.json, never weekly report.json.

The rule behind the rule: a filename is an address. The more predictable it is, the less whoever's looking for it has to think. If everyone follows kebab-case and mirrors the workflow's name, anyone can guess a workflow's filename without looking at the folder. That guessing ability is what feels like "the repo navigates itself."

Mapping n8n's organization onto the repository

Inside n8n, your workflows don't live in a flat pile: n8n offers ways to organize them, and it's worth having the repository reflect that same organization, so the mental structure is one and the same on both sides.

n8n organizes workflows mainly with tags: words you attach to a workflow to group it —sales, ops, finance, support. The same workflow can have several. In recent versions, n8n also adds folders and projects to group workflows, though their exact availability depends on your version and plan (check yours). Tags are the option available in the Community edition, so they're what this guide uses as the baseline.

The practical question is: if in n8n order-triage and support-autoresponder both have the support tag, how does that show up in the repo? Two strategies:

Strategy A — folders by tag/domain. You mirror the tags as subfolders inside workflows/:

workflows/
├── sales/
│   └── order-triage.json
├── ops/
│   └── inventory-sync.json
├── finance/
│   └── weekly-report.json
└── support/
    └── support-autoresponder.json

It's the most navigable option once you have many workflows: the business domain shows up in the structure itself. The downside: a workflow with two tags doesn't fit into two folders at once, so you have to pick its "main" folder.

Strategy B — flat with a manifest. You leave all the workflows flat in workflows/ and keep a file —say workflows/INDEX.md— that lists each workflow with its tags:

| Workflow | Tags | Purpose |
|---|---|---|
| order-triage | sales, ai | Classifies incoming orders |
| inventory-sync | ops | Syncs inventory every hour |

It's simpler to maintain (no moving files between folders when tags change) and handles workflows with multiple tags well. The downside: the organization doesn't show up in the folder structure, you have to open the manifest.

Which to choose? The honest rule: for fewer than ten workflows, flat with a manifest (B) is simpler and enough. As the instance grows and domains multiply, folders by domain (A) start paying for their cost. Cumbre, with its four workflows, is comfortable flat. Don't over-organize a small repo: a folder hierarchy for four files is more ceremony than help.

What matters isn't which one you choose, but that the repo's organization and n8n's tell the same story. If in n8n you group by business domain, the repo should too; if in n8n you use flat tags, the repo should too. Incoherence between the two —tags on one side, folders by a different criterion on the other— is what confuses.

Worked example: setting up Cumbre's skeleton

Let's create the structure from scratch, with terminal commands. I'm assuming you're already inside cumbre-automations, the repository you put under Git in Module 2.

Step 1 — Create the folders.

mkdir -p workflows credentials docs scripts

mkdir creates folders —like clicking "New folder" on the desktop. The -p flag tells it "create all the ones that are missing and don't complain if one already exists," so you can run it without fear. What to expect: four new folders in the root. Check with ls.

Step 2 — Place the normalized workflows. If you exported them into workflows/ in the previous lessons, they're already there; just rename them to readable names (path 1 or 2 above). When done, workflows/ has the four files with names like order-triage.json.

Step 3 — Create the credentials inventory, without secrets. Inside credentials/, create a README.md that lists which credentials each workflow needs. No values:

# Required credentials

These credentials must be created on each n8n instance. The real values
do NOT live in this repo (see the security lesson). Only what's needed
is documented here.

| Workflow | Credential | Type | What for |
|---|---|---|---|
| order-triage | Cumbre CRM key | Header Auth | Reading customer data in the CRM |
| order-triage | Cumbre LLM key | (language model) | Classifying the order with the AI Agent |
| inventory-sync | Store API | Header Auth | Reading stock from the online store |

Step 4 — Create the .env.example. In the root, a file that documents which secret variables are needed, with the names but without the values:

# .env.example — copy this to .env and fill in YOUR values. .env is NOT uploaded (see .gitignore).
CRM_API_KEY=
LLM_API_KEY=
STORE_API_KEY=

What to expect: each variable has its name and an = with nothing after it. It's the mold: whoever clones the repo copies this file to .env, fills in their keys, and .gitignore (lesson 3) makes sure that real .env never gets uploaded.

Step 5 — Verify the secrets are still out. Before committing the new structure, lesson 3's reflex:

git status

Confirm that workflows/, credentials/, docs/, scripts/, .env.example, and the README show up, and that .env, credential exports, and the .n8n/ folder do not. If .gitignore is set right, the secrets are invisible.

With that, you have the skeleton. It's empty of detailed documentation —that's lesson 6— and of automation —that's 7— but the shape is already there, and that shape is what a developer recognizes as "a well-cared-for repo."

A detail that confuses people: Git doesn't track empty folders. If you create docs/ and it still has no files inside, Git acts as if the folder doesn't exist —Git versions files, not folders— and whoever clones the repo won't see it. The convention for forcing an empty folder to travel is to put a marker file inside, conventionally called .gitkeep:

touch docs/.gitkeep scripts/.gitkeep

(touch creates an empty file.) .gitkeep has no special meaning to Git; it's just any file whose sole job is to make the folder stop being empty so Git includes it. Once the folder has real content —your first docs/order-triage.md.gitkeep becomes unnecessary and you can delete it. It's a small trick that avoids the confusion of "I cloned the repo and the docs folder is missing."

A layout that grows toward environments

It's worth designing the skeleton with an eye on what's coming, so you don't have to redo it in Module 4. That module introduces Cumbre's three environments —dev, staging, prod— and the question that comes up is: does the workflow's logic change between environments? The answer, and it's one of the most important ideas in this whole guide, is no. order-triage is the same workflow across the three environments; what changes isn't its logic, but its configuration: which CRM it queries (a test one in dev, the real one in prod), which keys it uses, which AI model.

That has a direct consequence for the layout: workflows get versioned once, not one copy per environment. You're not going to have workflows-dev/, workflows-staging/, and workflows-prod/ with the same order-triage.json repeated three times —that would be a maintenance nightmare where you fix a bug in one place and forget the other two. You're going to have a single workflows/order-triage.json, and whatever differs by environment lives separately, in the configuration.

So the layout that grows toward environments looks roughly like this (the detail belongs to Module 4; I'm previewing it so today's structure fits tomorrow's):

cumbre-automations/
├── workflows/              ← ONE version of each workflow, shared by all 3 environments
├── environments/           ← what DOES change per environment (Module 4)
│   ├── dev/
│   ├── staging/
│   └── prod/
├── .env.example            ← the shared variable template
└── ...

The logic is shared (a single workflows/ folder); the configuration is per environment (one subfolder per environment). Separating "what's the same everywhere" from "what changes depending on where it runs" is the principle that organizes not just this repo, but any system deployed in more than one place. For now, don't create the environments/ folder —that's Module 4— just design workflows/ knowing it's going to be shared, not duplicated. One workflow, many environments.

The root README: the cover

The most important file in the whole repository isn't a workflow: it's the root README.md. It's the first thing anyone reads, and often the only thing they read before deciding whether the repo is useful to them. A good root README answers, in this order, five questions:

  1. What is this? One or two sentences: "Cumbre's n8n automation repository, versioned and documented."
  2. What's inside? The folder map, in three lines: workflows here, docs there, scripts over here.
  3. How do I get it running? The steps to bring a workflow up from scratch: copy .env.example to .env, create the credentials, import the workflows.
  4. How do I maintain it? How to export and update (points to lesson 7's script).
  5. Where are the secrets? The explicit clarification that credentials live outside the repo and how to get them.

A minimal skeleton that covers the five questions looks like this:

# cumbre-automations

Cumbre's n8n automations, versioned and documented.

## What's inside
- `workflows/` — the exported, normalized workflows
- `credentials/` — inventory of required credentials (no values)
- `docs/` — per-workflow handoff documentation
- `scripts/` — export and normalization tools

## Getting it running
1. Copy `.env.example` to `.env` and fill in your values.
2. Create in n8n the credentials listed in `credentials/README.md`.
3. Import the workflows: `n8n import:workflow --separate --input=./workflows`.

## Maintaining it
After changing a workflow in n8n, run `scripts/export.sh` to re-export,
normalize, and leave the repo ready to commit.

## The secrets
Credentials do NOT live in this repo. Request them through the team's secure channel.

It doesn't have to be long. It has to be enough for someone who never saw the repo to orient themselves without writing to you. Lesson 6 goes deeper into per-workflow documentation; the root README is the top level, the bird's-eye view.

Common mistakes

Piling everything in the root (practical). What happens: someone exports the workflows and leaves them loose in the repo's root, next to the README and config files, with no folders. With four files it's tolerable; with twenty it's chaos where you can't tell a workflow from a tool from a config file. Why it happens: creating folders feels like extra work when there are few files, and the problem doesn't hurt until there are already many. How to spot it: if your repo's root has more than six or seven files mixing different categories, it's missing structure. How to fix it: group by the four categories —content, dependencies, docs, tools— from the start, even if each folder has only one file. It's easier to be born organized than to organize yourself later.

Over-organizing a small repo (practical). What happens: the opposite of the previous one, someone creates a five-level folder hierarchy by domain, subdomain, and type, for four workflows. Now finding order-triage requires opening four folders. Why it happens: a large project's structure gets copied without scaling it to the real size. How to spot it: if you have more folders than workflows, or reaching a file takes more than two clicks, you're over-organizing. How to fix it: structure should grow with content, not get ahead of it. For Cumbre, flat inside workflows/ is right; subfolders by domain arrive when workflows are counted in dozens.

Leaving id names as filenames (practical). What happens: someone exports with --separate, sees the files named aBcD1234.json, Xy9Z00Kw.json, and commits them as is. Now the repo is unreadable: to know what each file is, you have to open it. Why it happens: renaming four files by hand feels tedious and gets postponed. How to spot it: if your filenames are random strings instead of workflow names, this is it. How to fix it: rename them to kebab-case mirroring the workflow's name, by hand if there are few or with lesson 7's script if there are many. The id is still inside the file; you don't need it in the name too.

Putting a credential value in credentials/ "because that's what the folder is for" (conceptual, and dangerous). What happens: someone sees the credentials/ folder and reasons that's where credentials go, values and all. Why it happens: the folder's name invites that reading. How to spot it: if credentials/ has a file with a real key (sk-..., Bearer ..., a password), you have a secret in the repo. How to fix it: credentials/ contains documentation about the credentials —which types are needed, for what— never their values. Values live outside the repo, as lesson 3 taught. The folder's name describes the topic; it doesn't authorize secret content.

Exercises

Exercise 1 — Place each file. You have these six freshly exported or created files. Say which folder (or whether the root) each one goes into inside cumbre-automations, and why: (a) normalized order-triage.json; (b) export.sh; (c) a document explaining weekly-report's trigger and dependencies; (d) .env with the CRM's real key; (e) .env.example; (f) an inventory of which credentials each workflow needs.

See solution

(a) workflows/order-triage.json — it's content: the versioned logic. (b) scripts/export.sh — it's a maintenance tool. (c) docs/weekly-report.md — it's a workflow's handoff documentation. (d) Nowhere in the repo. .env with real values is a secret; it lives outside the repo and .gitignore ignores it. It doesn't go into any versioned folder. (e) .env.example, in the root — it's the valueless template, and it goes up top because it's the first thing anyone needs when configuring. (f) credentials/README.md — it's documentation about which credentials are needed, without their values.

Why it works: five of the six fall into one of the four categories —content, tools, docs, dependencies— and the sixth (d) is the exception that proves the security rule: the secret has no place in the repo. If you placed all six, you already have the layout's mental map.

Exercise 2 — Choose the organization strategy. For each scenario, say whether you'd use folders by domain (strategy A) or flat with a manifest (strategy B), and why: (a) Cumbre's instance with its four workflows; (b) an agency with sixty workflows spread across eight clients; (c) a personal project with two workflows.

See solution

(a) Flat with a manifest (B). Four workflows don't justify a folder hierarchy; an INDEX.md with their tags is more than enough. Putting four files into four subfolders is ceremony without benefit.

(b) Folders by domain (A). With sixty workflows and eight clients, the folder structure —for example workflows/client-a/, workflows/client-b/— is what makes the repo navigable. Here the hierarchy pays for itself many times over.

(c) Flat (B), or almost nothing. With two workflows, you don't even need a manifest: the filenames already say it all. Don't invent organization where it isn't needed.

Why it works: the decision isn't about taste, it's about scale. Structure should be proportional to the size of the content. The classic mistake is applying (b)'s structure to a repo the size of (a) or (c), and ending up with more folders than files. The rule: organize when the lack of organization starts to hurt, not before.

Exercise 3 — Write the root README. Write cumbre-automations's root README.md, answering the five questions from "The cover" section in no more than one screen. When you're done, give it to someone who doesn't know the project (or read it yourself imagining you're seeing it for the first time) and ask yourself: could this person get a workflow running without writing to me?

See solution

There's no single correct answer, but a good Cumbre README covers, in this order: what it is (Cumbre's n8n automations, versioned and documented); what's inside (workflows in workflows/, docs in docs/, tools in scripts/, credentials documented in credentials/); how to get it running (copy .env.example to .env, create the credentials listed in credentials/README.md, import the workflows with n8n import:workflow); how to maintain it (run scripts/export.sh after every change); and where the secrets are (outside the repo; request them through the team's secure channel).

The real test is the last question: if your README leaves a stranger able to start a workflow without help, it does its job. If they had to guess something, there's the gap to fill. That standard —"someone else can, without me"— is what this entire module is chasing, and the root README is its first line of defense.

Summary and next step

In this lesson you gave cumbre-automations shape. You saw that a repository is a message: its top-level folders tell another developer, in half a second, whether this was built by someone who thinks about whoever comes next. You built the four-category layout —content in workflows/, documented dependencies in credentials/ and .env.example, documentation in docs/ and the README, tools in scripts/— with the security warning repeated: credentials/ holds the inventory of which credentials are needed, never their values. You swapped ugly id names for readable kebab-case names that mirror the workflow's name, adopted the naming convention, and learned to map n8n's organization —tags, folders— onto the repo with two strategies (folders by domain or flat with a manifest), choosing based on scale, not taste. And you saw that the root README is the cover that answers the five orientation questions.

Before moving on you should be able to: draw cumbre-automations's layout from memory and say what goes in each folder; explain why a credential value doesn't go in credentials/; name the filename convention and why; and say when folders by domain make sense versus flat with a manifest.

Lesson 6 fills the skeleton with real documentation. A well-structured repo tells you where everything is, but it still doesn't say what each workflow does or how it works. You're going to write the per-workflow README —purpose, trigger, dependencies, required credentials, variables, and node diagram—, you're going to use the sticky notes inside the canvas that travel within the JSON itself, and you're going to aim for the standard handoff offers demand: "documentation good enough for another developer to pick it up."

Resources