Skip to content
Back to skills

Workhub Flow Map

ASecurity

Write or update a Workhub flow map (the Flow maps screen, ⌘7): a JSON diagram of one business flow across services, measured live from APM. Trigger: "flow map", "mapa de flujo", "map this flow for Workhub", "draw the flow of <feature>", "add a flow to Workhub".

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added November 5, 2026
developmentnodeaws

Security analysis

A100/100

Scanned November 5, 2026

npx -y skills add SoldierIced/workhub --skill workhub-flow-map --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Workhub Flow Map?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Workhub Flow Map
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/soldiericed-workhub-flow-map/badge)](https://www.skillsdirectory.com/skills/soldiericed-workhub-flow-map)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: workhub-flow-map
description: Write or update a Workhub flow map (the Flow maps screen, ⌘7): a JSON diagram of one business flow across services, measured live from APM. Trigger: "flow map", "mapa de flujo", "map this flow for Workhub", "draw the flow of <feature>", "add a flow to Workhub".
---

# Workhub flow map

A flow map is one JSON file Workhub draws as an architecture diagram and measures from the
environment's Elastic APM data (calls, p50, p95, failures, hour by hour). You write the file; you
never write code in Workhub for it. Full reference: `docs/user-guide/flow-maps.md` in the Workhub
repository; complete example: `docs/examples/flow-maps/checkout.json`.

## When to use

The user wants to see a flow (a payment, an onboarding, a batch) in Workhub, or wants timings per
hop of an existing flow, or asks to fix a map that Workhub lists as invalid.

## Steps

1. **Discover the flow from data, not from memory.** With read access to the APM traces index:
   - find one recent, successful entry transaction (`processor.event: transaction`,
     `service.name`, `transaction.name`);
   - fetch every document of its `trace.id`; link each transaction to the span that called it
     (`parent.id` = the calling span's `span.id`). Transactions are nodes, http/grpc spans are
     `sync` edges, their `span.destination.service.resource` is the edge's measure `destination`;
   - the trace stops at a message broker (the publish span is the last event). The consumer is a
     new trace: find it by a business id in its logs or by `transaction.name` of the consumer
     (`consume <topic>`, `job <name>`), and draw that hop as `async`; time spent in the queue
     is a `wait` edge;
   - services without an APM agent appear only as destinations: draw them as `external` nodes.
   Without data access, ask the user for the trace or the service list; do not invent names.

2. **Write the file.** Rules that keep it readable:
   - 3–6 lanes, top to bottom in flow order: the app or BFF, the orchestration, the domain
     services, then an `exception` lane for external systems and queues;
   - list nodes in flow order (columns are computed from the edges; an edge to an earlier node is
     drawn as a reply and does not move it);
   - one `measure` per node that serves an entry point (`apm.transaction`) and per hop whose
     caller has an APM agent (`apm.span`). Leave the measure out when you are not sure of the
     names: a wrong name shows as "could not read" and hides nothing else;
   - short labels (the node box is 170 px: about 22 monospace characters), the detail in `note`;
   - optional `stages` for a saga's steps, `phases` for the headers over the columns;
   - `environmentId` only when the map applies to one Health environment;
   - `group`: the heading the map is listed under (a domain, such as "Payments"); reuse the
     headings the folder already has;
   - `entry`: the transaction that starts the flow, so the screen can list its executions
     (traces). Leave it out when the leftmost measured node is the entry;
   - `alerts` when the user wants to be told: `{ "p95Ms": <about twice the entry's p95 over a
     day>, "errorRate": 0.05 }`, with `parts` for a stricter or looser part. Workhub checks the
     last hour every five minutes and notifies once an hour per part. A part with fewer than
     `minCalls` calls in the hour (default 20) never counts as over a limit; `"notify": false`
     keeps the limits on screen without notifying. The user can also edit them in
     **Settings → Alerts → Flow maps · limits**.
   - When you redraw a map that already exists, leave `alerts` out unless the user asked to change
     them: posting a map without `alerts` keeps the limits the stored map has. Send `alerts` to
     replace them.

3. **Install it.** Inside a Workhub session, post it to the control endpoint; the answer names the
   invalid fields if any:

   ```sh
   # macOS and Linux (WORKHUB_SOCKET):
   curl --unix-socket "$WORKHUB_SOCKET" -X POST http://workhub/v1/flow-maps \
     -H 'content-type: application/json' --data-binary @<map>.json
   # Windows (WORKHUB_URL and WORKHUB_TOKEN):
   curl -H "Authorization: Bearer $WORKHUB_TOKEN" -X POST "$WORKHUB_URL/v1/flow-maps" \
     -H 'content-type: application/json' --data-binary @<map>.json
   ```

   A posted map replaces the stored one with the same `id`, except that a map posted without
   `alerts` keeps the stored map's `alerts`.

   Outside a session, save it as `<data folder>/flow-maps/<id>.json` or hand the file to the user
   for **Settings → Flow maps → Import JSON…**. Then tell the user to open Flow maps (⌘7) → the
   environment → Reload.

4. **Check it.** Open it in Workhub (or ask the user to): the Coverage tab lists what is not
   measured, what APM returned nothing for and what failed. "No data" or "could not read" means a
   wrong `service`, `transaction` or `destination`: verify the exact value in APM (`terms`
   aggregation on the field, filtered by `service.name`) and fix the file. The Executions tab
   lists, for one real trace, the calls the map does not know about ("Seen in the trace, not in
   the map"): add the ones that matter. Posting the same `id` again replaces the file.

## Never

- Put secrets, customer data or anything from a log line in the file: only service, transaction
  and destination names.
- Guess a transaction name from the code: APM's name is what counts (route templates, not paths
  with ids).
- Change Workhub's source to add a map.

## Schema in short

```jsonc
{
  "version": 1,
  "id": "checkout", "name": "Checkout", "description": "…", "group": "Payments", "environmentId": "prod",
  "entry": { "type": "apm.transaction", "service": "shop-bff", "transaction": "POST /checkout" },
  "phases": [{ "label": "Checkout · sync", "fromColumn": 0, "toColumn": 2 }],
  "lanes": [{ "id": "app", "label": "01 / App" }, { "id": "ex", "label": "EX / External", "kind": "exception" }],
  "nodes": [{ "id": "bff", "lane": "app", "label": "shop-bff", "subtitle": "POST /checkout", "kind": "app",
              "measure": { "type": "apm.transaction", "service": "shop-bff", "transaction": "POST /checkout" } }],
  "edges": [{ "from": "bff", "to": "orders", "label": "place", "kind": "sync", "emphasis": true,
              "measure": { "type": "apm.span", "service": "shop-bff", "destination": "orders:8080" } },
            { "from": "events", "to": "fulfilment", "kind": "wait", "label": "queue" }],
  "stages": [{ "id": "paid", "label": "Paid" }], "stagesLane": "core",
  "alerts": { "p95Ms": 3000, "errorRate": 0.05, "parts": { "orders": { "p95Ms": 1500 } } }
}
```

Node kinds: `app`, `service`, `orchestrator`, `worker`, `queue`, `external`, `datastore`. Edge
kinds: `sync`, `async`, `wait`; `entry: "top"` enters the target from above when the side route
would cross a node.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.