Skip to content

feat: v1.7.0 — workflow archive / unarchive (reversible alternative to delete) - #3

Merged
trngthnh369 merged 1 commit into
mainfrom
feat/workflow-archive
Aug 14, 2026
Merged

trngthnh369 merged 1 commit into
mainfrom
feat/workflow-archive

Conversation

@trngthnh369

Copy link
Copy Markdown
Owner

Closes the gap the user hit: n8nctl could only delete workflows, permanently.

n8nctl workflow archive <id>     # reversible; forces the workflow inactive
n8nctl workflow unarchive <id>   # restores it, still inactive

Maps to the n8n public API POST /workflows/{id}/archive / /unarchive
(n8n-io/n8n#27513, merged 2026-03-27).
Both follow the existing activate.ts shape and support --dry-run.

The version gap this has to handle

The instance this was developed against (n8n 1.122.5) returns isArchived on the
workflow model but 404s on POST /workflows/{id}/archive — the field shipped before
the endpoints did. So a 404 here is ambiguous: missing workflow, or missing endpoint?

archiveWithVersionGuard resolves it on the error path only (no extra request on the
happy path):

Probe GET /workflows/{id} Meaning Result
404 (confirming) workflow genuinely gone rethrow the original 404
200 endpoint is what's absent upgrade hint citing #27513
anything else (401, 5xx, network) probe proves nothing surface that error

That last row came out of review: a bare catch would have reported "workflow not found —
verify the ID" for what is really an auth or connectivity fault, blaming the user for the
wrong thing.

unarchive --dry-run reuses the workflow it already fetched to flag the documented 400 when
the target is not archived, rather than reporting success for a call that cannot succeed.

Verified, not assumed

  • Live against n8npc 1.122.5: archive/unarchive 404 while GET returns 200 (checked at
    raw HTTP, confirming the guard's GET-200 branch actually fires); DELETE still hard-deletes
    there; --dry-run mutates nothing. Scratch workflow created and cleaned up — no production
    workflow was archived.
  • Source, not guesswork: WorkflowService.archive sets active=false; archiving an
    already-archived workflow is a 200 no-op (skipArchived); unarchiving a non-archived one
    throws BadRequestError → 400. The user-facing messages state what the code does.
  • 12 new tests (502 total), coverage gate green, npm audit --omit=dev clean, docs
    drift gate clean.

Deliberately out of scope

--json does not change these commands' output — they print a status line, exactly like
activate / deactivate / delete. Review suggested wiring printData in; I left it alone
because doing it for archive only would make it the odd one out among the mutation verbs.
Wiring --json through all of them is a worthwhile separate change.

Endpoint contracts + the version gap are recorded in scripts/SESSION_REST_CONTRACT.md.

Not tagged/published — v1.7.0 is bumped in package.json + CHANGELOG.md, but tagging
(which triggers the npm release) is left for you to decide.

🤖 Generated with Claude Code

…o delete

n8nctl could only delete workflows, permanently. n8n has had a reversible
archive since PR #27513 (n8n-io/n8n, merged 2026-03-27) — this wires it up:

  n8nctl workflow archive <id>     # reversible; forces the workflow inactive
  n8nctl workflow unarchive <id>   # restores it, still inactive

Both follow the activate.ts shape and support --dry-run. Like activate and
delete they print a status line rather than a JSON document, so --json does
not change their output; wiring --json through the mutation verbs is a
separate change and is deliberately not smuggled in here.

## The version gap this has to handle

The instance this was developed against (n8n 1.122.5) returns `isArchived` on
the workflow model but 404s on POST /workflows/{id}/archive — the field
shipped BEFORE the endpoints did. So a 404 here is ambiguous: missing
workflow, or missing endpoint?

`archiveWithVersionGuard` resolves that on the error path only (no extra
request on the happy path): re-GET the workflow, and only a CONFIRMING 404
proves it is gone. A GET 200 means the endpoint is what is absent, so the
user gets an upgrade hint instead of a bare "verify the ID". Any other probe
failure (401 mid-command, 5xx, retries exhausted) is surfaced as itself —
reporting "workflow not found" for an auth or connectivity fault would blame
the user for the wrong thing.

`unarchive --dry-run` reuses the workflow it already fetched to flag the
documented 400 when the target is not archived, rather than reporting success
for a call that cannot succeed.

## Verified, not assumed

- Live: archive/unarchive 404 on n8npc 1.122.5 while GET returns 200 (raw
  HTTP, confirming the guard's GET-200 branch actually fires); DELETE still
  hard-deletes there; dry-run mutates nothing. Scratch workflow cleaned up.
- Source: `WorkflowService.archive` sets active=false and archiving an
  already-archived workflow is a 200 no-op (skipArchived), while unarchiving a
  non-archived one throws BadRequestError → 400. The messages state what the
  code does, not what seemed likely.
- 12 new tests (502 total), coverage gate green, audit clean.

Endpoint contracts + the version gap recorded in SESSION_REST_CONTRACT.md.
@trngthnh369
trngthnh369 merged commit b43a4a9 into main Aug 14, 2026
7 checks passed
@trngthnh369
trngthnh369 deleted the feat/workflow-archive branch August 14, 2026 07:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant