← All Modules

assay.n8n

n8n public REST API (/api/v1) client. Client: n8n.client(url, {api_key="..."})api_key falls back to N8N_API_KEY, and travels in the X-N8N-API-KEY header. url is the instance base URL without /api/v1; override the API root with opts.api_path if the instance mounts it elsewhere.

Every collection is cursor-paginated. :list(opts?) returns the item array of the first page; :page(opts?) returns the raw {data, nextCursor} envelope; n8n.all(section, opts?) walks every page. All list methods accept limit (max 250) and cursor alongside their own filters.

Workflows

create/update pin an empty nodes to a JSON array and an absent connections/settings to a JSON object. An empty Lua table encodes as {}, which is correct for the latter two but is rejected by n8n for the node list.

Test Runs

Executions

Credentials

Tags, Variables, Projects, Folders, Users

Source Control, Audit

Data Tables

Instance Administration

Module Helpers

Idempotent reconcilers. Each is safe to run repeatedly: it inspects current state first and writes only what differs, so a script can be re-run without creating duplicates.

Example:

local n8n = require("assay.n8n")
n8n.wait("http://n8n:5678")
local c = n8n.client("http://n8n:5678", { api_key = env.get("N8N_API_KEY") })

n8n.ensure_variable(c, "API_HOST", "https://api.example.com")

local wf = n8n.ensure_workflow(c, {
  name = "Nightly Sync",
  nodes = {
    {
      id = "trigger",
      name = "Schedule",
      type = "n8n-nodes-base.scheduleTrigger",
      typeVersion = 1.2,
      position = { 0, 0 },
      parameters = { rule = { interval = { { triggerAtHour = 2 } } } },
    },
  },
  connections = {},
  settings = { executionOrder = "v1" },
}, { active = true })

n8n.ensure_workflow_tags(c, wf.id, { "nightly", "owned-by-platform" })

for _, run in ipairs(c.executions:list({ workflowId = wf.id, status = "error", limit = 20 })) do
  log.warn("failed execution " .. tostring(run.id) .. " at " .. tostring(run.startedAt))
end

The API key must carry the scopes for what the script does — n8n answers 403 Forbidden (not 401) when a valid key lacks a scope, so a read-only key fails every write.