# Approved patterns

> Page: https://docs.plotwork.dev/approved-patterns. Index of every page: https://docs.plotwork.dev/llms.txt

An approved pattern is a diagram your organisation drew once, the way it wants every team to draw it: the parts that must stay are fixed, and the parts each team names are slots. Agents connected to the organisation start from it.

## TL;DR

| Question | Answer |
|---|---|
| What is a pattern? | A diagram with fixed parts and named slots, in one or more flavours (neutral, AWS, Azure, Google Cloud), with a title, a summary and advice for agents. |
| Why have them? | So every team's diagram of the same thing has the same shape and the same guardrails, such as the firewall in front of every API. |
| What is fixed and what is a slot? | Fixed parts stay exactly as drawn. A slot is a label a team fills in, such as a service name, written `{{slot-id}}`. |
| Where are they made? | The organisation's **Patterns** tab, with **New pattern**. The owner and admins make them; every member reads them. |
| Do I have to write source? | No. Start from one of twelve built-in starters, or a diagram already in your library, then mark the labels teams rename. |
| How do agents use them? | `find_pattern` lists your patterns first, marked **Your organisation's pattern**. `draw_from_pattern` fills the slots with the agent's values. |
| Can agents be held to them? | **Agents start from approved patterns** on the Settings tab makes `find_pattern` answer with yours only. It guides, it does not block. |
| Can an agent write one? | Yes. A pattern is one JSON file. An agent writes it, checks it with `check_pattern`, and an owner's or admin's agent saves it with `publish_pattern`. See [Pattern files and agents](#pattern-files-and-agents). |
| Can I import or export one? | Yes. **A pattern file** on the Start step imports a `.json` file; every card and the form download one. |
| Which plan? | Enterprise. Up to 50 patterns. |

## What a pattern is

A pattern is Plotwork source with two kinds of part.

| Part | What it is | Example |
|---|---|---|
| Fixed part | Drawn exactly as saved, in every diagram made from the pattern | The web application firewall, the audit log, the segmentation firewall |
| Slot | A label each team fills in, written `{{slot-id}}` inside a quoted label | `{{service}}`, `{{database}}`, `{{region}}` |

Each slot has a default, which is drawn when an agent gives no value, and one line on what belongs there, which the agent reads. Every pattern has the slots `title` and `description`, so each diagram gets its own title and alt text.

## Fixed parts and slots, worked through

Your security team insists that every public web app sits behind the web application firewall and keeps its secrets in the vault. The team that owns the app names the app and its database. This is the pattern, drawn with its defaults:

**Diagram source**

```diagram
flow "Customer web app" direction=down desc="Customers reach the web app through the web application firewall. The app keeps its data in its database and reads its secrets from the vault."

node users client "Customers"
node waf gateway "Web application firewall" icon=lucide:shield
node app service "Web app"
node db database "App database"
node vault service "Secrets vault" icon=lucide:key-round

users -> waf
waf -> app
app -> db
app -> vault : "reads secrets" role=identity
```

*Picture: Customer web app: 5 nodes, 4 connections. Customers reach the web app through the web application firewall. The app keeps its data in its database and reads its secrets from the vault.*

**Text description**

**Nodes:**

- Customers, client
- Web application firewall, gateway
- Web app, service
- App database, database
- Secrets vault, service

**Connections:**

- Customers to Web application firewall
- Web application firewall to Web app
- Web app to App database
- Web app to Secrets vault (identity line): reads secrets

Its source marks the three labels a team renames, and leaves the firewall and the vault fixed:

```plotwork
flow "{{title}}" direction=down desc="{{description}}"

node users client "{{users}}"
node waf gateway "Web application firewall" icon=lucide:shield
node app service "{{app}}"
node db database "{{database}}"
node vault service "Secrets vault" icon=lucide:key-round
```

The payments team's agent calls `draw_from_pattern` with `app` set to `Payments API`, `database` to `Ledger` and `users` to `Shoppers`. The firewall and the vault come out as they were saved:

**Diagram source**

```diagram
flow "Payments API" direction=down desc="Shoppers reach the payments API through the web application firewall. The API keeps payments in the ledger and reads its secrets from the vault."

node users client "Shoppers"
node waf gateway "Web application firewall" icon=lucide:shield
node app service "Payments API"
node db database "Ledger"
node vault service "Secrets vault" icon=lucide:key-round

users -> waf
waf -> app
app -> db
app -> vault : "reads secrets" role=identity
```

*Picture: Payments API: 5 nodes, 4 connections. Shoppers reach the payments API through the web application firewall. The API keeps payments in the ledger and reads its secrets from the vault.*

**Text description**

**Nodes:**

- Shoppers, client
- Web application firewall, gateway
- Payments API, service
- Ledger, database
- Secrets vault, service

**Connections:**

- Shoppers to Web application firewall
- Web application firewall to Payments API
- Payments API to Ledger
- Payments API to Secrets vault (identity line): reads secrets

| Make it a slot when | Keep it fixed when |
|---|---|
| Each team has its own name for it: a service, a database, a region, an address range | Every team must draw it the same: a firewall, a gateway, an approval step, an audit log |
| Leaving it out still draws a sensible diagram with the default | Renaming it would hide a control an auditor looks for |

## Flavours and icons

A flavour is the same diagram drawn with one provider's icons. An agent that asks `draw_from_pattern` for `aws` gets the AWS flavour; one that leaves the flavour out gets the first one.

| Flavour | Drawn with |
|---|---|
| Neutral | Generic icons, for any provider or none |
| AWS | AWS icons, with `provider=aws` on the header |
| Azure | Azure icons, with `provider=azure` |
| Google Cloud | Google Cloud icons, with `provider=gcp` |

On the **Flavours** step, turning a provider on copies the first flavour with its header naming the provider. Each box is then listed with the icon it has in the first flavour, and a search of the icon catalog finds that provider's icon for it: a load balancer, a queue, a database. Every flavour uses the same slots, so an agent fills one list whichever it picks.

## How agents use them

| Step | What the agent does |
|---|---|
| 1 | Calls `find_pattern` with the request in plain words. Your patterns come first, marked **Your organisation's pattern**, with ids such as `org:payments-service`. |
| 2 | Calls `get_pattern` to read the slots, what each one means, and your advice on what to keep, rename and split. |
| 3 | Calls `draw_from_pattern` with the id, a flavour and a value for each slot it fills. A value that would break a label comes back as a problem with a hint, never a broken diagram. |

With **Agents start from approved patterns** on, `find_pattern` answers with your patterns only and tells the agent to start from them. When none fits, the agent draws from nothing as before.

## Create your first pattern

The **New pattern** button on the **Patterns** tab opens six steps. Save is on every step after the first; **Try it** is optional.

| Step | What you do |
|---|---|
| 1. Start | Choose a built-in starter, a diagram from your library, a pattern file, pasted source, or a blank one with two boxes. |
| 2. Source | Change the diagram in the editor. The preview draws every slot at its default, errors show with their line and hint, and the pattern check says when it is ready to save. Select words in a label and choose **Mark as slot** to make them a slot. |
| 3. Slots | Each slot is a row with its default and what an agent may put there. The fixed parts are listed under it; the plus beside one makes it a slot, and the bin beside a slot makes it fixed again. |
| 4. Flavours | Turn on the providers your teams use, and choose each box's icon for that provider. |
| 5. Details | The name (it becomes the id `org:` and the name, and never changes), the title, one sentence on what it shows, when to use it, tags, and your advice to agents. |
| 6. Try it | Fill the slots with sample values, pick a flavour, and draw what an agent would get. |

## Pattern files and agents

A pattern file is the whole pattern as JSON, saved as `<name>.pattern.json`. It is what an agent writes, what `check_pattern` checks, what `publish_pattern` saves, what the Patterns tab imports and downloads, and a valid body for `POST /orgs/{org}/patterns` as it is. Its JSON Schema, with a description of every field, is at [docs.plotwork.dev/schemas/plotwork.pattern.json](https://docs.plotwork.dev/schemas/plotwork.pattern.json); name it in `$schema` and an editor completes and checks the file.

```json
{
  "$schema": "https://docs.plotwork.dev/schemas/plotwork.pattern.json",
  "name": "customer-web-app",
  "title": "Customer web app",
  "summary": "Customers reach the web app through the web application firewall, and the app reads its secrets from the vault.",
  "whenToUse": "Start here for any public web app. For an internal tool, start from the internal app pattern.",
  "tags": [
    "web app",
    "waf",
    "vault"
  ],
  "templates": {
    "neutral": "flow \"{{title}}\" direction=down\n\nnode users client \"{{users}}\"\nnode waf gateway \"Web application firewall\"\nnode app service \"{{app}}\"\nnode vault service \"Secrets vault\"\n\nusers -> waf\nwaf -> app\napp -> vault : \"reads secrets\" role=identity"
  },
  "slots": [
    {
      "id": "title",
      "default": "Customer web app",
      "meaning": "The diagram title, which is also its accessible name."
    },
    {
      "id": "users",
      "default": "Customers",
      "meaning": "Who uses the app."
    },
    {
      "id": "app",
      "default": "Web app",
      "meaning": "The service the team runs."
    }
  ],
  "notes": {
    "keep": "Keep the firewall in front of the app and the vault beside it: security reviews look for both."
  }
}
```

| Field | What it holds |
|---|---|
| `name` | Lowercase words joined with hyphens. The id becomes `org:<name>`, and it never changes once saved. |
| `title`, `summary`, `whenToUse`, `tags` | The words `find_pattern` matches and shows. |
| `templates` | Plotwork source for each flavour (`neutral`, `aws`, `azure`, `gcp`), at least one. Fixed parts are plain text; each part a team fills in is a `{{slot}}` inside a quoted label. |
| `slots` | Every `{{slot}}` the templates use: its id, its default (up to 60 characters, 600 for `description`), and one line on what belongs there. |
| `notes` | What to keep, what to rename, and when to split, each optional. |

### How an agent makes one

| Step | Tool | What happens |
|---|---|---|
| 1 | `get_pattern` with `file: true` | Gives a built-in or an organisation pattern as a file to start from, fitted to an organisation's limits. Rename it. |
| 2 | `check_pattern` | Checks the file as a save checks it, with no network. Every problem comes back with its field and a fix; a valid file comes back with each flavour filled with its defaults. |
| 3 | `draw_diagram` | Draws each source `check_pattern` returned, so the agent fixes warnings and the person sees the picture. |
| 4 | `publish_pattern` | Saves the file to the organisation, on the hosted server. A name it has is refused unless `replace` is true, which the agent sets only after you say so. |

Without the hosted server, or without the right role, the agent saves the file as `<name>.pattern.json` for an owner or admin to import on the Patterns tab.

### Who can publish one

| Caller | `publish_pattern` |
|---|---|
| An owner or admin, with their own key or browser sign-in, naming the organisation (`organization`) | Saves it |
| An owner's or admin's agent connected to the organisation | Saves it |
| A member, a reader, or someone outside the organisation | Refused, with who can |
| An organisation key from the Keys tab | Refused: it acts for no one, and a pattern steers every agent of the organisation |
| An organisation not on Enterprise | Refused; keep the file to import later |

Every save is in the audit log as `orgs.pattern.create` or `orgs.pattern.update`, naming the person or the agent connection. With the REST API, send the file as it is: `curl -X POST https://api.plotwork.dev/orgs/<org>/patterns -H 'content-type: application/json' --data @customer-web-app.pattern.json` from a signed-in session.

## Starters

Each starter is a built-in pattern that `find_pattern` offers everyone. Choosing one copies it, with every flavour, into your form; the built-in one does not change. All twelve come in neutral, AWS, Azure and Google Cloud.

| Starter | Id | What it draws |
|---|---|---|
| Hub and spoke network | `hub-and-spoke-network` | The hub firewall every route passes, spokes that never connect directly |
| Zero trust access to private apps | `zero-trust-access` | The identity provider with MFA, the device posture check, the access proxy, logs to the SIEM |
| Three-tier web app behind a WAF | `web-app-behind-waf` | The CDN and the web application firewall in front of the load balancer |
| Event-driven pipeline | `event-driven-pipeline` | The event bus between services, the dead-letter queue |
| Data lake and analytics | `data-lake-analytics` | Raw and curated zones, the catalog |
| GitOps delivery to clusters | `gitops-kubernetes-delivery` | Build, test and scan, the promotion pull request, the approval before production |
| Multi-region failover | `multi-region-failover` | Global routing, the standby region with replicated data |
| Single sign-on and SCIM provisioning | `sso-scim-integration` | Sign-in through the identity provider, provisioning and deprovisioning, the audit log |
| Monitoring and observability stack | `observability-stack` | One OpenTelemetry collector, metrics, logs and traces, alerting to on-call |
| Segmented cardholder data environment | `pci-cardholder-data-environment` | The WAF, the segmentation firewalls, tokenisation, the card vault, key management, file integrity monitoring |
| Internal developer platform | `internal-developer-platform` | Golden paths, policy checks on infrastructure as code, secrets, cost reporting |
| Retrieval-augmented AI application | `ai-rag-application` | The AI gateway, input and output guardrails, the audit log of prompts and answers |

A built-in pattern can hold more than an organisation's 12 tags and 20 slots. A copy keeps the first 12 tags, and any slot past the twentieth becomes a fixed part with its default.

## Troubleshooting

| Problem | Fix |
|---|---|
| **Declare the slots it uses** | A `{{slot}}` in the source has no row. Open **Slots** and give it a default and a meaning, or remove the marker. |
| **No template uses this slot** | A slot is listed that no flavour uses. Make it fixed with the bin, or put `{{slot-id}}` in a label. |
| **The default is longer than 60 characters** | Shorten it: a default must fit a label. Only `description` may run to 600. |
| **Every flavour draws the same kind of diagram** | One flavour is a different kind, such as a sequence. Start that flavour again from the first one on **Flavours**. |
| **Mark as slot** does nothing | Select words inside one quoted label. A marker anywhere else could break the source. |
| No preview | The browser could not start the drawing engine. The pattern still saves, and the API checks it the same way. |
| **An organisation key from the Keys tab cannot change the organisation's patterns** | Connect the agent as yourself (your own key or a browser sign-in), as an owner or admin, and name the organisation. |
| **The organisation has a pattern of that name** | Change `name` for a new pattern, or let the agent call `publish_pattern` again with `replace: true` to change it. |
| An imported file opens with problems | Its shape was right and a rule was not, such as a slot no template uses. Each step marks the problems to fix; save when none are left. |
| An agent ignores the pattern | Check that it connects through the organisation's connection or one of its keys: a person's own connection does not see the organisation's patterns. |

## See also

| Doc | Why |
|---|---|
| [Retention, legal hold and approved patterns](https://docs.plotwork.dev/retention-and-patterns.md) | The policy that holds agents to your patterns, and what is recorded |
| [Architecture patterns](https://docs.plotwork.dev/architecture-patterns.md) | Every built-in pattern, with its slots and flavours |
| [Cloud architecture](https://docs.plotwork.dev/cloud-architecture.md) | Provider icons and group types |
| [MCP server](https://docs.plotwork.dev/mcp/overview.md) | `find_pattern`, `get_pattern`, `draw_from_pattern`, `check_pattern` and `publish_pattern` |
