FlowKit

n8n Execution Data node: annotate executions so you can find them

Published 25 August 2026 · 8 min read

You know a workflow mishandled order ORD-20841 yesterday afternoon, but the executions list shows nothing but a column of timestamps and green ticks. Opening executions one by one to find the right one costs ten minutes per incident. The Execution Data node (n8n-nodes-base.executionData) exists for exactly this: it attaches business key/value pairs to the current execution, and those pairs become filter criteria in the executions list. This guide covers its exact parameters, its real limits, its plan availability — the part most tutorials skip — and how to pick keys that help without turning your history into a personal data store.

The problem: dozens of executions, only one that matters

While a workflow runs three times a day, the history is fine. Once it processes hundreds of events — order webhooks, support tickets, inbound emails — the list becomes a wall of undifferentiated timestamps. Debugging changes nature at that point: the problem is no longer "understand the error" but "locate the run", which comes before everything described in our n8n workflow debugging guide.

The problem reaches far beyond n8n. In Google's technical report "Dapper, a Large-Scale Distributed Systems Tracing Infrastructure" (2010), Benjamin H. Sigelman, Luiz André Barroso, Mike Burrows and their co-authors describe a tracing infrastructure whose key mechanisms include letting developers attach application-level annotations to traces so they can find and correlate the requests they care about. The Execution Data node is the minimal version of that idea.

What the Execution Data node actually does

The node ships under the name Execution Data in the core nodes. Its documentation sums it up as: save metadata for workflow executions, so you can then search by that data in the Executions list.

  • A single operation: Save Execution Data for Search. The node has no read mode.
  • A repeatable field: you add one Saved Field per piece of metadata, with a Key and a Value. Both accept expressions, so {{ $json.order_id }} works directly.
  • No effect on the data: items pass through unchanged. You can drop it in the middle of a chain without breaking the flow.

The Code node equivalent, for cases where the key itself is dynamic:

// One value at a time
$execution.customData.set('order_id', $json.order_id);

// Or the whole object at once (replaces whatever was there)
$execution.customData.setAll({
  order_id: String($json.order_id),
  customer_id: String($json.customer_id),
  channel: 'webhook',
});

return $input.all();

The visual node covers most needs; the Code node earns its keep when you have to build keys on the fly or write metadata conditionally.

Plan availability: check this before wiring anything

This is the nuance missing from most tutorials. The node appears in the nodes panel of any instance, but the custom executions data feature is plan-gated. The n8n documentation states it plainly:

Custom executions data is available on: Cloud: Pro, Enterprise; Self-Hosted: Enterprise, registered Community

Translated into practical decisions:

  • n8n Cloud: entry plans do not include it. You need Pro at minimum, or Enterprise.
  • Self-hosted: available with an Enterprise licence… or with a registered Community edition. That second case is the most interesting and the least known: the free self-hosted Community edition unlocks the feature by requesting a free licence key by email (Settings > Usage and plan > Unlock), alongside workflow folders and debug in editor.

In other words: if you self-host, this guide costs you nothing — but only after registering the instance. On the entry-level Cloud plan, the node will run without raising a visible error, yet the matching filter will not appear in the executions list, so verify this before refactoring ten workflows. It is also a factor when you separate development from production: an unregistered test instance will not reproduce production behaviour here.

The exact limits (and one ambiguity in the docs)

The documented constraints are deliberately tight, because this data is indexed for search:

  • Maximum 10 items of custom data per execution.
  • Key: 50 characters maximum.
  • Value: it must be a string. On length, the documentation is inconsistent — the node page states 512 characters, while the custom executions data page states 255. Until the two agree, stay under 255 characters: it is the safe bound, and no useful search key needs more.
  • On overflow, n8n truncates the value and logs the event rather than failing the execution.

Watch the typing: a numeric order_id must be converted (String($json.order_id)) if you go through the Code node. Via the Execution Data node, the expression is already rendered as text.

Where to place the node in the workflow

The rule is simple: as early as possible once the business identifier is known, and never after a step that might fail.

  • Right after the trigger, when the webhook already carries the identifier. Ideal placement: even if the workflow blows up three nodes later, the failed execution is still findable.
  • After the first enrichment, when the identifier comes from an API call. You lose traceability on runs that fail before that point, so compensate with a coarse key set upstream (source: webhook_stripe).
  • Several times, to enrich progressively: one key at the trigger, another after an agent's decision. Successive calls add keys (set); watch out for setAll(), which replaces the entire object.

When you split work into sub-workflows, each sub-workflow running in its own context produces its own execution: set the same keys on both sides, or you will find the parent without the child. And in an error workflow, this metadata makes excellent alert content — "failure on order ORD-20841" beats "failure".

Choosing your keys: stable identifiers, and nothing more

Ten keys maximum forces you to choose. The good candidates are stable business identifiers: order_id, ticket_id, customer_id (the primary keys of your systems); source or channel to segment volumes; env if one instance serves several contexts; a decision status (route: human_escalation) to find disputed cases. These are often your idempotency keys already: the logic in our guide to webhook idempotency transfers as-is, and that is a good sign — a key that reliably identifies an event also reliably identifies an execution.

Conversely, avoid personal data whenever a technical identifier does the job. Emails, names, phone numbers: those values are stored with the execution in the instance database and appear in the list filters to anyone with UI access. From a GDPR standpoint this is basic minimisation: customer_id: 4821 supports exactly the same lookup as email: first.last@company.com, without creating a new store of identifying data inside a technical log whose retention is rarely documented. For a richer, defensible trail, the right place is a dedicated table with its own retention policy — the approach detailed in our guide to a GDPR audit trail with n8n and Supabase.

That distinction between search metadata and a provenance record is not new. The reference survey by Yogesh L. Simmhan, Beth Plale and Dennis Gannon, "A Survey of Data Provenance in e-Science" (ACM SIGMOD Record, 2005), classifies provenance systems by why provenance is recorded, what it describes and how it is stored — and shows that "discovery and search" use cases do not call for the same level of detail as "audit and reproducibility" ones. n8n's execution data clearly belongs to the first category.

Reading values back: that's the Code node, not the Execution Data node

Classic trap: the Execution Data node cannot read back what it wrote. Its only operation is writing. To retrieve values during the run, use a Code node:

const orderId = $execution.customData.get('order_id');
const all = $execution.customData.getAll();

return [{ json: { orderId, all, executionId: $execution.id } }];

$execution.id is the natural complement: the unique technical identifier of the current run. The pattern that works well is to write that ID into your business system — an n8n_execution_id field on the order or ticket — while the Execution Data node writes the business identifier into n8n. The link becomes bidirectional: from the CRM you open the execution by URL, from n8n you filter by order number. Same principle as logging an AI agent's decisions, except that here the payload stays deliberately tiny.

Annotations, pruning and Insights: the three complements

Execution annotations (tags and ratings applied manually from the UI) serve a different need: flagging an interesting case after the fact, whereas custom data is written automatically by the workflow. They have a valuable side effect, documented by n8n: annotated executions are never pruned. If your instance purges history via EXECUTIONS_DATA_MAX_AGE or EXECUTIONS_DATA_PRUNE_MAX_COUNT, a tag is the simplest way to preserve a reference execution — worth keeping in mind alongside our guide to cleaning up n8n executions and the database.

Insights works one floor above: volume aggregates, failure rates, time saved, as covered in our n8n Insights guide. The two complement each other neatly — Insights tells you there were 12 failures this week, custom data tells you which ones. To get out of the UI, executions remain queryable through the n8n REST API.

When this setup stops being enough — fine-grained tracing of an agent's steps, prompt comparison, per-call latency — the need has changed nature and calls for a dedicated tool, as described in our guide to AI agent observability with Langfuse.

Key takeaways

The Execution Data node does one thing well: attach up to 10 key/value pairs (key ≤ 50 characters, string values, keep them under 255 characters) to the current execution through its single Save Execution Data for Search operation, so you can filter for it later. Place it early, as soon as the business identifier is known; pick stable identifiers over personal data; read values back with $execution.customData.get() in a Code node, never with the node itself. And check your plan first: Cloud Pro or Enterprise, self-hosted Enterprise, or — the good news — a self-hosted Community edition that has simply been registered, for free.

Going further

If your motivation is regulatory traceability rather than debugging comfort, the Compliance & Audit Pack (€149) goes well beyond the 10 keys allowed here: it structures a real externalised audit trail, with controlled retention and data minimisation — precisely what n8n execution data is not meant to carry. For an email-processing workflow where the point is mostly to find "customer X's message" quickly, the AI Inbox Pack (€79) ships pre-wired with business identifiers in the right places.

FAQ

Frequently asked questions

Is the Execution Data node available on every n8n plan?

The node shows up in the editor everywhere, but the feature it feeds — custom executions data and searching by it — is plan-gated. The n8n docs state: Cloud on Pro and Enterprise; self-hosted on Enterprise or on a registered Community edition. That last one matters most: the free self-hosted Community edition unlocks this feature at no cost by requesting a licence key by email from Settings > Usage and plan.

How many key/value pairs can I attach to an n8n execution?

The n8n docs state a maximum of 10 items of custom data per execution, with keys capped at 50 characters. For values, the node page says 512 characters while the custom executions data page says 255, so in practice stay well under 255. Beyond the limits, n8n truncates the value and logs the event rather than failing the run.

Can the Execution Data node read custom data back?

No. The node only writes: its single operation is Save Execution Data for Search. To read values during the run you need a Code node, with $execution.customData.get("key") for one value or $execution.customData.getAll() for the whole object.

Should I store the customer's email in execution data?

Avoid it whenever a technical identifier does the job. Custom data is stored with the execution in the instance database and shows up in the executions list filters, so it is visible to anyone with instance access. A stable internal ID (customer ID, order number, ticket ID) supports the same search without turning your execution log into another store of personal data.

Bundle FlowKit Complet

€269