n8n EU AI Act guard: AI notices, HMAC digests and approval links

n8n-nodes-spurvik is an MIT community node for n8n running in your own stack. It adds one node, Spurvik AI Act Guard (type id spurvikEuAiActGuard), with three operations:

  1. Add an AI interaction notice: puts a notice in front of the AI output text, to support your Art. 50(1) notice where an n8n workflow answers people.
  2. Record a keyed digest (shown in n8n as Record an HMAC-SHA256 digest): records a keyed digest of the input field instead of the raw prompt text.
  3. Send an ask_human() approval request: sends signed Approve / Block links to a reviewer before the workflow does something risky.

By default, when the optional Spurvik Console API Key is left empty, the node runs locally and does not call the Spurvik HTTP API, so it needs no console account. Set that key on the credential to sync to the hosted console at the node's Console Base URL setting (default https://console.spurvik.com). It does not mark content.

Only for n8n in your own stack. The node has not been reviewed by n8n for n8n Cloud, so install it on an instance you run yourself. It was tested against the n8n-workflow 2.46.4 library.

Install

In n8n: Settings → Community nodes → Install, then enter:

n8n-nodes-spurvik@1.0.4

Or with npm in your n8n custom nodes folder:

npm i n8n-nodes-spurvik@1.0.4

Import the starter workflow in 10 seconds

In the n8n workflow editor, open the ⋯ menu → Import from URL and paste the URL below (or download n8n-art50-starter.json). It wires all three operations from one sample AI item — a parallel HMAC-SHA256 input digest branch (no prompt text in the node output) and an Art. 50(1) AI notice on outputText followed by conditional Slack approval links — with optional fra1 Console sync (Free €0 for 10k events/mo · Pro €19/mo).

n8n Import from URL
https://spurvik.com/r/n8n-art50-starter.json

Credential: spurvikApi

Create a Spurvik credential. It holds:

  • Signing Secret (required): at least 16 characters, no leading or trailing whitespace. The node throws otherwise. It is used for every digest and every approval link.
  • Spurvik Console API Key (optional): leave empty to run locally. When set, the node can send hashed receipts, disclosure sessions or approval rows to the hosted console at the node's Console Base URL setting (default https://console.spurvik.com).
  • Channel tokens as password fields, only for the channels you use: Slack, Discord or Teams webhook URL; Telegram bot token; Resend API key (Email); Linear API key; Jira API token; Monday.com API token; Asana access token.

Operation 1: add an AI interaction notice

FieldDefaultWhat it does
AI System NameAI systemName used in the notice
Output FieldoutputTextField on the incoming item that holds the AI output
Notice TextemptyYour own wording; empty uses the default

Default notice: "You are interacting with an AI system (‹name›). This notice is designed to support transparency about that interaction." The node returns it as spurvik.art50Notice and the notice plus the output as spurvik.disclosedOutputText.

The node refuses an Output Field that equals the Input Field or is named promptText, prompt, chatInput, query or input, so the notice lands on the AI output, not the prompt.

Operation 2: record a keyed digest

The node reads the Input Field (default promptText) and returns a record under spurvik:

  • inputHmac: HMAC SHA256 of spurvik:workflow:input:v1| plus the input field, keyed with your signing secret
  • previousRecordHash: the incoming item's previousRecordHash, or 64 zeros
  • outputHmac: always null in this operation, because the node passes only the input field to the digest
  • recordHash: HMAC SHA256, keyed with your signing secret, over spurvik:workflow:chain:v1| plus the previous hash, system name, inputHmac, outputHmac (or the literal none when it is null, as it always is from this node) and timestamp, so you can chain records
  • timestampIso

Prompt field names and the configured Input Field are never copied onto the output, including when listed in the Pass-through Fields setting.

Mind n8n's own execution data. The node's output holds no prompt text, but n8n stores each node's input on the execution. Set your retention yourself with EXECUTIONS_DATA_SAVE_ON_SUCCESS, EXECUTIONS_DATA_SAVE_ON_ERROR, EXECUTIONS_DATA_SAVE_MANUAL_EXECUTIONS, EXECUTIONS_DATA_PRUNE, EXECUTIONS_DATA_MAX_AGE and EXECUTIONS_DATA_PRUNE_MAX_COUNT.

Operation 3: send an ask_human() approval request

FieldNotes
ChannelSlack, Telegram, Email (Resend), Discord, Microsoft Teams, Linear, Jira, Monday.com, Asana
Approver TargetChat id, recipient, team id, project key, board id or project gid (not needed for Slack, Discord, Teams)
Callback URLRequired. A URL you run that accepts GET clicks on the links
Action Name / Action SummaryWhat the reviewer sees. Keep personal data out of the summary

The node signs separate Approve and Block links. Each link carries the gate id, a timestamp, an action HMAC, a random nonce and a signature. It returns approveUrl, rejectUrl, gateId and the channel receipt.

Monday.com only creates a board item. It carries the gate id but no Approve or Block links, so the reviewer needs another way to decide.

There is no hosted handler. The links point at your Callback URL. Something you run must check the click with verifyChannelApprovalToken() from spurvik-sdk/channels, record the nonce and refuse a repeat, and let the action continue only on approved. A common pattern:

  1. An n8n Webhook node receives the GET click: /webhook/spurvik-gate?gateId=…&ts=…&hmac=…&nonce=…&decision=approved&sig=…
  2. An n8n HTTP Request node posts those values to your service.
  3. Your service returns the result of the check; the workflow continues only when it is true and the decision is approved.
// your-service/verify.ts (spurvik-sdk@1.0.8)
import { verifyChannelApprovalToken } from "spurvik-sdk/channels";

export function checkClick(q: URLSearchParams) {
  const gateId = q.get("gateId");
  const decision = q.get("decision");
  const nonce = q.get("nonce");
  if (!gateId || !nonce || (decision !== "approved" && decision !== "rejected")) {
    return { valid: false, approved: false, gateId, nonce };
  }
  const valid = verifyChannelApprovalToken({
    gateId,
    actionHmac: q.get("hmac") ?? "",
    decision,
    issuedAtUnix: Number(q.get("ts")),
    signature: q.get("sig") ?? "",
    nonce,
    signingSecret: process.env.SPURVIK_SIGNING_SECRET!, // same secret as the n8n credential
  });
  // Record the nonce and refuse a repeat before you act on the result.
  return { valid, approved: valid && decision === "approved", gateId, nonce };
}

Links expire after 24 hours by default (maxAgeSeconds). Until then a link can be reused, which is why you record the nonce.

The SDK and MCP server are MIT and run in your own stack.

Try it

Install in n8n
n8n-nodes-spurvik@1.0.4
Copy the badge for your website
<script src="https://spurvik.com/badge.js" data-system="Your AI" data-attribution="true" defer></script>
  • A small “Art. 50 · Spurvik” link shows after the notice by default. On Pro (€19/mo) you can hide it with data-attribution="false" for a white-label badge.

Free €0 (10k events/mo) · Pro €19/mo · Team €79/mo · excl. VAT · B2B only.

FAQ

Does the node work on n8n Cloud?

It is built for n8n running in your own stack. n8n has not reviewed it for n8n Cloud.

Do I need a Spurvik account or API key?

No account is required. By default the node does not call the Spurvik API and needs only the signing secret and the tokens for the channels you use. Set the optional console API key only if you want the node to sync to the hosted console.

Does the node store my prompts?

The node output does not copy prompt text or the configured input field. n8n itself keeps each node's input on the execution, so set n8n's execution data retention.

Who checks the Approve and Block clicks?

You do. The links go to your Callback URL, and your service checks them with verifyChannelApprovalToken() from the channels export of the Spurvik SDK. There is no hosted handler.

Does Monday.com get Approve and Block links?

No. Monday.com only gets a board item with the gate id.

Does the node mark content generated by AI?

No. It adds a visible notice to text and records digests; it is not Art. 50(2) marking in a machine readable format.

Sources

Reviewed 11 November 2026. Not legal advice. Supports parts of your Art. 50 work; responsibility stays with you. EyeTea Solutions ApS · Trekantområdet, Denmark

Your AI Act controls should live in a few lines of code, not in six months of legal memos.