@tpsdev-ai/flair 0.26.0 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,197 @@
1
+ # Flair integrations
2
+
3
+ Where Flair already runs. Each integration shown here is a working surface — the same memory, federated across all of them, scoped per-agent by Ed25519 keys.
4
+
5
+ > **The point.** Memory should follow the agent across orchestrators. Every entry below pulls from the same Flair instance, sees the same `agentId` namespace, respects the same isolation. Pick whichever harness you're shipping in; the memory layer doesn't care.
6
+
7
+ ---
8
+
9
+ ## Quick install matrix
10
+
11
+ | Surface | Install path | Auth | Notes |
12
+ |---------|--------------|------|-------|
13
+ | **Claude Code** | [`flair-mcp`](#claude-code-cursor-codex-gemini-cli-continuedev-via-flair-mcp) | MCP config | Standard MCP server |
14
+ | **Cursor** | [`flair-mcp`](#claude-code-cursor-codex-gemini-cli-continuedev-via-flair-mcp) | MCP config | Standard MCP server |
15
+ | **Continue.dev** | [`flair-mcp`](#claude-code-cursor-codex-gemini-cli-continuedev-via-flair-mcp) | MCP config | Standard MCP server |
16
+ | **OpenAI Codex CLI** | [`flair-mcp`](#claude-code-cursor-codex-gemini-cli-continuedev-via-flair-mcp) | MCP config | Standard MCP server |
17
+ | **Gemini CLI** | [`flair-mcp`](#claude-code-cursor-codex-gemini-cli-continuedev-via-flair-mcp) | MCP config | Standard MCP server |
18
+ | **Goose** (block/goose) | [`flair-mcp`](#claude-code-cursor-codex-gemini-cli-continuedev-via-flair-mcp) | MCP config | Goose ships native MCP support |
19
+ | **LangGraph (TS)** | [`langgraph-flair`](#langgraph-typescript) | FlairClient | Drop-in `BaseStore` |
20
+ | **OpenClaw** | [`openclaw-flair`](#openclaw) | Ed25519 | Native plugin + context engine |
21
+ | **n8n** | [`n8n-nodes-flair`](#n8n) | FlairApi credential | Three nodes (chat memory, search, store) |
22
+ | **Hermes Agent** | [`hermes-flair`](#hermes-agent) | Ed25519 | Python `MemoryProvider` |
23
+ | **Pi agent** | [`pi-flair`](#pi-agent) | Ed25519 | TS plugin |
24
+
25
+ Don't see your harness? If it speaks **MCP** — Flair already works with `flair-mcp`. If it has a **custom memory protocol** like LangGraph's `BaseStore` or CrewAI's `RAGStorage`, an adapter is a ~200-line package; [open an issue](https://github.com/tpsdev-ai/flair/issues) or [send a PR](https://github.com/tpsdev-ai/flair).
26
+
27
+ **Adjacent: memory bridges** — for moving memories between Flair and another memory product. Five bridges ship today (Mem0, ChatGPT exports, claude-project files, agentic-stack, markdown); see [bridges.md](bridges.md). Bridges are import/export plumbing, not live orchestrator integrations.
28
+
29
+ ---
30
+
31
+ ## Claude Code, Cursor, Codex, Gemini CLI, Continue.dev, Goose — via `flair-mcp`
32
+
33
+ [`@tpsdev-ai/flair-mcp`](https://www.npmjs.com/package/@tpsdev-ai/flair-mcp) is a [Model Context Protocol](https://modelcontextprotocol.io/) server that exposes Flair as a memory tool to any MCP-speaking client. One server, every MCP client.
34
+
35
+ No install step needed — every snippet below uses `npx -y @tpsdev-ai/flair-mcp`, which fetches and runs the server on demand (zero-install). The fastest path is `flair init`, which detects and wires these clients for you. To wire by hand, drop the relevant snippet into each tool's MCP config:
36
+
37
+ **Claude Code** (`~/.config/claude-code/config.toml` or per-project `.claude/config.toml`):
38
+ ```toml
39
+ [mcp.servers.flair]
40
+ command = "npx"
41
+ args = ["-y", "@tpsdev-ai/flair-mcp"]
42
+ env = { FLAIR_AGENT_ID = "claude-code" }
43
+ ```
44
+
45
+ **Cursor** (`~/.cursor/mcp.json`):
46
+ ```json
47
+ {
48
+ "mcpServers": {
49
+ "flair": {
50
+ "command": "npx",
51
+ "args": ["-y", "@tpsdev-ai/flair-mcp"],
52
+ "env": { "FLAIR_AGENT_ID": "cursor" }
53
+ }
54
+ }
55
+ }
56
+ ```
57
+
58
+ **Codex CLI** (`~/.codex/config.toml`):
59
+ ```toml
60
+ [mcp_servers.flair]
61
+ command = "npx"
62
+ args = ["-y", "@tpsdev-ai/flair-mcp"]
63
+
64
+ [mcp_servers.flair.env]
65
+ FLAIR_AGENT_ID = "codex"
66
+ ```
67
+
68
+ **Gemini CLI** (`~/.gemini/settings.json`): same shape as Cursor.
69
+
70
+ **Continue.dev** (`~/.continue/config.json`):
71
+ ```json
72
+ {
73
+ "experimental": {
74
+ "modelContextProtocolServer": {
75
+ "command": "npx",
76
+ "args": ["-y", "@tpsdev-ai/flair-mcp"],
77
+ "env": { "FLAIR_AGENT_ID": "continue" }
78
+ }
79
+ }
80
+ }
81
+ ```
82
+
83
+ **Goose** (`~/.config/goose/profiles.yaml`):
84
+ ```yaml
85
+ default:
86
+ extensions:
87
+ flair:
88
+ cmd: npx
89
+ args: ["-y", "@tpsdev-ai/flair-mcp"]
90
+ envs: { FLAIR_AGENT_ID: goose }
91
+ ```
92
+
93
+ **Auth.** Set `FLAIR_AGENT_ID` to whatever identifier you want this client to claim. The MCP server will prompt you to register that agent on first call (`flair agent add <id>` writes the Ed25519 keypair). Subsequent calls auto-load the key.
94
+
95
+ Full per-tool walkthrough including troubleshooting: [`docs/mcp-clients.md`](mcp-clients.md).
96
+
97
+ ---
98
+
99
+ ## LangGraph (TypeScript)
100
+
101
+ [`@tpsdev-ai/langgraph-flair`](https://www.npmjs.com/package/@tpsdev-ai/langgraph-flair) implements LangGraph's `BaseStore`. Drop-in for `InMemoryStore`.
102
+
103
+ ```bash
104
+ npm install @tpsdev-ai/langgraph-flair
105
+ ```
106
+
107
+ ```typescript
108
+ import { FlairStore } from "@tpsdev-ai/langgraph-flair";
109
+ import { createReactAgent } from "@langchain/langgraph/prebuilt";
110
+
111
+ const store = new FlairStore({ agentId: "my-langgraph-agent" });
112
+ const agent = createReactAgent({ llm, tools, store });
113
+ ```
114
+
115
+ Maps LangGraph namespaces to Flair tags, keys to ids, values to JSON content. Search delegates to Flair's HNSW. Filter operators applied client-side. Full mapping table: [`packages/langgraph-flair/README.md`](../packages/langgraph-flair/README.md).
116
+
117
+ LangGraph **Python** support is on the roadmap (same `BaseStore` shape, Python adapter).
118
+
119
+ ---
120
+
121
+ ## OpenClaw
122
+
123
+ [`@tpsdev-ai/openclaw-flair`](https://www.npmjs.com/package/@tpsdev-ai/openclaw-flair) is the native OpenClaw plugin. Adds Flair as a memory provider AND registers the `flair` context engine that re-injects PERMANENT-tier rules (SOUL.md, IDENTITY.md, AGENTS.md) every turn.
124
+
125
+ ```bash
126
+ openclaw plugins install @tpsdev-ai/openclaw-flair
127
+ ```
128
+
129
+ Configuration via OpenClaw's standard plugin surface. See [`docs/openclaw.md`](openclaw.md) for the per-agent install pattern, including how to wire SOUL.md so behavioral anchors persist across long sessions without drift.
130
+
131
+ ---
132
+
133
+ ## n8n
134
+
135
+ [`@tpsdev-ai/n8n-nodes-flair`](https://www.npmjs.com/package/@tpsdev-ai/n8n-nodes-flair) ships three nodes:
136
+
137
+ - **FlairChatMemory** — drop-in chat-memory for n8n's AI Agent / LangChain workflow nodes. Same role as Postgres / Redis chat memory but with cross-orchestrator portability.
138
+ - **FlairSearch** — semantic search over your Flair memories from any workflow.
139
+ - **FlairApi** credential — Ed25519 keypair entry point for the agentId.
140
+
141
+ Install via the standard n8n community-node UI (Settings → Community nodes → `@tpsdev-ai/n8n-nodes-flair`) or:
142
+
143
+ ```bash
144
+ cd ~/.n8n && npm install @tpsdev-ai/n8n-nodes-flair
145
+ ```
146
+
147
+ Walkthrough including a worked example flow: [`docs/n8n.md`](n8n.md).
148
+
149
+ ---
150
+
151
+ ## Hermes Agent
152
+
153
+ [`hermes-flair`](https://github.com/tpsdev-ai/flair/tree/main/packages/hermes-flair) implements Nous Research [Hermes](https://github.com/NousResearch/hermes-agent)'s `MemoryProvider` plugin contract in Python. Bootstrap injection at session start, background prefetch between turns, two tools (`flair_search`, `flair_store`), built-in MEMORY.md mirroring, circuit breaker.
154
+
155
+ ```bash
156
+ hermes plugins install path:/path/to/flair/packages/hermes-flair
157
+ ```
158
+
159
+ Auth: TPS-Ed25519 (the same model the rest of Flair uses) — writes are isolated per agent identity server-side; reads are open within the org for non-private memories, with `visibility: private` staying owner-only. See [SECURITY.md](../SECURITY.md).
160
+
161
+ ---
162
+
163
+ ## Pi agent
164
+
165
+ [`@tpsdev-ai/pi-flair`](https://www.npmjs.com/package/@tpsdev-ai/pi-flair) is the TS plugin for the [Pi coding agent](https://github.com/mariozechner/pi-coding-agent). Memory + identity for the Pi runtime.
166
+
167
+ ```bash
168
+ npm install @tpsdev-ai/pi-flair
169
+ ```
170
+
171
+ Pi resolves the plugin via its standard plugin config; pin `agentId` per host.
172
+
173
+ ---
174
+
175
+ ## Don't see your harness?
176
+
177
+ If it speaks MCP, you're already covered — every MCP client works through `flair-mcp` (the section above lists 6 we've explicitly tested).
178
+
179
+ If it has a custom memory protocol, the adapter pattern is small (~200 lines). LangGraph and Hermes are the reference implementations. **Adapters we'd love to see:**
180
+
181
+ - LangGraph Python (mirror of our TS adapter)
182
+ - CrewAI (Python `BaseRAGStorage` protocol)
183
+ - AG2 / AutoGen (Python)
184
+ - Mastra (TS, denser thread model)
185
+ - ADK (Google, Python + TS)
186
+
187
+ [Open an issue](https://github.com/tpsdev-ai/flair/issues) describing the harness and we'll triage. PRs welcome — see [`packages/langgraph-flair`](../packages/langgraph-flair) as the smallest-shape reference.
188
+
189
+ ---
190
+
191
+ ## See also
192
+
193
+ - [Quickstart](quickstart.md) — `flair init` to working memory in 30 seconds
194
+ - [Memory bridges](bridges.md) — import/export Flair ↔ Mem0, ChatGPT, claude-project, markdown, agentic-stack (five bridges shipped)
195
+ - [Federation](federation.md) — pair instances peer-to-peer for cross-machine sync
196
+ - [Supply-chain policy](supply-chain-policy.md) — what we do to keep this list of integrations safe
197
+ - [The team](the-team.md) — the multi-agent rig that builds Flair, dogfooded on every harness above
@@ -0,0 +1,240 @@
1
+ # Flair via MCP — Claude Code, Gemini CLI, OpenAI Codex CLI
2
+
3
+ Flair ships an MCP server (`@tpsdev-ai/flair-mcp`) that any MCP-compatible client can use as its persistent memory + identity layer. One server, three (and counting) integrations. Switch between agent CLIs without losing your agent's memory.
4
+
5
+ This page is the install + config snippet for each of the three major CLIs. The bootstrap is the same:
6
+
7
+ 1. Install Flair and create an agent identity (one-time, ~2 min).
8
+ 2. Add the MCP server to your CLI of choice (1 command or 1 file).
9
+ 3. Verify the agent can call `memory_search` / `memory_store`.
10
+
11
+ If you've never set up Flair before, do step 1 first. If Flair is already running and you have an agent ID, jump to your CLI section.
12
+
13
+ ---
14
+
15
+ ## Step 1 — Install Flair (do once)
16
+
17
+ ```bash
18
+ # Install Flair globally
19
+ npm install -g @tpsdev-ai/flair
20
+
21
+ # Initialize the local Harper-backed server
22
+ flair init
23
+
24
+ # Provision an agent identity. Pick a name — typically per-project, per-purpose,
25
+ # or "me" if you want one durable identity across everything.
26
+ flair agent add my-project
27
+ # → writes ~/.flair/keys/my-project.key (Ed25519 PKCS8) and registers the agent
28
+
29
+ # Sanity check
30
+ flair status
31
+ ```
32
+
33
+ Flair runs as a local server at `http://127.0.0.1:19926` by default. The MCP server connects to it on demand via Ed25519-signed requests; nothing leaves your machine unless you explicitly route to a remote Flair instance.
34
+
35
+ ---
36
+
37
+ ## Step 2 — Wire the MCP server into your CLI
38
+
39
+ Pick whichever you use. The MCP server is the same package; only the config syntax differs.
40
+
41
+ ### Claude Code
42
+
43
+ The canonical approach is the `claude mcp add` CLI (writes to `~/.claude/mcp.json`):
44
+
45
+ ```bash
46
+ claude mcp add flair --scope user \
47
+ -e FLAIR_AGENT_ID=my-project \
48
+ -- npx -y @tpsdev-ai/flair-mcp
49
+ ```
50
+
51
+ Verify:
52
+
53
+ ```bash
54
+ claude mcp list
55
+ # → flair (stdio, npx -y @tpsdev-ai/flair-mcp)
56
+ ```
57
+
58
+ Or, if you prefer the project-scoped `.mcp.json` checked into your repo:
59
+
60
+ ```json
61
+ {
62
+ "mcpServers": {
63
+ "flair": {
64
+ "command": "npx",
65
+ "args": ["-y", "@tpsdev-ai/flair-mcp"],
66
+ "env": {
67
+ "FLAIR_AGENT_ID": "my-project"
68
+ }
69
+ }
70
+ }
71
+ }
72
+ ```
73
+
74
+ #### Auto-recall on session start (optional hook)
75
+
76
+ The MCP server gives the agent *pull* access to memory — it calls `bootstrap` /
77
+ `memory_search` when it decides to. If you'd rather have Flair context loaded
78
+ automatically the moment a session opens (no "call the bootstrap tool" nudge),
79
+ register Flair's `SessionStart` hook. It's a separate bin shipped in the same
80
+ package and is entirely optional — it complements the MCP server, it doesn't
81
+ replace it.
82
+
83
+ The one-command way (recommended — idempotent, `--dry-run`-able, and
84
+ symmetric with `flair hook uninstall`/`flair hook status`):
85
+
86
+ ```bash
87
+ flair hook install # wires ~/.claude/settings.json for FLAIR_AGENT_ID/FLAIR_URL
88
+ flair hook install --dry-run # prints the exact JSON delta, writes nothing
89
+ flair hook status # wired? correct shape? which agent/instance?
90
+ flair hook uninstall # removes only Flair's hook entry
91
+ ```
92
+
93
+ `--harness claude-code` is the only supported value today (it's also the
94
+ default) — the flag exists so a future harness is an additive registry entry,
95
+ not a breaking change. `flair doctor` already checks for this same hook (see
96
+ below) and recognizes anything `flair hook install` writes.
97
+
98
+ Or wire it by hand — add a `SessionStart` hook to `~/.claude/settings.json`:
99
+
100
+ ```json
101
+ {
102
+ "hooks": {
103
+ "SessionStart": [
104
+ {
105
+ "hooks": [
106
+ {
107
+ "type": "command",
108
+ "command": "FLAIR_AGENT_ID=me npx -y @tpsdev-ai/flair-mcp flair-session-start"
109
+ }
110
+ ]
111
+ }
112
+ ]
113
+ }
114
+ }
115
+ ```
116
+
117
+ Swap `me` for your `FLAIR_AGENT_ID`. The hook reads Claude Code's SessionStart
118
+ payload on stdin, calls Flair's `bootstrap` (soul + relevant memories +
119
+ predicted context, scoped to your project by the session's working directory),
120
+ and emits it as `hookSpecificOutput.additionalContext` — which Claude Code
121
+ injects into the new session's context. The matcher is omitted, so it fires on
122
+ every session start (`startup`, `resume`, `clear`, `compact`); add
123
+ `"matcher": "startup"` to a hook group if you only want it on fresh sessions.
124
+
125
+ It honors the same env as the MCP server (`FLAIR_AGENT_ID`, `FLAIR_URL`,
126
+ `FLAIR_KEY_PATH`), plus `FLAIR_HOOK_TIMEOUT_MS` (default 8000, clamped
127
+ 500–30000) for the bootstrap timeout.
128
+
129
+ **It degrades to a no-op, always.** No `FLAIR_AGENT_ID`, Flair down, an auth
130
+ error, or a hung daemon (past the timeout) → the hook prints `{}` and exits 0.
131
+ Claude Code treats that as "no context to add" and starts normally. The hook
132
+ can never block or break session startup. The injected context is clamped to
133
+ ≤10,000 characters to keep the session-start payload small.
134
+
135
+ ### Gemini CLI
136
+
137
+ Edit `~/.gemini/settings.json` (create it if absent):
138
+
139
+ ```json
140
+ {
141
+ "mcpServers": {
142
+ "flair": {
143
+ "command": "npx",
144
+ "args": ["-y", "@tpsdev-ai/flair-mcp"],
145
+ "env": {
146
+ "FLAIR_AGENT_ID": "my-project"
147
+ }
148
+ }
149
+ }
150
+ }
151
+ ```
152
+
153
+ Restart your Gemini CLI session for the config to take effect. Then in chat:
154
+
155
+ ```
156
+ > @flair memory_search "what did we decide about auth last week?"
157
+ ```
158
+
159
+ ### OpenAI Codex CLI
160
+
161
+ Edit `~/.codex/config.toml` (create it if absent):
162
+
163
+ ```toml
164
+ [mcp_servers.flair]
165
+ command = "npx"
166
+ args = ["-y", "@tpsdev-ai/flair-mcp"]
167
+
168
+ [mcp_servers.flair.env]
169
+ FLAIR_AGENT_ID = "my-project"
170
+ ```
171
+
172
+ For project-scoped trust (per Codex's MCP guide), the same block in `.codex/config.toml` at the project root.
173
+
174
+ Restart your Codex CLI session and the `flair_*` tools become available to the agent.
175
+
176
+ ---
177
+
178
+ ## Step 3 — Verify
179
+
180
+ In any of the three CLIs, ask the agent to do this:
181
+
182
+ > Use the bootstrap tool to load my Flair memory context, then store a memory that says "successful first MCP integration test."
183
+
184
+ If you see (a) the agent calling the `bootstrap` tool returning soul + recent memories, and (b) `memory_store` confirming a write — you're wired up. The memory now persists across CLI sessions AND across CLIs. Switch to a different CLI tomorrow and `memory_search "MCP integration test"` will find it.
185
+
186
+ ---
187
+
188
+ ## What the MCP server exposes
189
+
190
+ Eleven tools, kept deliberately small:
191
+
192
+ | Tool | What it does |
193
+ |---|---|
194
+ | `memory_search` | Semantic search across your agent's memories |
195
+ | `memory_store` | Save a memory with type, durability, tags. Auto-dedups near-duplicates |
196
+ | `memory_update` | Update an existing memory by ID — overwrite in place, or version it with `preserveHistory` |
197
+ | `memory_get` | Fetch a specific memory by ID |
198
+ | `memory_delete` | Remove a memory |
199
+ | `relationship_store` | Record a subject-predicate-object relationship triple (e.g. "nathan manages flair") |
200
+ | `bootstrap` | Get session-start context: soul + recent memories + predicted-relevant context |
201
+ | `soul_set` | Set a personality/project/standards entry — included in every bootstrap |
202
+ | `soul_get` | Get a soul entry |
203
+ | `flair_workspace_set` | Set your agent's current workspace state (ref/branch, phase, task) in the Office Space |
204
+ | `flair_orgevent` | Publish an org-wide coordination event (claim/release/status) to the Office Space |
205
+
206
+ Writes are scoped per-agent (your `FLAIR_AGENT_ID`) and enforced by Flair's server, not by client convention — you can't write as another agent. Reads are more open by design: any agent on the same Flair instance can read any other agent's non-private memories (open-within-org read; see [SECURITY.md](../SECURITY.md)). Mark a memory `visibility: private` to keep it owner-only.
207
+
208
+ ---
209
+
210
+ ## Configuration reference
211
+
212
+ | Env var | Default | Notes |
213
+ |---|---|---|
214
+ | `FLAIR_AGENT_ID` | (none — required) | Must match `flair agent add <id>` |
215
+ | `FLAIR_URL` | `http://127.0.0.1:19926` | Override for remote Flair instances |
216
+ | `FLAIR_KEY_PATH` | `~/.flair/keys/<agent>.key` | Ed25519 PKCS8 key — created by `flair agent add` |
217
+
218
+ The MCP server has no client-side flags beyond these env vars; everything else (timeouts, dedup thresholds, error classification) is opinionated defaults from the underlying [`@tpsdev-ai/flair-client`](../packages/flair-client) package.
219
+
220
+ ---
221
+
222
+ ## What about Hermes (Nous Research)?
223
+
224
+ Hermes uses its own Python-native `MemoryProvider` ABC instead of MCP. It has its own Flair integration in [`packages/hermes-flair/`](../packages/hermes-flair). Same backend, same agent isolation, different plug shape.
225
+
226
+ Future MCP-capable agent CLIs (and there are more landing every month) will work out of the box with the MCP server above — no per-framework adapter required from us.
227
+
228
+ ---
229
+
230
+ ## Troubleshooting
231
+
232
+ **"FLAIR_AGENT_ID is required" on startup.** Set it in the MCP server's `env` block (per snippets above). The CLI's own env doesn't propagate to the spawned MCP subprocess unless declared.
233
+
234
+ **"connection_error: could not reach Flair at http://127.0.0.1:19926".** The Flair server isn't running. Run `flair status` to check; `flair start` to bring it up.
235
+
236
+ **"auth_error: …" on every call.** The agent identity doesn't match a registered key. Re-run `flair agent add <id>` (idempotent on re-add — won't lose existing memories).
237
+
238
+ **Tool calls succeed but the agent doesn't see results in subsequent turns.** Check that the CLI is actually invoking `bootstrap` at session start — most CLIs need an explicit prompt nudge ("call the bootstrap tool now") on first use. Subsequent turns should pick up automatically once the CLI sees the schema.
239
+
240
+ For deeper issues see [`troubleshooting.md`](troubleshooting.md) and the [`@tpsdev-ai/flair-mcp` repo](https://github.com/tpsdev-ai/flair/tree/main/packages/flair-mcp).
@@ -0,0 +1,142 @@
1
+ # n8n Workflow Management
2
+
3
+ Programmatic activation/deactivation of n8n workflows — the approach we use for ops automation.
4
+
5
+ ## The Problem
6
+
7
+ Direct SQLite edits to `workflow_entity.active` (or using `n8n update:workflow` while n8n is running) don't re-register schedule triggers in the in-memory scheduler. The schedule simply stops firing. Only the v1 public API's `POST /api/v1/workflows/:id/activate` (or flipping Active in the UI) properly registers the trigger.
8
+
9
+ **Root cause**: n8n tracks activation via `activeVersionId`, not the `active` boolean alone. The `activeWorkflowManager` in-memory scheduler only picks up workflows where `activeVersionId IS NOT NULL`. Direct SQLite edits that only set `active=1` without setting `activeVersionId` will not be picked up even on restart.
10
+
11
+ ## Recommended Approach: v1 Public API (live instance)
12
+
13
+ The **v1 public API** is the correct tool for managing workflows on a **running** n8n instance. It updates the DB AND calls the in-memory `ActiveWorkflowManager.add()` to register triggers immediately — no restart needed.
14
+
15
+ ### Endpoints
16
+
17
+ | Action | Method | Endpoint | Body |
18
+ |---|---|---|---|
19
+ | Activate | `POST` | `/api/v1/workflows/:id/activate` | `{}` or `{"versionId": "..."}` |
20
+ | Deactivate | `POST` | `/api/v1/workflows/:id/deactivate` | (none) |
21
+ | List | `GET` | `/api/v1/workflows` | — |
22
+ | Get one | `GET` | `/api/v1/workflows/:id` | — |
23
+
24
+ ### Authentication
25
+
26
+ All v1 API calls require `X-N8N-API-KEY` header with a valid API key (see [API Key Generation](#api-key-generation) below).
27
+
28
+ ### Examples
29
+
30
+ ```bash
31
+ # Activate workflow
32
+ curl -X POST "http://127.0.0.1:5678/api/v1/workflows/$WORKFLOW_ID/activate" \
33
+ -H "X-N8N-API-KEY: $N8N_API_KEY" \
34
+ -H "Content-Type: application/json" \
35
+ -d '{}'
36
+
37
+ # Deactivate workflow
38
+ curl -X POST "http://127.0.0.1:5678/api/v1/workflows/$WORKFLOW_ID/deactivate" \
39
+ -H "X-N8N-API-KEY: $N8N_API_KEY"
40
+
41
+ # Check if workflow is active
42
+ curl -s "http://127.0.0.1:5678/api/v1/workflows/$WORKFLOW_ID" \
43
+ -H "X-N8N-API-KEY: $N8N_API_KEY" | python3 -c "import json,sys; print(json.load(sys.stdin)['active'])"
44
+ ```
45
+
46
+ See `scripts/n8n/activate-workflow.sh` for a reusable wrapper.
47
+
48
+ ## Why PATCH Doesn't Work
49
+
50
+ n8n's v1 API docs mention `PATCH /api/v1/workflows/:id` with `{ "active": true }`. **This returns 405 Method Not Allowed on n8n 1.123.38.** The documented activation endpoints are `POST /api/v1/workflows/:id/activate` and `POST /api/v1/workflows/:id/deactivate`.
51
+
52
+ ## Alternative Approaches
53
+
54
+ ### Approach 2: CLI when n8n is stopped
55
+
56
+ The `n8n update:workflow` CLI command works when n8n is **not running**. It updates both `active` and `activeVersionId` in the DB. On the next `n8n start`, `ActiveWorkflowManager.init()` reads `getAllActiveIds()` and registers all workflows with non-null `activeVersionId`.
57
+
58
+ ```bash
59
+ # Stop n8n
60
+ launchctl stop ai.tpsdev.n8n
61
+
62
+ # Activate/deactivate via CLI
63
+ N8N_PATH="/opt/homebrew/opt/node@24/bin" npx n8n@1.123.38 update:workflow --id=$WORKFLOW_ID --active=true
64
+ # or
65
+ N8N_PATH="/opt/homebrew/opt/node@24/bin" npx n8n@1.123.38 update:workflow --id=$WORKFLOW_ID --active=false
66
+
67
+ # Start n8n
68
+ launchctl start ai.tpsdev.n8n
69
+ ```
70
+
71
+ **Caveats**:
72
+ - `update:workflow` is deprecated (n8n v2 will replace it with `publish:workflow`/`unpublish:workflow`)
73
+ - Requires a full stop/start cycle (downtime for all workflows)
74
+ - `npx` needs node@24 (`/opt/homebrew/opt/node@24/bin/node`)
75
+
76
+ ### Approach 3: Internal REST API (editor UI)
77
+
78
+ The `/rest/workflows/:id/activate` endpoint is what the editor UI uses. It requires session-based auth (cookie from `/rest/login`), which needs the admin password. Not practical for automation without storing credentials.
79
+
80
+ ```bash
81
+ # Requires session cookie from /rest/login
82
+ curl -X POST "http://127.0.0.1:5678/rest/workflows/$WORKFLOW_ID/activate" \
83
+ -b "fern-uid=...; n8n.session=..."
84
+ ```
85
+
86
+ **Not recommended** for ops automation — session cookies expire and the password is a higher-value secret than an API key.
87
+
88
+ ## API Key Generation
89
+
90
+ The v1 API key is a JWT signed with a secret derived from the encryption key. To create one programmatically:
91
+
92
+ ```javascript
93
+ const crypto = require('crypto');
94
+ const jwt = require('jsonwebtoken');
95
+ const fs = require('fs');
96
+
97
+ // Read encryption key from n8n config
98
+ const config = JSON.parse(fs.readFileSync(process.env.HOME + '/.n8n/config', 'utf8'));
99
+ const encKey = config.encryptionKey;
100
+
101
+ // Derive JWT secret: SHA256 of every-other-character of encryption key
102
+ let baseKey = '';
103
+ for (let i = 0; i < encKey.length; i += 2) {
104
+ baseKey += encKey[i];
105
+ }
106
+ const jwtSecret = crypto.createHash('sha256').update(baseKey).digest('hex');
107
+
108
+ // Sign JWT with user's ID (from `user` table in SQLite)
109
+ const userId = '<user-uuid-from-sqlite>'; // e.g., 'a6ce7ac9-ef6d-4157-835a-4b0f01dc788a'
110
+ const apiKey = jwt.sign(
111
+ { sub: userId, iss: 'n8n', aud: 'public-api' },
112
+ jwtSecret
113
+ );
114
+
115
+ // Store the returned key in the user_api_keys table so it persists
116
+
117
+ console.log('API key:', apiKey);
118
+ ```
119
+
120
+ Once stored in the DB, the key is valid until deleted. No expiration by default.
121
+
122
+ **Security note**: The encryption key is needed to derive the JWT secret. This is the same key used to decrypt workflow credentials. If the encryption key is compromised, so are the credentials. Keep it scoped to the n8n user.
123
+
124
+ ## Why Direct SQLite Edits Don't Work
125
+
126
+ The `workflow_entity` table has two activation fields:
127
+ - `active` (boolean): UI flag
128
+ - `activeVersionId` (varchar): points to the `workflow_history` record that defines the active version
129
+
130
+ The `ActiveWorkflowManager` only considers workflows where `activeVersionId IS NOT NULL`. If you only flip `active=1` without setting `activeVersionId`, n8n never picks up the workflow — not on restart, not on refresh.
131
+
132
+ Even with both fields set correctly, a live n8n instance has an in-memory scheduler that doesn't re-read the DB. Only the v1 API (or UI toggle) calls `activeWorkflowManager.add()` to register the trigger in-memory.
133
+
134
+ ## Quick Reference
135
+
136
+ | Scenario | Method | Restart needed? |
137
+ |---|---|---|
138
+ | Activate on running n8n | `POST /api/v1/workflows/:id/activate` | No |
139
+ | Deactivate on running n8n | `POST /api/v1/workflows/:id/deactivate` | No |
140
+ | Activate when stopped | `n8n update:workflow --id=X --active=true` | Yes |
141
+ | Direct DB edit (active + activeVersionId) | SQLite `UPDATE` | Yes, on restart |
142
+ | Direct DB edit (active only) | SQLite `UPDATE` | **Won't work** — needs activeVersionId too |